Android利用LocalSocket实现Java端进程与C端进程之间的IPC

  Android是建立在Linux之上的OS,在涉及到安全、网络协议、文件加密等功能时,往往需要通过C语言调用底层API来实现,而如何发出指令让C端执行我们想要的功能,并且在执行之后有返回结果呢,这就需要打通Java端进程和C端进程,使之能高效地通信。这样,C端进程用于实现功能,Java端进程负责UI、功能的触发及结果处理就可以了。

  对于*nix系统来说,“一切皆为文件”,Socket也不例外,Socket按照收发双方的媒介来说有三种类型:1,通过网络端口;2,通过文件系统;3,通过内存映射文件。具体说来,三种类型均可以用来作为IPC的Socket:1,通过本地回环接口(即LoopBack)127.0.0.1来收发数据;2,通过文件作为收发数据的中转站;3,在内存中开辟一块区域作为收发数据的中转站,此区域仍然使用文件读写API进行访问。LocalSocket支持方式2和方式3,从效率的角度来说,显然是方式3效率最高,那么下面我们就使用LocalSocket来演示如何实现Java端进程与C端进程之间的IPC。

  以下的demo是Java端作为server,C端作为client;实际场景中可能更多的是Java端作为client,而C端作为server。

服务端代码如下:

  1 package main.activity;
  2 
  3 import java.io.IOException;
  4 import java.io.InputStream;
  5 import java.io.InputStreamReader;
  6 
  7 import android.app.Activity;
  8 import android.net.LocalServerSocket;
  9 import android.net.LocalSocket;
 10 import android.os.Bundle;
 11 import android.util.Log;
 12 
 13 /**
 14  * @author pengyiming
 15  * @note 启动localSocketServer
 16  *
 17  */
 18 
 19 public class LocalSocketServerActivity extends Activity
 20 {
 21     /* 数据段begin */
 22     private final String TAG = "server";
 23     
 24     private ServerSocketThread mServerSocketThread;
 25     /* 数据段end */
 26     
 27     /* 函数段begin */
 28     @Override
 29     protected void onCreate(Bundle savedInstanceState)
 30     {
 31         super.onCreate(savedInstanceState);
 32         
 33         mServerSocketThread = new ServerSocketThread();
 34         mServerSocketThread.start();
 35     }
 36     
 37     @Override
 38     protected void onDestroy()
 39     {
 40         super.onDestroy();
 41         
 42         mServerSocketThread.stopRun();
 43     }
 44     /* 函数段end */
 45     
 46     /* 内部类begin */
 47     private class ServerSocketThread extends Thread
 48     {
 49         private boolean keepRunning = true;
 50         private LocalServerSocket serverSocket;
 51         
 52         private void stopRun()
 53         {
 54             keepRunning = false;
 55         }
 56         
 57         @Override
 58         public void run()
 59         {
 60             try
 61             {
 62                 serverSocket = new LocalServerSocket("pym_local_socket");
 63             }
 64             catch (IOException e)
 65             {
 66                 e.printStackTrace();
 67                 
 68                 keepRunning = false;
 69             }
 70             
 71             while(keepRunning)
 72             {
 73                 Log.d(TAG, "wait for new client coming !");
 74                 
 75                 try
 76                 {
 77                     LocalSocket interactClientSocket = serverSocket.accept();
 78                     
 79                     //由于accept()在阻塞时,可能Activity已经finish掉了,所以再次检查keepRunning
 80                     if (keepRunning)
 81                     {
 82                         Log.d(TAG, "new client coming !");
 83                         
 84                         new InteractClientSocketThread(interactClientSocket).start();
 85                     }
 86                 }
 87                 catch (IOException e)
 88                 {
 89                     e.printStackTrace();
 90                     
 91                     keepRunning = false;
 92                 }
 93             }
 94             
 95             if (serverSocket != null)
 96             {
 97                 try
 98                 {
 99                     serverSocket.close();
100                 }
101                 catch (IOException e)
102                 {
103                     e.printStackTrace();
104                 }
105             }
106         }
107     }
108     
109     private class InteractClientSocketThread extends Thread
110     {
111         private LocalSocket interactClientSocket;
112         
113         public InteractClientSocketThread(LocalSocket interactClientSocket)
114         {
115             this.interactClientSocket = interactClientSocket;
116         }
117         
118         @Override
119         public void run()
120         {
121             StringBuilder recvStrBuilder = new StringBuilder();
122             InputStream inputStream = null;
123             try
124             {
125                 inputStream = interactClientSocket.getInputStream();
126                 InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
127                 char[] buf = new char[4096];
128                 int readBytes = -1;
129                 while ((readBytes = inputStreamReader.read(buf)) != -1)
130                 {
131                     String tempStr = new String(buf, 0, readBytes);
132                     recvStrBuilder.append(tempStr);
133                 }
134             }
135             catch (IOException e)
136             {
137                 e.printStackTrace();
138                 
139                 Log.d(TAG, "resolve data error !");
140             }
141             finally
142             {
143                 if (inputStream != null)
144                 {
145                     try
146                     {
147                         inputStream.close();
148                     }
149                     catch (IOException e)
150                     {
151                         e.printStackTrace();
152                     }
153                 }
154             }
155         }
156     }
157     /* 内部类end */
158 }

 客户端代码如下:

 1 package main.activity;
 2 
 3 import android.app.Activity;
 4 import android.os.Bundle;
 5 import android.util.Log;
 6 
 7 /**
 8  * @author pengyiming
 9  * @note 用于启动localSocketClient,向server发送心跳报文
10  *
11  */
12 
13 public class LocalSocketClientActivity extends Activity
14 {
15     /* 数据段begin */
16     private final String TAG = "client";
17     
18     private HeartBeatThread mHeartBeatThread;
19     
20     public native int startHeartBeat();
21     /* 数据段end */
22     
23     /* 函数段begin */
24     static
25     {
26         System.loadLibrary("pymclient");
27     }
28     
29     @Override
30     protected void onCreate(Bundle savedInstanceState)
31     {
32         super.onCreate(savedInstanceState);
33         
34         mHeartBeatThread = new HeartBeatThread();
35         mHeartBeatThread.start();
36     }
37     
38     @Override
39     protected void onDestroy()
40     {
41         super.onDestroy();
42         
43         mHeartBeatThread.stopRun();
44     }
45     /* 函数段end */
46     
47     /* 内部类begin */
48     private class HeartBeatThread extends Thread
49     {
50         int ret;
51         boolean keepRunning = true;
52         
53         public void stopRun()
54         {
55             keepRunning = false;
56         }
57         
58         @Override
59         public void run()
60         {
61             Log.d(TAG, "start heart beat!");
62             
63             while (keepRunning)
64             {
65                 ret = startHeartBeat();
66                 
67                 Log.d(TAG, "ret = " + ret);
68                 
69                 if (ret != 0)
70                 {
71                     break;
72                 }
73                 
74                 try
75                 {
76                     sleep(1000);
77                 }
78                 catch (InterruptedException e)
79                 {
80                     e.printStackTrace();
81                 }
82             }
83             
84             Log.d(TAG, "stop heart beat!");
85         }
86     }
87     /* 内部类end */
88 }

