输入系统:进程间双向通信(socketpair+binder)
一、双向通信(socketpair)
socketpair()函数用于创建一对无名的、相互连接的套接子,如果函数成功,则返回0,创建好的套接字分别是sv[0]和sv[1];否则返回-1,错误码保存于errno中。
- socketpair()函数的声明:
#include <sys/types.h> #include <sys/socket.h> int socketpair(int domain, int type, int protocol, int sv[2]);
- 参数说明:
参数1(domain):表示协议族,在Linux下只能为AF_LOCAL或者AF_UNIX。(自从Linux 2.6.27后也支持SOCK_NONBLOCK和SOCK_CLOEXEC)
参数2(type):表示协议,可以是SOCK_STREAM或者SOCK_DGRAM。SOCK_STREAM是基于TCP的,而SOCK_DGRAM是基于UDP的
参数3(protocol):表示类型,只能为0
参数4(sv[2]):套节字柄对,该两个句柄作用相同,均能进行读写双向操作
返回errno含义:
EAFNOSUPPORT:本机上不支持指定的address。
EFAULT: 地址sv无法指向有效的进程地址空间内。
EMFILE: 已经达到了系统限制文件描述符,或者该进程使用过量的描述符。
EOPNOTSUPP:指定的协议不支持创建套接字对。
EPROTONOSUPPORT:本机不支持指定的协议。
- 注意:
1.该函数只能用于UNIX域(LINUX)下。
2.只能用于有亲缘关系的进程(或线程)间通信。
3.所创建的套节字对作用是一样的,均能够可读可写(而管道PIPE只能进行单向读或写)。
4.这对套接字可以用于全双工通信,每一个套接字既可以读也可以写。例如,可以往sv[0]中写,从sv[1]中读;或者从sv[1]中写,从sv[0]中读;
5.该函数是阻塞的,且如果往一个套接字(如sv[0])中写入后,再从该套接字读时会阻塞,只能在另一个套接字中(sv[1])上读成功;
6. 读、写操作可以位于同一个进程,也可以分别位于不同的进程,如父子进程。如果是父子进程时,一般会功能分离,一个进程用来读,一个用来写。因为文件描述副sv[0]和sv[1]是进程共享的,所以读的进程要关闭写描述符, 反之,写的进程关闭读描述符。
示例代码1(同一进程不同线程间通信):
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#define SOCKET_BUFFER_SIZE (32768U)
/* 参考:
* frameworks\native\libs\input\InputTransport.cpp
*/
void *function_thread1 (void *arg)
{
int fd = (int)arg;
char buf[500];
int len;
int cnt = 0;
while (1)
{
/* 向 main线程发出: Hello, main thread */
len = sprintf(buf, "Hello, main thread, cnt = %d", cnt++);
write(fd, buf, len);
/* 读取数据(main线程发回的数据) */
len = read(fd, buf, 500);
buf[len] = '\0';
printf("%s\n", buf);
sleep(5);
}
return NULL;
}
int main(int argc, char **argv)
{
int sockets[2];
socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets);
int bufferSize = SOCKET_BUFFER_SIZE;
setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
/* 创建线程1 */
pthread_t threadID;
pthread_create(&threadID, NULL, function_thread1, (void *)sockets[1]);
char buf[500];
int len;
int cnt = 0;
int fd = sockets[0];
while(1)
{
/* 读数据: 线程1发出的数据 */
len = read(fd, buf, 500);
buf[len] = '\0';
printf("%s\n", buf);
/* main thread向thread1 发出: Hello, thread1 */
len = sprintf(buf, "Hello, thread1, cnt = %d", cnt++);
write(fd, buf, len);
}
}
参考Android源码:
frameworks\native\libs\input\InputTransport.cpp (socketpair)
调用过程:
WindowManagerService.java
InputChannel.openInputChannelPair(name)
nativeOpenInputChannelPair(name);
android_view_InputChannel_nativeOpenInputChannelPair
InputChannel::openInputChannelPair (InputTransport.cpp)
示例代码2(父子进程间通信):
#include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <error.h> #include <errno.h> #include <sys/socket.h> #include <stdlib.h> const char* str = "SOCKET PAIR TEST."; int main(int argc, char* argv[]){ char buf[128] = {0}; int socket_pair[2]; pid_t pid; if(socketpair(AF_UNIX, SOCK_STREAM, 0, socket_pair) == -1 ) { printf("Error, socketpair create failed, errno(%d): %s\n", errno, strerror(errno)); return EXIT_FAILURE; } pid = fork(); //创建子进程 if(pid < 0) { printf("Error, fork failed, errno(%d): %s\n", errno, strerror(errno)); return EXIT_FAILURE; } else if(pid > 0) { //父进程 //关闭另外一个套接字 close(socket_pair[1]); int size = write(socket_pair[0], str, strlen(str)); printf("Write success, pid: %d\n", getpid()); } else if(pid == 0) { //子进程 //关闭另外一个套接字 close(socket_pair[0]); read(socket_pair[1], buf, sizeof(buf)); printf("Read result: %s, pid: %d\n",buf, getpid()); } for(;;) { sleep(1); } return EXIT_SUCCESS; }
二、任意进程间通信(socketpair_binder)
Android系统中进程间通信方式主要为Binder,而Binder通信方式中,Client端可以主动发起请求与Server端通信,但Server端无法向Client端主动发起请求,基于socketpair + binder可以实现任意进程间的双向通信(通过binder将fd句柄传到另一个非亲缘关系的进程),代码实现如下:
1.IHelloService.h (Hello服务接口定义)
/* 参考: frameworks\av\include\media\IMediaPlayerService.h */ #ifndef ANDROID_IHELLOERVICE_H #define ANDROID_IHELLOERVICE_H #include <utils/Errors.h> // for status_t #include <utils/KeyedVector.h> #include <utils/RefBase.h> #include <utils/String8.h> #include <binder/IInterface.h> #include <binder/Parcel.h> #define HELLO_SVR_CMD_SAYHELLO 1 #define HELLO_SVR_CMD_SAYHELLO_TO 2 #define HELLO_SVR_CMD_GET_FD 3 namespace android { class IHelloService: public IInterface { public: DECLARE_META_INTERFACE(HelloService); virtual void sayhello(void) = 0; virtual int sayhello_to(const char *name) = 0; virtual int get_fd(void) = 0; }; class BnHelloService: public BnInterface<IHelloService> { private: int fd; public: virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); virtual void sayhello(void); virtual int sayhello_to(const char *name); virtual int get_fd(void); BnHelloService(); BnHelloService(int fd); }; } #endif
2.IGoodbyeService.h (Goodbye服务接口定义)
/* 参考: frameworks\av\include\media\IMediaPlayerService.h */ #ifndef ANDROID_IGOODBYEERVICE_H #define ANDROID_IGOODBYEERVICE_H #include <utils/Errors.h> // for status_t #include <utils/KeyedVector.h> #include <utils/RefBase.h> #include <utils/String8.h> #include <binder/IInterface.h> #include <binder/Parcel.h> #define GOODBYE_SVR_CMD_SAYGOODBYE 1 #define GOODBYE_SVR_CMD_SAYGOODBYE_TO 2 namespace android { class IGoodbyeService: public IInterface { public: DECLARE_META_INTERFACE(GoodbyeService); virtual void saygoodbye(void) = 0; virtual int saygoodbye_to(const char *name) = 0; }; class BnGoodbyeService: public BnInterface<IGoodbyeService> { public: virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); virtual void saygoodbye(void); virtual int saygoodbye_to(const char *name); }; } #endif
3.BnHelloService.cpp (Hello服务本地类)
/* 参考: frameworks\av\media\libmedia\IMediaPlayerService.cpp */ #define LOG_TAG "HelloService" #include "IHelloService.h" namespace android { BnHelloService::BnHelloService() { } BnHelloService::BnHelloService(int fd) { this->fd = fd; } status_t BnHelloService::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { /* 解析数据,调用sayhello/sayhello_to */ switch (code) { case HELLO_SVR_CMD_SAYHELLO: { sayhello(); reply->writeInt32(0); /* no exception */ return NO_ERROR; } break; case HELLO_SVR_CMD_SAYHELLO_TO: { /* 从data中取出参数 */ int32_t policy = data.readInt32(); String16 name16_tmp = data.readString16(); /* IHelloService */ String16 name16 = data.readString16(); String8 name8(name16); int cnt = sayhello_to(name8.string()); /* 把返回值写入reply传回去 */ reply->writeInt32(0); /* no exception */ reply->writeInt32(cnt); return NO_ERROR; } break; case HELLO_SVR_CMD_GET_FD: { int fd = this->get_fd(); reply->writeInt32(0); /* no exception */ /* 参考: * frameworks\base\core\jni\android_view_InputChannel.cpp * android_view_InputChannel_nativeWriteToParcel */ reply->writeDupFileDescriptor(fd); return NO_ERROR; } break; default: return BBinder::onTransact(code, data, reply, flags); } } void BnHelloService::sayhello(void) { static int cnt = 0; ALOGI("say hello : %d\n", ++cnt); } int BnHelloService::sayhello_to(const char *name) { static int cnt = 0; ALOGI("say hello to %s : %d\n", name, ++cnt); return cnt; } int BnHelloService::get_fd(void) { return fd; } }
4.BnGoodbyeService.cpp (Goodbye服务本地类)
/* 参考: frameworks\av\media\libmedia\IMediaPlayerService.cpp */ #define LOG_TAG "GoodbyeService" #include "IGoodbyeService.h" namespace android { status_t BnGoodbyeService::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { /* 解析数据,调用saygoodbye/saygoodbye_to */ switch (code) { case GOODBYE_SVR_CMD_SAYGOODBYE: { saygoodbye(); reply->writeInt32(0); /* no exception */ return NO_ERROR; } break; case GOODBYE_SVR_CMD_SAYGOODBYE_TO: { /* 从data中取出参数 */ int32_t policy = data.readInt32(); String16 name16_tmp = data.readString16(); /* IGoodbyeService */ String16 name16 = data.readString16(); String8 name8(name16); int cnt = saygoodbye_to(name8.string()); /* 把返回值写入reply传回去 */ reply->writeInt32(0); /* no exception */ reply->writeInt32(cnt); return NO_ERROR; } break; default: return BBinder::onTransact(code, data, reply, flags); } } void BnGoodbyeService::saygoodbye(void) { static int cnt = 0; ALOGI("say goodbye : %d\n", ++cnt); } int BnGoodbyeService::saygoodbye_to(const char *name) { static int cnt = 0; ALOGI("say goodbye to %s : %d\n", name, ++cnt); return cnt; } }
5.BpHelloService.cpp (Hello服务代理类)
/* 参考: frameworks\av\media\libmedia\IMediaPlayerService.cpp */ #include "IHelloService.h" namespace android { class BpHelloService: public BpInterface<IHelloService> { public: BpHelloService(const sp<IBinder>& impl) : BpInterface<IHelloService>(impl) { } void sayhello(void) { /* 构造/发送数据 */ Parcel data, reply; data.writeInt32(0); data.writeString16(String16("IHelloService")); remote()->transact(HELLO_SVR_CMD_SAYHELLO, data, &reply); } int sayhello_to(const char *name) { /* 构造/发送数据 */ Parcel data, reply; int exception; data.writeInt32(0); data.writeString16(String16("IHelloService")); data.writeString16(String16(name)); remote()->transact(HELLO_SVR_CMD_SAYHELLO_TO, data, &reply); exception = reply.readInt32(); if (exception) return -1; else return reply.readInt32(); } int get_fd(void) { /* 构造/发送数据 */ Parcel data, reply; int exception; data.writeInt32(0); data.writeString16(String16("IHelloService")); remote()->transact(HELLO_SVR_CMD_GET_FD, data, &reply); exception = reply.readInt32(); if (exception) return -1; else { /* 参考: * frameworks\base\core\jni\android_view_InputChannel.cpp * android_view_InputChannel_nativeReadFromParcel */ int rawFd = reply.readFileDescriptor(); return dup(rawFd); //复制句柄,reply中析构会关闭原始句柄 } } }; IMPLEMENT_META_INTERFACE(HelloService, "android.media.IHelloService"); }
6.BpGoodbyeService.cpp (Goodbye服务代理类)
/* 参考: frameworks\av\media\libmedia\IMediaPlayerService.cpp */ #include "IGoodbyeService.h" namespace android { class BpGoodbyeService: public BpInterface<IGoodbyeService> { public: BpGoodbyeService(const sp<IBinder>& impl) : BpInterface<IGoodbyeService>(impl) { } void saygoodbye(void) { /* 构造/发送数据 */ Parcel data, reply; data.writeInt32(0); data.writeString16(String16("IGoodbyeService")); remote()->transact(GOODBYE_SVR_CMD_SAYGOODBYE, data, &reply); } int saygoodbye_to(const char *name) { /* 构造/发送数据 */ Parcel data, reply; int exception; data.writeInt32(0); data.writeString16(String16("IGoodbyeService")); data.writeString16(String16(name)); remote()->transact(GOODBYE_SVR_CMD_SAYGOODBYE_TO, data, &reply); exception = reply.readInt32(); if (exception) return -1; else return reply.readInt32(); } }; IMPLEMENT_META_INTERFACE(GoodbyeService, "android.media.IGoodbyeService"); }
7.test_server.cpp
/* 参考: frameworks\av\media\mediaserver\Main_mediaserver.cpp */ #define LOG_TAG "TestService" //#define LOG_NDEBUG 0 #include <fcntl.h> #include <sys/prctl.h> #include <sys/wait.h> #include <binder/IPCThreadState.h> #include <binder/ProcessState.h> #include <binder/IServiceManager.h> #include <cutils/properties.h> #include <utils/Log.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/socket.h> #include "IHelloService.h" #include "IGoodbyeService.h" #define SOCKET_BUFFER_SIZE (32768U) using namespace android; /* 参考: * http://blog.csdn.net/linan_nwu/article/details/8222349 */ class MyThread: public Thread { private: int fd; public: MyThread() {} MyThread(int fd) { this->fd = fd; } //如果返回true,循环调用此函数,返回false下一次不会再调用此函数 bool threadLoop() { char buf[500]; int len; int cnt = 0; while(1) { /* 读数据: test_client发出的数据 */ len = read(fd, buf, 500); buf[len] = '\0'; ALOGI("%s\n", buf); /* 向 test_client 发出: Hello, test_client */ len = sprintf(buf, "Hello, test_client, cnt = %d", cnt++); write(fd, buf, len); } return true; } }; /* usage : test_server */ int main(void) { int sockets[2]; socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets); int bufferSize = SOCKET_BUFFER_SIZE; setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize)); setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize)); setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize)); setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize)); /* 创建一个线程, 用于跟test_client使用socketpiar通信 */ sp<MyThread> th = new MyThread(sockets[0]); th->run(); /* addService */ /* while(1){ read data, 解析数据, 调用服务函数 } */ /* 打开驱动, mmap */ sp<ProcessState> proc(ProcessState::self()); /* 获得BpServiceManager */ sp<IServiceManager> sm = defaultServiceManager(); sm->addService(String16("hello"), new BnHelloService(sockets[1])); //传入句柄,Hello服务类的构造函数中会保存 sm->addService(String16("goodbye"), new BnGoodbyeService()); /* 循环体 */ ProcessState::self()->startThreadPool(); IPCThreadState::self()->joinThreadPool(); return 0; }
8.test_client.cpp
#define LOG_TAG "TestService" //#define LOG_NDEBUG 0 #include <fcntl.h> #include <sys/prctl.h> #include <sys/wait.h> #include <binder/IPCThreadState.h> #include <binder/ProcessState.h> #include <binder/IServiceManager.h> #include <cutils/properties.h> #include <utils/Log.h> #include <unistd.h> #include "IHelloService.h" #include "IGoodbyeService.h" using namespace android; /* ./test_client <hello|goodbye> * ./test_client <readfile> * ./test_client <hello|goodbye> <name> */ int main(int argc, char **argv) { int cnt; if (argc < 2){ ALOGI("Usage:\n"); ALOGI("%s <readfile>\n", argv[0]); ALOGI("%s <hello|goodbye>\n", argv[0]); ALOGI("%s <hello|goodbye> <name>\n", argv[0]); return -1; } /* getService */ /* 打开驱动, mmap */ sp<ProcessState> proc(ProcessState::self()); /* 获得BpServiceManager */ sp<IServiceManager> sm = defaultServiceManager(); if (strcmp(argv[1], "hello") == 0) { sp<IBinder> binder = sm->getService(String16("hello")); if (binder == 0) { ALOGI("can't get hello service\n"); return -1; } /* service肯定是BpHelloServie指针 */ sp<IHelloService> service = interface_cast<IHelloService>(binder); /* 调用Service的函数 */ if (argc < 3) { service->sayhello(); ALOGI("client call sayhello"); } else { cnt = service->sayhello_to(argv[2]); ALOGI("client call sayhello_to, cnt = %d", cnt); } } else if (strcmp(argv[1], "readfile") == 0) { sp<IBinder> binder = sm->getService(String16("hello")); if (binder == 0) { ALOGI("can't get hello service\n"); return -1; } /* service肯定是BpHelloServie指针 */ sp<IHelloService> service = interface_cast<IHelloService>(binder); /* 调用Service的函数 */ int fd = service->get_fd(); ALOGI("client call get_fd = %d", fd); char buf[500]; int len; int cnt = 0; while (1) { /* 向 test_server 进程发出: Hello, test_server */ len = sprintf(buf, "Hello, test_server, cnt = %d", cnt++); write(fd, buf, len); /* 读取数据(test_server进程发回的数据) */ len = read(fd, buf, 500); buf[len] = '\0'; ALOGI("%s\n", buf); sleep(5); } } else { sp<IBinder> binder = sm->getService(String16("goodbye")); if (binder == 0) { ALOGI("can't get goodbye service\n"); return -1; } /* service肯定是BpGoodbyeServie指针 */ sp<IGoodbyeService> service = interface_cast<IGoodbyeService>(binder); /* 调用Service的函数 */ if (argc < 3) { service->saygoodbye(); ALOGI("client call saygoodbye"); } else { cnt = service->saygoodbye_to(argv[2]); ALOGI("client call saygoodbye_to, cnt = %d", cnt); } } return 0; }
9.Android.mk
LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ BnHelloService.cpp \ BpHelloService.cpp \ BnGoodbyeService.cpp \ BpGoodbyeService.cpp \ test_server.cpp LOCAL_SHARED_LIBRARIES := \ libcutils \ libutils \ liblog \ libbinder LOCAL_MODULE:= test_server LOCAL_32_BIT_ONLY := true include $(BUILD_EXECUTABLE) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ BpHelloService.cpp \ BpGoodbyeService.cpp \ test_client.cpp LOCAL_SHARED_LIBRARIES := \ libcutils \ libutils \ liblog \ libbinder LOCAL_MODULE:= test_client LOCAL_32_BIT_ONLY := true include $(BUILD_EXECUTABLE)
参考代码:
frameworks\base\core\jni\android_view_InputChannel.cpp (用binder传文件句柄) server端写fd: android_view_InputChannel_nativeWriteToParcel parcel->writeDupFileDescriptor client端读fd: android_view_InputChannel_nativeReadFromParcel int rawFd = parcel->readFileDescriptor(); int dupFd = dup(rawFd); frameworks\native\libs\binder\Parcel.cpp
-end-
posted on 2019-03-16 15:57 sheldon_blogs 阅读(2850) 评论(0) 编辑 收藏 举报