Android利用LocalSocket实现Java端进程与C端进程之间的IPC
2013-12-04 18:46
537 查看
Android是建立在Linux之上的OS,在涉及到安全、网络协议、文件加密等功能时,往往需要通过C语言调用底层API来实现,而如何发出指令让C端执行我们想要的功能,并且在执行之后有返回结果呢,这就需要打通Java端进程和C端进程,使之能高效地通信。这样,C端进程用于实现功能,Java端进程负责UI、功能的触发及结果处理就可以了。
对于unix系统来说,“一切皆为文件”,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。
服务端代码如下:
客户端代码如下:
上述客户端代码启动了一个线程用于发送“心跳”报文,每隔1s构建一个新的LocalSocket,连接服务端并发送流数据,其核心就在于native方法的实现。值得一提的是,我最初使用原生socket函数,没想connect总是返回错误;后来在同事的提醒下,我参考了Android源码rild.c中socket_local_client的使用,并从socket_local_client.c中抽取出相应代码改写而成。
客户端native方法头文件:
客户端native方法实现:
注意到100~101行比较特殊,是从p_addr->sun_path[1]开始拷贝本地域名,这就是之前为什么一直connect不上的原因,至于为什么偏移1个字节来拷贝本地域名,你可以在unix系统下输入"man 7 unix"来找到原因。
先启动server,再启动client就可以看到结果了。对了,在成功创建并已自动连接后,我并未发送任何数据,其实发送数据就是写入文件,It's your trun now! 在close之前加入这段代码吧~
对于unix系统来说,“一切皆为文件”,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个字节来拷贝本地域名,你可以在unix系统下输入"man 7 unix"来找到原因。
先启动server,再启动client就可以看到结果了。对了,在成功创建并已自动连接后,我并未发送任何数据,其实发送数据就是写入文件,It's your trun now! 在close之前加入这段代码吧~
int ret; char buf[] = "hello"; ret = write(socketID, buf, strlen(buf));
相关文章推荐
- android 代码实现控件之间的间距
- 解決Linux下Android开发真机调试设备不被识别问题
- [Android]在代码里运行另一个程序的方法
- [软件咨询]WPS2012正式版已发布 金山Office移动版4.0发布
- Android笔记-Linux Kernel Ftrace (Function Trace)解析
- android USB如何修改VID具体实现
- Android Mouse实现过程详细笔记
- 深入Android Browser配置管理的详解
- 进程间通信之深入消息队列的详解
- Android Mms之:深入理解对话列表管理
- Android APP与媒体存储服务的交互
- android 多线程技术应用
- Android之采用execSQL与rawQuery方法完成数据的添删改查操作详解
- Android数据类型之间相互转换系统介绍
- 通过Android trace文件分析死锁ANR实例过程
- Android实现左右滑动效果的方法详解
- Android 各国语言缩写及简称详细介绍
- Android 自动判断是电话,网址,EMAIL方法之Linkify的使用
- android IPC之binder通信机制
- Android应用开发中模拟按下HOME键的效果(实现代码)