将binder和socketpair结合实现任意进程间的双向通讯

binder机制是Android系统中特有的实现进程间远程通信的机制,它是基于C/S模式来实现的,一般一个是client端,一个是server端;而socketpair机制可以实现双向通讯,但是通讯范围限制在同一个进程的线程之间或者是具有亲缘关系的进程之间。本文通过将这两种机制结合起来,实现任意两个进程间的双向通讯。

作者本人能力有限,文中有说的不对或者不合理的地方还请多多指教。


一、基本开发环境和实现原理

1、基本开发环境

操作系统:Ubuntu12.04 64bit

交叉编译工具版本:gcc version 4.5.1 (ctng-1.8.1-FA)

Android系统版本:5.0.2

编程语言:C++

2、实现原理

socketpair在实例化时会创建两个文件描述符,分别用于双向通讯的两端,我们只要把其中的一个文件描述符通过binder机制传递给另外一个进程不就可以实现这两个进程间的双向通讯了。所以要做的工作主要就是在服务器端是实现一个方法来获取到这个文件描述符,然后通过binder机制传递给客服端。(注意:这里并不是简单的把文件描述符传递过去,具体内容可以参考相关资料)。


二、具体的实现

为了实现这个目的,编写了如下文件:

ISocketpairService.h : 实现一个接口文件,这个文件中实现了获取文件描述符的纯虚方法。

BnSocketpairService.cpp : Binder的本地类的实现,主要用于获取socketpair实例化的文件描述符

BpSocketpairService.cpp : Binder的代理类的实现,获取本地类传递过来的文件描述符

socketpair_server.cpp  : 服务器端的实现,把binder服务加入到Android系统当中,实现和客服端的双向通讯

socketpair_client.cpp : 客服端的实现,从Android系统中获取代理类对象,实现和服务器端的双向通讯


1、ISocketpairService.h

这个文件主要实现了接口类和本地类的定义,具体如下:

#ifndef ANDROID_ISOCKETPAIRSERVICE_H
#define ANDROID_ISOCKETPAIRSERVICE_H

#include   // for status_t
#include 
#include 
#include 
#include 
#include 

#define YL_SOCKETPAIR_SERVER_FD	0

namespace android {

/* 接口类 : 主要实现一个获取文件描述符的纯虚方法 */
class ISocketpairService: public IInterface
{
public:
    DECLARE_META_INTERFACE(SocketpairService);
	virtual int get_fd(void) = 0;
};

/* 本地类的定义 */
class BnSocketpairService: public BnInterface
{
private:
	int fd;
public:

	BnSocketpairService(void);
	BnSocketpairService(int fd);	// 定义一个构造函数获取传入的文件描述符

	/* binder RPC 层主体函数 */
    virtual status_t    onTransact( uint32_t code,		
                                    const Parcel& data,
                                    Parcel* reply,
                                    uint32_t flags = 0);

	virtual int get_fd(void);	// 获取文件描述符
};
}

#endif  // ANDROID_ISOCKETPAIRSERVICE_H

2、BnSocketpairService.cpp

这个文件主要是对本地类的实现和RPC层的核心函数onTransact,具体如下:

#define LOG_TAG "SocketpairService"

#include 
#include 
#include 
#include 


#include "ISocketpairService.h"


namespace android {

BnSocketpairService::BnSocketpairService(void){}

BnSocketpairService::BnSocketpairService(int fd)
{
	this->fd = fd;
}

status_t BnSocketpairService::onTransact( uint32_t code,
                                const Parcel& data,
                                Parcel* reply,
                                uint32_t flags)
{
	/* 解析数据,调用get_fd */

    switch (code) {
        case YL_SOCKETPAIR_SERVER_FD: {
			reply->writeDupFileDescriptor(get_fd());
            return NO_ERROR;
        } break;
		
        default:
            return BBinder::onTransact(code, data, reply, flags);
    }
}

/* 获取文件描述符 */
int BnSocketpairService::get_fd(void)
{
	return this->fd;
}

}

3、BpSocketpairService.cpp

这个类主要实现了代理类获取文件描述符的方法,具体如下:

#include "ISocketpairService.h"

namespace android {

class BpSocketpairService: public BpInterface
{
public:
    BpSocketpairService(const sp& impl)
        : BpInterface(impl)
    {
    }

	/* 获取文件描述符 */
	int get_fd(void)
	{
		Parcel data, reply;

        data.writeInt32(0);

        remote()->transact(YL_SOCKETPAIR_SERVER_FD, data, &reply);
		int rawFd = reply.readFileDescriptor();

		return dup(rawFd);
	}

};

IMPLEMENT_META_INTERFACE(SocketpairService, "android.media.ISocketpairService");

}

4、socketpair_server.cpp

这个是服务器端的试下,将binder服务加入到Android系统当中,并实例化socketpair,实现进程和客服端的双向通讯,具体实现如下所示:

