Android利用LocalSocket實現Java端進程與C端進程之間的IPC

原文地址:http://www.cnblogs.com/zealotrouge/archive/2013/06/24/3152941.html  

經驗證,可實現android端c層與java層的socket通信。

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();

            //啓動client
            Intent i = new Intent(LocalSocketServerActivity.this,LocalSocketClientActivity.class);
            startActivity(i);
       }
 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));

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章