简介
socketpair会创建一对无名套接字的描述符,具有全双工通信特性(描述符可读也可写),他的域只能为AF_UNIX(本地).
应用场景
socketpair主要用于C/S模式的进程间通讯.由于binder通信具有透传fd的特性,使得socketpair不再受限于亲缘关系进程
函数原型
SYNOPSIS
#include <sys/types.h>
#include <sys/socket.h>
int socketpair(int domain, int type, int protocol, int sv[2]);
DESCRIPTION
TYPE | 说明 |
---|---|
SOCK_STREAM | TCP协议,提供有序,面向连接,双向,可靠的传输 |
SOCK_DGRAM | UDP协议,提供面向无连接,不可靠的数据包传输 |
SOCK_SEQPACKET | 提供有序,双向,可靠的传输 |
EFD_CLOEXEC | 该标志位设置后,当执行exec族函数时,会自动关闭fd.防止越权和造成fd leak |
EFD_NONBLOCK | 该标志位设置后,执行IO操作时不会阻塞,会立即返回. |
想必大家对SOCK_STREAM与SOCK_SEQPACKET有疑问,这2个描述基本一样那他们有没区别呢?我们直接查看源码
// Af_unix.c (kernel-4.9\net\unix)
static int unix_seqpacket_sendmsg(struct socket *sock, struct msghdr *msg,
size_t len)
{
int err;
struct sock *sk = sock->sk;
err = sock_error(sk);
if (err)
return err;
if (sk->sk_state != TCP_ESTABLISHED)
return -ENOTCONN;
if (msg->msg_namelen)
msg->msg_namelen = 0;
return unix_dgram_sendmsg(sock, msg, len);
}
可以看到SOCK_SEQPACKET类型的发送数据流走的是SOCK_DGAM(UDP协议)的发送接口,该接口不具备TCP协议的重传功能
RETURN VALUE
On success, zero is returned
On error, -1 is returned, and errno is set appropriately
例子
/*本例子的流程为:
* 1. 调用socketpair得到一对双向fd
* 2. 调用fork,产生子进程
* 3. 父进程向子进程发送信息,子进程接收并打印
* 4. 子进程向父进程发送信息,父进程接收并打印
*/
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <signal.h>
void childLoop(int fd) {
char recvMsg[128] = { 0 };
char *sendMsg = "I am Child";
if (read(fd, recvMsg, sizeof(recvMsg) / sizeof(*recvMsg)) < 0) {
printf("%s read fail reason: %s\n", __func__, strerror(errno));
return;
}
printf("%s read msg : %s \n", __func__, recvMsg);
if (write(fd, sendMsg, strlen(sendMsg) + 1) < 0) {
printf("%s write fail reason: %s\n", __func__, strerror(errno));
return;
}
}
void parentLoop(int fd) {
char recvMsg[128] = { 0 };
char *sendMsg = "I am Father";
if (write(fd, sendMsg, strlen(sendMsg) + 1) < 0) {
printf("%s write fail reason: %s\n", __func__, strerror(errno));
return;
}
if (read(fd, recvMsg, sizeof(recvMsg) / sizeof(*recvMsg)) < 0) {
printf("%s read fail reason: %s\n", __func__, strerror(errno));
return;
}
printf("%s read msg : %s \n", __func__, recvMsg);
}
int main(int argc __unused, char **argv __unused) {
int pid;
int fd[2];
int err = 0;
signal(SIGCHLD, SIG_IGN); //忽略子进程停止信号,防止产生僵尸进程
err = socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, fd);
if (err != 0) {
printf("socketpair fail reason: %s\n", strerror(errno));
return -1;
}
pid = fork();
if (pid < 0) {
printf("fork fail reason: %s\n", strerror(errno));
return -1;
}
if (pid == 0) { //子进程
close(fd[0]); //关闭fd[0]
childLoop(fd[1]);
close(fd[1]); //关闭fd[1]
return 0;
}else { //父进程
close(fd[1]); //关闭fd[1]
parentLoop(fd[0]);
close(fd[0]); //关闭fd[0]
return 0;
}
return 0;
}
运行结果 ``` generic_arm64:/ # example example childLoop read msg : I am Father parentLoop read msg : I am Child