本帖最后由 空道 于 2014-11-27 13:58 编辑
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。 服务端代码如下: package main.activity; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import android.app.Activity; import android.net.LocalServerSocket; import android.net.LocalSocket; import android.os.Bundle; import android.util.Log; /** * @author pengyiming * @note 启动localSocketServer * */ public class LocalSocketServerActivity extends Activity { /* 数据段begin */ private final String TAG = "server"; private ServerSocketThread mServerSocketThread; /* 数据段end */ /* 函数段begin */ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mServerSocketThread = new ServerSocketThread(); mServerSocketThread.start(); } @Override protected void onDestroy() { super.onDestroy(); mServerSocketThread.stopRun(); } /* 函数段end */ /* 内部类begin */ private class ServerSocketThread extends Thread { private boolean keepRunning = true; private LocalServerSocket serverSocket; private void stopRun() { keepRunning = false; } @Override public void run() { try { serverSocket = new LocalServerSocket("pym_local_socket"); } catch (IOException e) { e.printStackTrace(); keepRunning = false; } while(keepRunning) { Log.d(TAG, "wait for new client coming !"); try { LocalSocket interactClientSocket = serverSocket.accept(); //由于accept()在阻塞时,可能Activity已经finish掉了,所以再次检查keepRunning if (keepRunning) { Log.d(TAG, "new client coming !"); new InteractClientSocketThread(interactClientSocket).start(); } } catch (IOException e) { e.printStackTrace(); keepRunning = false; } } if (serverSocket != null) { try { serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } } } } private class InteractClientSocketThread extends Thread { private LocalSocket interactClientSocket; public InteractClientSocketThread(LocalSocket interactClientSocket) { this.interactClientSocket = interactClientSocket; } @Override public void run() { StringBuilder recvStrBuilder = new StringBuilder(); InputStream inputStream = null; try { inputStream = interactClientSocket.getInputStream(); InputStreamReader inputStreamReader = new InputStreamReader(inputStream); char[] buf = new char[4096]; int readBytes = -1; while ((readBytes = inputStreamReader.read(buf)) != -1) { String tempStr = new String(buf, 0, readBytes); recvStrBuilder.append(tempStr); } } catch (IOException e) { e.printStackTrace(); Log.d(TAG, "resolve data error !"); } finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } } } /* 内部类end */ } 客户端代码如下: package main.activity; import android.app.Activity; import android.os.Bundle; import android.util.Log; /** * @author pengyiming * @note 用于启动localSocketClient,向server发送心跳报文 * */ public class LocalSocketClientActivity extends Activity { /* 数据段begin */ private final String TAG = "client"; private HeartBeatThread mHeartBeatThread; public native int startHeartBeat(); /* 数据段end */ /* 函数段begin */ static { System.loadLibrary("pymclient"); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mHeartBeatThread = new HeartBeatThread(); mHeartBeatThread.start(); } @Override protected void onDestroy() { super.onDestroy(); mHeartBeatThread.stopRun(); } /* 函数段end */ /* 内部类begin */ private class HeartBeatThread extends Thread { int ret; boolean keepRunning = true; public void stopRun() { keepRunning = false; } @Override public void run() { Log.d(TAG, "start heart beat!"); while (keepRunning) { ret = startHeartBeat(); Log.d(TAG, "ret = " + ret); if (ret != 0) { break; } try { sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } Log.d(TAG, "stop heart beat!"); } } /* 内部类end */ } 上述客户端代码启动了一个线程用于发送“心跳”报文,每隔1s构建一个新的LocalSocket,连接服务端并发送流数据,其核心就在于native方法的实现。值得一提的是,我最初使用原生socket函数,没想connect总是返回错误;后来在同事的提醒下,我参考了Android源码rild.c中socket_local_client的使用,并从socket_local_client.c中抽取出相应代码改写而成。 客户端native方法头文件: /* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class main_activity_LocalSocketClientActivity */ #ifndef _Included_main_activity_LocalSocketClientActivity #define _Included_main_activity_LocalSocketClientActivity #ifdef __cplusplus extern "C" { #endif /* socket命名空间(见cutils/sockets.h) */ #define ANDROID_SOCKET_NAMESPACE_ABSTRACT 0 #define ANDROID_SOCKET_NAMESPACE_RESERVED 1 #define ANDROID_SOCKET_NAMESPACE_FILESYSTEM 2 /* socket类型 */ #define SOCK_STREAM 1 #define SOCK_DGRAM 2 #define SOCK_RAW 3 #define SOCK_RDM 4 #define SOCK_SEQPACKET 5 #define SOCK_PACKET 10 /* 清0宏 */ #define MEM_ZERO(pDest, destSize) memset(pDest, 0, destSize) /* 错误码定义 */ #define NO_ERR 0 #define CREATE_ERR -1 #define CONNECT_ERR -2 #define LINUX_MAKE_ADDRUN_ERROR -3 #define NO_LINUX_MAKE_ADDRUN_ERROR -4 #define CLOSE_ERR -5 /* 是否使用linux的本地socket命令空间 */ #define HAVE_LINUX_LOCAL_SOCKET_NAMESPACE "linux_local_socket_namespace" #undef main_activity_LocalSocketClientActivity_MODE_PRIVATE #define main_activity_LocalSocketClientActivity_MODE_PRIVATE 0L #undef main_activity_LocalSocketClientActivity_MODE_WORLD_READABLE #define main_activity_LocalSocketClientActivity_MODE_WORLD_READABLE 1L #undef main_activity_LocalSocketClientActivity_MODE_WORLD_WRITEABLE #define main_activity_LocalSocketClientActivity_MODE_WORLD_WRITEABLE 2L #undef main_activity_LocalSocketClientActivity_MODE_APPEND #define main_activity_LocalSocketClientActivity_MODE_APPEND 32768L #undef main_activity_LocalSocketClientActivity_MODE_MULTI_PROCESS #define main_activity_LocalSocketClientActivity_MODE_MULTI_PROCESS 4L #undef main_activity_LocalSocketClientActivity_BIND_AUTO_CREATE #define main_activity_LocalSocketClientActivity_BIND_AUTO_CREATE 1L #undef main_activity_LocalSocketClientActivity_BIND_DEBUG_UNBIND #define main_activity_LocalSocketClientActivity_BIND_DEBUG_UNBIND 2L #undef main_activity_LocalSocketClientActivity_BIND_NOT_FOREGROUND #define main_activity_LocalSocketClientActivity_BIND_NOT_FOREGROUND 4L #undef main_activity_LocalSocketClientActivity_BIND_ABOVE_CLIENT #define main_activity_LocalSocketClientActivity_BIND_ABOVE_CLIENT 8L #undef main_activity_LocalSocketClientActivity_BIND_ALLOW_OOM_MANAGEMENT #define main_activity_LocalSocketClientActivity_BIND_ALLOW_OOM_MANAGEMENT 16L #undef main_activity_LocalSocketClientActivity_BIND_WAIVE_PRIORITY #define main_activity_LocalSocketClientActivity_BIND_WAIVE_PRIORITY 32L #undef main_activity_LocalSocketClientActivity_BIND_IMPORTANT #define main_activity_LocalSocketClientActivity_BIND_IMPORTANT 64L #undef main_activity_LocalSocketClientActivity_BIND_ADJUST_WITH_ACTIVITY #define main_activity_LocalSocketClientActivity_BIND_ADJUST_WITH_ACTIVITY 128L #undef main_activity_LocalSocketClientActivity_CONTEXT_INCLUDE_CODE #define main_activity_LocalSocketClientActivity_CONTEXT_INCLUDE_CODE 1L #undef main_activity_LocalSocketClientActivity_CONTEXT_IGNORE_SECURITY #define main_activity_LocalSocketClientActivity_CONTEXT_IGNORE_SECURITY 2L #undef main_activity_LocalSocketClientActivity_CONTEXT_RESTRICTED #define main_activity_LocalSocketClientActivity_CONTEXT_RESTRICTED 4L #undef main_activity_LocalSocketClientActivity_RESULT_CANCELED #define main_activity_LocalSocketClientActivity_RESULT_CANCELED 0L #undef main_activity_LocalSocketClientActivity_RESULT_OK #define main_activity_LocalSocketClientActivity_RESULT_OK -1L #undef main_activity_LocalSocketClientActivity_RESULT_FIRST_USER #define main_activity_LocalSocketClientActivity_RESULT_FIRST_USER 1L #undef main_activity_LocalSocketClientActivity_DEFAULT_KEYS_DISABLE #define main_activity_LocalSocketClientActivity_DEFAULT_KEYS_DISABLE 0L #undef main_activity_LocalSocketClientActivity_DEFAULT_KEYS_DIALER #define main_activity_LocalSocketClientActivity_DEFAULT_KEYS_DIALER 1L #undef main_activity_LocalSocketClientActivity_DEFAULT_KEYS_SHORTCUT #define main_activity_LocalSocketClientActivity_DEFAULT_KEYS_SHORTCUT 2L #undef main_activity_LocalSocketClientActivity_DEFAULT_KEYS_SEARCH_LOCAL #define main_activity_LocalSocketClientActivity_DEFAULT_KEYS_SEARCH_LOCAL 3L #undef main_activity_LocalSocketClientActivity_DEFAULT_KEYS_SEARCH_GLOBAL #define main_activity_LocalSocketClientActivity_DEFAULT_KEYS_SEARCH_GLOBAL 4L /* * Class: main_activity_LocalSocketClientActivity * Method: startHeartBeat * Signature: ()I */ JNIEXPORT jint JNICALL Java_main_activity_LocalSocketClientActivity_startHeartBeat (JNIEnv *, jobject); #ifdef __cplusplus } #endif #endif 客户端native方法实现: /* 头文件begin */ #include "main_activity_LocalSocketClientActivity.h" #include <sys/socket.h> #include <sys/un.h> #include <stddef.h> #include <string.h> /* 头文件end */ #ifdef __cplusplus extern "C" { #endif /* * Class: main_activity_LocalSocketClientActivity * Method: startHeartBeat */ JNIEXPORT jint JNICALL Java_main_activity_LocalSocketClientActivity_startHeartBeat(JNIEnv * env, jobject object) { int socketID; struct sockaddr_un serverAddr; char path[] = "pym_local_socket\0"; int ret; socketID = socket_local_client(path, ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM); if (socketID < 0) { return socketID; } ret = close(socketID); if (ret < 0) { return CLOSE_ERR; } return NO_ERR; } /* 创建本地socket客户端 */ int socket_local_client(const char *name, int namespaceId, int type) { int socketID; int ret; socketID = socket(AF_LOCAL, type, 0); if(socketID < 0) { return CREATE_ERR; } ret = socket_local_client_connect(socketID, name, namespaceId, type); if (ret < 0) { close(socketID); return ret; } return socketID; } /* 连接到相应的fileDescriptor上 */ int socket_local_client_connect(int fd, const char *name, int namespaceId, int type) { struct sockaddr_un addr; socklen_t socklen; size_t namelen; int ret; ret = socket_make_sockaddr_un(name, namespaceId, &addr, &socklen); if (ret < 0) { return ret; } if(connect(fd, (struct sockaddr *) &addr, socklen) < 0) { return CONNECT_ERR; } return fd; } /* 构造sockaddr_un */ int socket_make_sockaddr_un(const char *name, int namespaceId, struct sockaddr_un *p_addr, socklen_t *socklen) { size_t namelen; MEM_ZERO(p_addr, sizeof(*p_addr)); #ifdef HAVE_LINUX_LOCAL_SOCKET_NAMESPACE namelen = strlen(name); // Test with length +1 for the *initial* '\0'. if ((namelen + 1) > sizeof(p_addr->sun_path)) { return LINUX_MAKE_ADDRUN_ERROR; } p_addr->sun_path[0] = 0; memcpy(p_addr->sun_path + 1, name, namelen); #else namelen = strlen(name) + strlen(FILESYSTEM_SOCKET_PREFIX); /* unix_path_max appears to be missing on linux */ if (namelen > (sizeof(*p_addr) - offsetof(struct sockaddr_un, sun_path) - 1)) { return NO_LINUX_MAKE_ADDRUN_ERROR; } strcpy(p_addr->sun_path, FILESYSTEM_SOCKET_PREFIX); strcat(p_addr->sun_path, name); #endif p_addr->sun_family = AF_LOCAL; *socklen = namelen + offsetof(struct sockaddr_un, sun_path) + 1; return NO_ERR; } #ifdef __cplusplus } #endif 注意到100~101行比较特殊,是从p_addr->sun_path[1]开始拷贝本地域名,这就是之前为什么一直connect不上的原因,至于为什么偏移1个字节来拷贝本地域名,你可以在*nix系统下输入"man 7 unix"来找到原因。 先启动server,再启动client就可以看到结果了。对了,在成功创建并已自动连接后,我并未发送任何数据,其实发送数据就是写入文件,It's your trun now! 在close之前加入这段代码吧~ Int ret; Char buf[] = “hello”; Ret = write(socketID, buf, strlen(buf)); 转载自: http://www.cnblogs.com/zealotrouge/p/3152941.html //////////////////////////////////////////////补充 socket_local_server 函数
#define LISTEN_BACKLOG 4 /*单次同步, 把信号量先初始化为0*/
/** * Binds a pre-created socket(AF_LOCAL) 's' to 'name' * returns 's' on success, -1 on fail * * Does not call listen() */ int socket_local_server_bind(int s, const char *name, int namespaceId) { struct sockaddr_un addr; socklen_t alen; int n; int err;
err = socket_make_sockaddr_un(name, namespaceId, &addr, &alen);
if (err < 0) { return -1; }
/* basically: if this is a filesystem path, unlink first */ #ifndef HAVE_LINUX_LOCAL_SOCKET_NAMESPACE if (1) { #else if (namespaceId == ANDROID_SOCKET_NAMESPACE_RESERVED || namespaceId == ANDROID_SOCKET_NAMESPACE_FILESYSTEM) { #endif /*ignore ENOENT*/ unlink(addr.sun_path); }
n = 1; setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n));
if(bind(s, (struct sockaddr *) &addr, alen) < 0) { return -1; }
return s;
}
int socket_local_server(const char *name, int namespace, int type) { int err; int s;
s = socket(AF_LOCAL, type, 0); if (s < 0) return -1;
err = socket_local_server_bind(s, name, namespace);
if (err < 0) { close(s); return -1; }
if (type == SOCK_STREAM) { int ret;
ret = listen(s, LISTEN_BACKLOG);
if (ret < 0) { close(s); return -1; } }
return s; }
|