#define LOG_TAG "SocketpairService"

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 			/* See NOTES */
#include 
#include 
#include 
#include 

#include "ISocketpairService.h"

/* 定义socketpair缓冲区的大小 */
#define SOCKET_BUFFER_SIZE 		((32) * (1024))

using namespace android;

/*
 *	定义子线程的主体函数
 */
static void *thread_run_func (void *arg)
{
	int thread_fd = (int)arg;
	char thread_buf[512] = {0};
	int readlen;

	/* b、接收子进程发送过来的消息 */
	readlen = read(thread_fd, thread_buf, 512);
	thread_buf[readlen] = '\0';
	ALOGI("(2) recv from child thread : %s\n", thread_buf);

	/* c、发送一个消息给子进程 */
	ALOGI("(3) send to child thread : %s\n", "TECH-PRO");
	write(thread_fd, "TECH-PRO", sizeof("TECH-PRO"));

	return NULL;
}

int main(int argc, char *argv[])
{
	int sockets[2];
	int result;
	int bufferSize;

	int readlen;
	char buf[512];

	pthread_t thread;

	/* 创建一个socketpair双向通讯的实例 */
	result = socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets);
	if(-1 == result)
	{
		ALOGI("socketpair error!\n");
		return -1;
	}

	/* 设置socketpair双向通讯各个缓冲区 */
	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));
	
	/* 打开驱动, mmap */
	sp proc(ProcessState::self());

	/* 获得BpServiceManager */
	sp sm = defaultServiceManager();

	sm->addService(String16("socketpair"), new BnSocketpairService(sockets[0]));

	/* 进入循环之前创建一个子线程和client端进行双向通讯 */
	/* 创建一个子线程用于和主线程之间进行双向通讯 */
	result = pthread_create(&thread, NULL, thread_run_func, (void *)sockets[1]);

	/* 循环体 */
	ProcessState::self()->startThreadPool();
	IPCThreadState::self()->joinThreadPool();

	return 0;
}

5、socketpair_client.cpp

获取代理类对象的实例,通过相关函数获取文件描述符,实现和服务器端的双向通讯,具体实现如下:

#define LOG_TAG "SocketpairService"

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 

#include "ISocketpairService.h"

using namespace android;

int main(int argc, char **argv)
{
	int fd;
	char buf[512];
	int readlen;

	/* getService */
	/* 打开驱动, mmap */
	sp proc(ProcessState::self());

	/* 获得BpServiceManager */
	sp sm = defaultServiceManager();

    sp binder =
        sm->getService(String16("socketpair"));

	if (binder == 0)
	{
        ALOGI("can't get socketpair service\n");
		return -1;
	}

	/* service肯定是BpHelloServie指针 */
    sp service =
        interface_cast(binder);

	fd = service->get_fd();

	ALOGI("fd is %d\n", fd);

	/* a、向主进程发送一个消息 */
	ALOGI("(1) send to main thread : %s\n", "Hello,world!");
	write(fd, "Hello,world!", sizeof("Hello,world!"));

	/* d、接收主进程发送的消息 */
	readlen = read(fd, buf, 512);
	buf[readlen] = '\0';
	ALOGI("(4) recv from main thread : %s\n", buf);
	
	return 0;
}

三、编译测试

1、在PC端编译

首先把上述编写文件放入一个目录中,我这里命名为YL_APP_socketpair_binder(名字不重要可以随便取),然后把它放入到Android系统的源代码目录:frameworks/testing当中,还要在目录中添加一个编译脚本Android.mk,它的具体实现如下:

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)

LOCAL_SRC_FILES:= \
	BnSocketpairService.cpp \
	BpSocketpairService.cpp \
	socketpair_server.cpp

LOCAL_SHARED_LIBRARIES := \
	libcutils \
	libutils \
	liblog \
	libbinder 


LOCAL_MODULE:= socketpair_server
LOCAL_32_BIT_ONLY := true

include $(BUILD_EXECUTABLE)

include $(CLEAR_VARS)

LOCAL_SRC_FILES:= \
	BpSocketpairService.cpp \
	socketpair_client.cpp

LOCAL_SHARED_LIBRARIES := \
	libcutils \
	libutils \
	liblog \
	libbinder 


LOCAL_MODULE:= socketpair_client
LOCAL_32_BIT_ONLY := true

include $(BUILD_EXECUTABLE)

然后执行 mmm frameworks/testing/YL_APP_socketpair_binder 进行编译,编译成功后在out/target/product/tiny4412/system/bin这个目录中生成了可执行程序:socketpair_server和socketpair_client。

2、在开发板上进行测试

将上面生成的socketpair_server 和 socketpair_client复制到开发板当中,执行以下命令进行测试:

logcat SocketpairService:* *:S &
./socketpair_server &
./socketpair_client 
测试结果如下:



从结果可以看出,成功的实现了连个进程间的双向通讯。


你可能感兴趣的:(Android系统开发,Linux系统编程,android,linux,binder,socketpair)