原文地址: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));