上一篇初步学习了UNIX域协议相关知识,接下来继续学习socketpair函数、sendmsg/recvmsg函数以及UNIX域套接字传递描述符字。
1.socketpair函数
- 功能:创建一个全双工的流管道
- 原型:int socketpair(int domain, int type, int protocol, int sv[2]);
- 参数:
- domain:协议家族
- type:套接字类型
- protocol:协议类型
- sv:返回套接字对
- 返回值:成功返回0;失败返回-1
(1)socketpair函数有点类似于linux系统编程里讲的半双工pipe管道。socketpair与之不同的是:它是一个全双工的流管道;其他一样,它也只能用于父子进程或者具有亲缘关系间的进程间进行通信。
(2)使用socketpair来实现一个全双工的通信
1)首先,创建一个套接字对,定义一个数组用来接收套接字对:
int sockfds[2];
socketpair(AF_UNIX, SOCK_STREAM, 0, sockfds);
2)这样的套接字对只能用于父子进程或亲缘关系进程间通信,fork()一个进程出来,在父子进程之间实现全双工的通信,pid>0为父进程,pid==0为子进程:
pid_t pid;
pid = fork();
3)定义一个变量,在父子进程间以++操作的方式来进行通信:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
if (pid > 0) { int val = 0; close(sockfds[1]); //关闭其中一个套接字 while(1) { ++val; printf("sending data: %d\n", val); write(sockfds[0], &val, sizeof(val)); read(sockfds[0], &val, sizeof(val)); printf("data received: %d\n", val); sleep(1); } } else if (pid == 0) { int val; close(sockfds[0]); while(1) { read(sockfds[1], &val, sizeof(val)); //接收对方发来的val ++val; //接收完之后对其做++操作,然后再发回去 write(sockfds[1], &val, sizeof(val));//反射回去之后,父进程会打印出val,观察其值是否+1了 } } |
以上完整源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
#include<unistd.h> #include<sys/socket.h> #include<sys/types.h> #include<sys/un.h> #include<stdlib.h> #include<stdio.h> #include<errno.h> #include<string.h> #define ERR_EXIT(m)\ do\ {\ perror(m);\ exit(EXIT_FAILURE);\ }while(0) int main(void) { int sockfds[2]; if(socketpair(PF_UNIX, SOCK_STREAM, 0, sockfds) < 0) ERR_EXIT("socketpair"); pid_t pid; pid = fork(); if (pid == -1) ERR_EXIT("fork"); if (pid > 0){ int val = 0; close(sockfds[1]); while(1){ ++val; printf("sending data: %d\n", val); write(sockfds[0], &val, sizeof(val)); read(sockfds[0], &val, sizeof(val)); printf("data received: %d\n", val); sleep(1); } } else if (pid == 0){ int val; close(sockfds[0]); while(1){ read(sockfds[1], &val, sizeof(val)); ++val; write(sockfds[1], &val, sizeof(val)); } } return 0; } |
运行结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
[root@VM_198_209_centos 22_demon]# ./socketpair sending data: 1 data received: 2 sending data: 3 data received: 4 sending data: 5 data received: 6 sending data: 7 data received: 8 sending data: 9 data received: 10 sending data: 11 data received: 12 sending data: 13 data received: 14 sending data: 15 |
2.sendmsg/recvmsg
- ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
- ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
(1)sendmsg函数
1 2 3 4 |
#include <sys/socket.h> ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags); //返回值:成功返回发送的字节数;出错返回-1 |
sendmsg函数的功能比send函数的功能更加强大,发送到数据由结构体msghdr决定:
1 2 3 4 5 6 7 8 9 10 |
struct msghdr { void *msg_name; //optional address socklen_t msg_namelen; //address size in bytes struct iovec *msg_iov; //array of I/O buffers int msg_iovlen; //number of elements in array void *msg_control; //ancillary data socklen_t msg_controllen; //number of ancillary bytes int msg_flag; //flags for received message ... }; |
msghdr示意图如下所示:
1)其中,msg_name和msg_namelen是地址信息,决定了地址和地址长度,如果不关心的话可以填空指针和0。
2)参数struct iovec *msg_iov是套接字要发送的数据。结构体中包括了缓冲区起始地址iov_base和缓冲区大小iov_len:
1 2 3 4 |
struct iovec { void *iov_base; /*Starting address */ size_t iov_len; /*Number of bytes to transfer */ }; |
并且,指针msg_iov可以指向不止一个缓冲区,可以是多个缓冲区buf1,buf2。这样一来,通过sendmsg就可以发送两块数据给对方。相应地,参数msg_iovlen就变为2。
3)如果要想通过sendmsg传递文件描述字的话,还需要一些辅助信息——即控制信息,它由msg_control来指定(但是,如果我们仅仅是来传递一些普通数据,那么这些控制信息可以不必关心)。
4)指针msg_control也指向一个结构体,该结构体中有4个成员。辅助数据示意图如下所示: