sheldon_blogs

输入系统:进程间双向通信(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编辑  收藏  举报

导航