Android输入系统IMS(2)--基础知识socketpair

套接字可以用于网络通信,也可以用于本机内的进程通信。由于本机内进程的IP地址都相同,因此只需要进程号来确定通信的双方。Linux环境下使用socketpair函数创造一对未命名的、相互连接的UNIX域套接字。

socketpair 函数

功能:创建一个全双工的流管道
原型 int socketpair(int domain, int type, int protocol, int sv[2]);
参数
domain: 协议家族
type: 套接字类型
protocol: 协议类型
sv: 返回套接字对
返回值:成功返回0;失败返回-1

实际上socketpair 函数跟pipe 函数是类似的,也只能在同个主机上具有亲缘关系的进程间通信,但pipe 创建的匿名管道是半双工的,而socketpair 可以认为是创建一个全双工的管道。

可以使用socketpair 创建返回的套接字对进行父子进程通信

setsockopt设置函数。

DEMO程序:

#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>

#define SOCKET_BUFFER_SIZE      (32768U)

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];
/*
int socketpair(int domain, int type, int protocol, int sv[2]);

socketpair函数需要四个参数:
domain-套接口的域
type-套接口类型
protocol-使用的协议
sv[2]-指向存储文件描述符的指针

类型参数声明了我们希望创建哪种类型的套接口,socketpair函数的选择如下:
SOCK_STREAM
SOCK_DGRAM
对于socketpair函数,protocol参数必须提供为0。
参数sv[2]是接收代表两个套接口的整数数组。每一个文件描述符代表一个套接口,并且与另一个并没有区别。
如果函数成功,将会返回0值。否则将会返回-1表明创建失败,并且errno来表明特定的错误号。
socketpair可以用于多进程间全双工通讯。
1.调用socketpair,成功后便有两个socket文件描述符,一个socket就像是一个pipe。
在两个socket中指定一个给父进程使用,另外一个给子进程使用。不用在意指定哪一个socket给父进程使用,
随便挑一个就可以了。
2.调用fork,成功后就创建了子进程

    2.1 fork返回0,那就是子进程,关闭父进程的socket,保留子进程的socket

    2.2 fork返回非0,那就是父进程,关闭子进程的socket,保留父进程的socket
	为什么要关闭socket,现在还没有搞清楚。

3.现在有父子两个进程,每个进程都有一个socket描述符用以代表同一个pipe的两端。如果父进程调用write,
那么子进程就调用read,反之亦然。实现父子进程双向通信。
*/
	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]);//把一个文件句柄传给线程AA


	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);
	}
}

socketpair双向通信在Android输入系统有什么用了?InputReader负责通过EventHub读取输入事件,InputDispatcher负责分发事件。InputChannel就是用于InputDispatcher把事件分发给具体应用程序用的。

InputChannel的创建是在 ViewRootImplsetView方法中。也就是说InputDispatcher通过InputChannel 把事件发给了ViewRootImpl。这其中的InputChannel和ViewRootImpl就使用到了socketpair通信机制。相关代码:

status_t InputChannel::openInputChannelPair(const String8& name,
        sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
//创建一对
    int sockets[2];
    if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
        status_t result = -errno;
        ALOGE("channel '%s' ~ Could not create socket pair.  errno=%d",
                name.string(), errno);
        outServerChannel.clear();
        outClientChannel.clear();
        return result;
    }

    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));

    String8 serverChannelName = name;
    serverChannelName.append(" (server)");
    /*Server的名字,name的值是从WMS传过来的*/
    outServerChannel = new InputChannel(serverChannelName, sockets[0]);

    String8 clientChannelName = name;
    clientChannelName.append(" (client)");
    /*clien端使用*/
    outClientChannel = new InputChannel(clientChannelName, sockets[1]);
    return OK;
}

之后的收发操作:

status_t InputChannel::sendMessage(const InputMessage* msg) {
    size_t msgLength = msg->size();
    ssize_t nWrite;
    do {
        nWrite = ::send(mFd, msg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
    } while (nWrite == -1 && errno == EINTR);
...........................................
}
status_t InputChannel::receiveMessage(InputMessage* msg) {
    ssize_t nRead;
    do {
        nRead = ::recv(mFd, msg, sizeof(InputMessage), MSG_DONTWAIT);
    } while (nRead == -1 && errno == EINTR);
........................
}

也就是说c++层的InputDispatcher最终是通过socketpair通信方式把 event事件发给java层的app程序的,而不是jni,binder的方式。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值