上述客户端代码启动了一个线程用于发送“心跳”报文,每隔1s构建一个新的LocalSocket,连接服务端并发送流数据,其核心就在于native方法的实现。值得一提的是,我最初使用原生socket函数,没想connect总是返回错误;后来在同事的提醒下,我参考了Android源码rild.c中socket_local_client的使用,并从socket_local_client.c中抽取出相应代码改写而成。

客户端native方法头文件:

 1 /* DO NOT EDIT THIS FILE - it is machine generated */
 2 #include <jni.h>
 3 /* Header for class main_activity_LocalSocketClientActivity */
 4 
 5 #ifndef _Included_main_activity_LocalSocketClientActivity
 6 #define _Included_main_activity_LocalSocketClientActivity
 7 #ifdef __cplusplus
 8 extern "C" {
 9 #endif
10 
11 /* socket命名空间(见cutils/sockets.h) */
12 #define ANDROID_SOCKET_NAMESPACE_ABSTRACT 0
13 #define ANDROID_SOCKET_NAMESPACE_RESERVED 1
14 #define ANDROID_SOCKET_NAMESPACE_FILESYSTEM 2
15 
16 /* socket类型 */
17 #define SOCK_STREAM      1
18 #define SOCK_DGRAM       2
19 #define SOCK_RAW         3
20 #define SOCK_RDM         4
21 #define SOCK_SEQPACKET   5
22 #define SOCK_PACKET      10
23 
24 /* 清0宏 */
25 #define MEM_ZERO(pDest, destSize) memset(pDest, 0, destSize)
26 
27 /* 错误码定义 */
28 #define NO_ERR 0
29 #define CREATE_ERR -1
30 #define CONNECT_ERR -2
31 #define LINUX_MAKE_ADDRUN_ERROR -3
32 #define NO_LINUX_MAKE_ADDRUN_ERROR -4
33 #define CLOSE_ERR -5
34 
35 /* 是否使用linux的本地socket命令空间 */
36 #define HAVE_LINUX_LOCAL_SOCKET_NAMESPACE "linux_local_socket_namespace"
37 
38 #undef main_activity_LocalSocketClientActivity_MODE_PRIVATE
39 #define main_activity_LocalSocketClientActivity_MODE_PRIVATE 0L
40 #undef main_activity_LocalSocketClientActivity_MODE_WORLD_READABLE
41 #define main_activity_LocalSocketClientActivity_MODE_WORLD_READABLE 1L
42 #undef main_activity_LocalSocketClientActivity_MODE_WORLD_WRITEABLE
43 #define main_activity_LocalSocketClientActivity_MODE_WORLD_WRITEABLE 2L
44 #undef main_activity_LocalSocketClientActivity_MODE_APPEND
45 #define main_activity_LocalSocketClientActivity_MODE_APPEND 32768L
46 #undef main_activity_LocalSocketClientActivity_MODE_MULTI_PROCESS
47 #define main_activity_LocalSocketClientActivity_MODE_MULTI_PROCESS 4L
48 #undef main_activity_LocalSocketClientActivity_BIND_AUTO_CREATE
49 #define main_activity_LocalSocketClientActivity_BIND_AUTO_CREATE 1L
50 #undef main_activity_LocalSocketClientActivity_BIND_DEBUG_UNBIND
51 #define main_activity_LocalSocketClientActivity_BIND_DEBUG_UNBIND 2L
52 #undef main_activity_LocalSocketClientActivity_BIND_NOT_FOREGROUND
53 #define main_activity_LocalSocketClientActivity_BIND_NOT_FOREGROUND 4L
54 #undef main_activity_LocalSocketClientActivity_BIND_ABOVE_CLIENT
55 #define main_activity_LocalSocketClientActivity_BIND_ABOVE_CLIENT 8L
56 #undef main_activity_LocalSocketClientActivity_BIND_ALLOW_OOM_MANAGEMENT
57 #define main_activity_LocalSocketClientActivity_BIND_ALLOW_OOM_MANAGEMENT 16L
58 #undef main_activity_LocalSocketClientActivity_BIND_WAIVE_PRIORITY
59 #define main_activity_LocalSocketClientActivity_BIND_WAIVE_PRIORITY 32L
60 #undef main_activity_LocalSocketClientActivity_BIND_IMPORTANT
61 #define main_activity_LocalSocketClientActivity_BIND_IMPORTANT 64L
62 #undef main_activity_LocalSocketClientActivity_BIND_ADJUST_WITH_ACTIVITY
63 #define main_activity_LocalSocketClientActivity_BIND_ADJUST_WITH_ACTIVITY 128L
64 #undef main_activity_LocalSocketClientActivity_CONTEXT_INCLUDE_CODE
65 #define main_activity_LocalSocketClientActivity_CONTEXT_INCLUDE_CODE 1L
66 #undef main_activity_LocalSocketClientActivity_CONTEXT_IGNORE_SECURITY
67 #define main_activity_LocalSocketClientActivity_CONTEXT_IGNORE_SECURITY 2L
68 #undef main_activity_LocalSocketClientActivity_CONTEXT_RESTRICTED
69 #define main_activity_LocalSocketClientActivity_CONTEXT_RESTRICTED 4L
70 #undef main_activity_LocalSocketClientActivity_RESULT_CANCELED
71 #define main_activity_LocalSocketClientActivity_RESULT_CANCELED 0L
72 #undef main_activity_LocalSocketClientActivity_RESULT_OK
73 #define main_activity_LocalSocketClientActivity_RESULT_OK -1L
74 #undef main_activity_LocalSocketClientActivity_RESULT_FIRST_USER
75 #define main_activity_LocalSocketClientActivity_RESULT_FIRST_USER 1L
76 #undef main_activity_LocalSocketClientActivity_DEFAULT_KEYS_DISABLE
77 #define main_activity_LocalSocketClientActivity_DEFAULT_KEYS_DISABLE 0L
78 #undef main_activity_LocalSocketClientActivity_DEFAULT_KEYS_DIALER
79 #define main_activity_LocalSocketClientActivity_DEFAULT_KEYS_DIALER 1L
80 #undef main_activity_LocalSocketClientActivity_DEFAULT_KEYS_SHORTCUT
81 #define main_activity_LocalSocketClientActivity_DEFAULT_KEYS_SHORTCUT 2L
82 #undef main_activity_LocalSocketClientActivity_DEFAULT_KEYS_SEARCH_LOCAL
83 #define main_activity_LocalSocketClientActivity_DEFAULT_KEYS_SEARCH_LOCAL 3L
84 #undef main_activity_LocalSocketClientActivity_DEFAULT_KEYS_SEARCH_GLOBAL
85 #define main_activity_LocalSocketClientActivity_DEFAULT_KEYS_SEARCH_GLOBAL 4L
86 /*
87  * Class:     main_activity_LocalSocketClientActivity
88  * Method:    startHeartBeat
89  * Signature: ()I
90  */
91 JNIEXPORT jint JNICALL Java_main_activity_LocalSocketClientActivity_startHeartBeat
92   (JNIEnv *, jobject);
93 
94 #ifdef __cplusplus
95 }
96 #endif
97 #endif

