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_socket1 int ret;
2 char buf[] = "hello";
3
4 ret = write(socketID, buf, strlen(buf));
";
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* ''.
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之前加入这段代码吧~