简述
在Linux中,socketpair
函数可以用于创建一对相互连接的、通信域为AF_UNIX
的套接字,其中一个套接字可用于读取,另一个套接字可用于写入。可以使用这对套接字在同一进程内进行进程间通信(IPC)。
以下是使用socketpair
函数创建套接字对的基本步骤:
-
包含头文件
sys/types.h
、sys/socket.h
和unistd.h
。 -
声明一个长度为2的数组作为套接字描述符的参数。
-
调用
socketpair
函数,并将套接字描述符数组作为参数传递给它。函数原型为:int socketpair(int domain, int type, int protocol, int sv[2]);
domain
参数指定通信域,通常设置为AF_UNIX。type
参数指定套接字类型,可以设置为SOCK_STREAM
或SOCK_DGRAM
。protocol
参数通常设置为0,表示使用默认协议。sv
参数是一个长度为2的整数数组,用于保存创建的套接字描述符。
-
使用
sv
数组中的套接字描述符进行进程间通信。
Linux举例
下面是一个基于 socketpair
的简单例子,用于在父子进程之间传递数据:
#include <unistd.h>
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
int main() {
int sockfd[2];
char buf[1024];
pid_t pid;
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockfd) < 0) {
perror("socketpair");
return 1;
}
if ((pid = fork()) == -1) {
perror("fork");
return 1;
}
if (pid == 0) { // 子进程
close(sockfd[0]);
char msg[] = "Hello, parent!";
if (write(sockfd[1], msg, strlen(msg)) < 0) {
perror("write");
return 1;
}
close(sockfd[1]);
} else { // 父进程
close(sockfd[1]);
int n = read(sockfd[0], buf, sizeof(buf));
if (n < 0) {
perror("read");
return 1;
}
printf("Parent: received message from child: %s\n", buf);
close(sockfd[0]);
}
return 0;
}
该程序首先调用 socketpair
函数创建一对 UNIX 域套接字,存储在 sockfd
数组中。然后,程序调用 fork
函数创建一个子进程。在子进程中,它关闭 sockfd[0]
,然后将一条消息写入 sockfd[1]
,然后关闭 sockfd[1]
。在父进程中,它关闭 sockfd[1]
,然后从 sockfd[0]
中读取一条消息,并将其打印到标准输出中,最后关闭 sockfd[0]
。
当该程序运行时,它会创建一个子进程,子进程向父进程发送一条消息,父进程接收到该消息并将其打印出来:
Parent: received message from child: Hello, parent!
Android framework 举例
android-12.0.0_r28/frameworks/native/libs/sensor/BitTube.cpp
BitTube
void BitTube::init(size_t rcvbuf, size_t sndbuf) {
int sockets[2];
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets) == 0) {
size_t size = DEFAULT_SOCKET_BUFFER_SIZE;
setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf));
setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf));
// sine we don't use the "return channel", we keep it small...
setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));
setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
fcntl(sockets[0], F_SETFL, O_NONBLOCK);
fcntl(sockets[1], F_SETFL, O_NONBLOCK);
mReceiveFd = sockets[0];
mSendFd = sockets[1];
} else {
mReceiveFd = -errno;
ALOGE("BitTube: pipe creation failed (%s)", strerror(-mReceiveFd));
}
}
BitTube.cpp
是 Android 中传感器服务和传感器驱动程序之间通信使用的 BitTube
类的实现文件。
InputTransport
android-12.0.0_r28/frameworks/native/libs/input/InputTransport.cpp
status_t InputChannel::openInputChannelPair(const std::string& name,
std::unique_ptr<InputChannel>& outServerChannel,
std::unique_ptr<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=%s(%d)", name.c_str(),
strerror(errno), errno);
outServerChannel.reset();
outClientChannel.reset();
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));
sp<IBinder> token = new BBinder();
std::string serverChannelName = name + " (server)";
android::base::unique_fd serverFd(sockets[0]);
outServerChannel = InputChannel::create(serverChannelName, std::move(serverFd), token);
std::string clientChannelName = name + " (client)";
android::base::unique_fd clientFd(sockets[1]);
outClientChannel = InputChannel::create(clientChannelName, std::move(clientFd), token);
return OK;
}
在 Android Input 系统中,InputTransport
使用 socketpair
函数创建一对全双工的本地套接字(socket),用于在应用程序和驱动程序之间传递输入事件。其中一个套接字用于向驱动程序发送输入事件,另一个套接字用于接收来自驱动程序的输入事件。通过这种方式,InputTransport
可以将输入事件发送到驱动程序,并接收驱动程序生成的输入事件。