Android利用LocalSocket实现Java端进程与C端进程之间的IPC
本帖最后由 空道 于 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; 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 __cplusplusextern "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 __cplusplusextern "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; 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开始拷贝本地域名,这就是之前为什么一直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;}
学习了~~
牛X的空道{:soso_e179:} 牛叉,顶起来 虽然看得不少太明白,但是还是支持一下 谢谢楼主分享呀。。。。。不过看到这么多的英文就头痛呀。。。。。 这篇文章我转走。
页:
[1]