客户端native方法实现:

  1 /* 头文件begin */
  2 #include "main_activity_LocalSocketClientActivity.h"
  3 
  4 #include <sys/socket.h>
  5 #include <sys/un.h>
  6 #include <stddef.h>
  7 #include <string.h>
  8 /* 头文件end */
  9 
 10 #ifdef __cplusplus
 11 extern "C" {
 12 #endif
 13 
 14 /*
 15  * Class:     main_activity_LocalSocketClientActivity
 16  * Method:    startHeartBeat
 17  */
 18 JNIEXPORT jint JNICALL Java_main_activity_LocalSocketClientActivity_startHeartBeat(JNIEnv * env, jobject object)
 19 {
 20     int socketID;
 21     struct sockaddr_un serverAddr;
 22     char path[] = "pym_local_socket\0";
 23     int ret;
 24 
 25     socketID = socket_local_client(path, ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
 26     if (socketID < 0)
 27     {
 28         return socketID;
 29     }
 30 
 31     ret = close(socketID);
 32     if (ret < 0)
 33     {
 34         return CLOSE_ERR;
 35     }
 36 
 37     return NO_ERR;
 38 }
 39 
 40 /* 创建本地socket客户端 */
 41 int socket_local_client(const char *name, int namespaceId, int type)
 42 {
 43     int socketID;
 44     int ret;
 45 
 46     socketID = socket(AF_LOCAL, type, 0);
 47     if(socketID < 0)
 48     {
 49         return CREATE_ERR;
 50     }
 51 
 52     ret = socket_local_client_connect(socketID, name, namespaceId, type);
 53     if (ret < 0)
 54     {
 55         close(socketID);
 56 
 57         return ret;
 58     }
 59 
 60     return socketID;
 61 }
 62 
 63 /* 连接到相应的fileDescriptor上 */
 64 int socket_local_client_connect(int fd, const char *name, int namespaceId, int type)
 65 {
 66     struct sockaddr_un addr;
 67     socklen_t socklen;
 68     size_t namelen;
 69     int ret;
 70 
 71     ret = socket_make_sockaddr_un(name, namespaceId, &addr, &socklen);
 72     if (ret < 0)
 73     {
 74         return ret;
 75     }
 76 
 77     if(connect(fd, (struct sockaddr *) &addr, socklen) < 0)
 78     {
 79         return CONNECT_ERR;
 80     }
 81 
 82     return fd;
 83 }
 84 
 85 /* 构造sockaddr_un */
 86 int socket_make_sockaddr_un(const char *name, int namespaceId, struct sockaddr_un *p_addr, socklen_t *socklen)
 87 {
 88     size_t namelen;
 89 
 90     MEM_ZERO(p_addr, sizeof(*p_addr));
 91 #ifdef HAVE_LINUX_LOCAL_SOCKET_NAMESPACE
 92 
 93     namelen  = strlen(name);
 94 
 95     // Test with length +1 for the *initial* '\0'.
 96     if ((namelen + 1) > sizeof(p_addr->sun_path))
 97     {
 98         return LINUX_MAKE_ADDRUN_ERROR;
 99     }
100     p_addr->sun_path[0] = 0;
101     memcpy(p_addr->sun_path + 1, name, namelen);
102 
103 #else
104 
105     namelen = strlen(name) + strlen(FILESYSTEM_SOCKET_PREFIX);
106 
107     /* unix_path_max appears to be missing on linux */
108     if (namelen > (sizeof(*p_addr) - offsetof(struct sockaddr_un, sun_path) - 1))
109     {
110         return NO_LINUX_MAKE_ADDRUN_ERROR;
111     }
112 
113     strcpy(p_addr->sun_path, FILESYSTEM_SOCKET_PREFIX);
114     strcat(p_addr->sun_path, name);
115 
116 #endif
117 
118     p_addr->sun_family = AF_LOCAL;
119     *socklen = namelen + offsetof(struct sockaddr_un, sun_path) + 1;
120 
121     return NO_ERR;
122 }
123 
124 #ifdef __cplusplus
125 }
126 #endif

注意到100~101行比较特殊,是从p_addr->sun_path[1]开始拷贝本地域名,这就是之前为什么一直connect不上的原因,至于为什么偏移1个字节来拷贝本地域名,你可以在*nix系统下输入"man 7 unix"来找到原因。

  先启动server,再启动client就可以看到结果了。对了,在成功创建并已自动连接后,我并未发送任何数据,其实发送数据就是写入文件,It's your trun now! 在close之前加入这段代码吧~

1 int ret;
2 char buf[] = "hello";
3 
4 ret = write(socketID, buf, strlen(buf));

 

 

 

posted @ 2013-06-24 18:33  热气球  阅读(19373)  评论(2编辑  收藏  举报