socketpair的用法和理解

转载:https://blog.csdn.net/weixin_40039738/article/details/81095013?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522158918635519725219904734%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=158918635519725219904734&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2blogfirst_rank_v2~rank_v25-1-81095013.nonecase&utm_term=socketpair%E7%94%A8%E6%B3%95%E5%92%8C%E7%90%86%E8%A7%A3

socketpair()函数的声明:


 
 
  1. #include <sys/types.h>
  2. #include <sys/socket.h>
  3. int socketpair( int d, int type, int protocol, int sv[ 2]);

socketpair()函数用于创建一对无名的、相互连接的套接子。 
如果函数成功,则返回0,创建好的套接字分别是sv[0]和sv[1];否则返回-1,错误码保存于errno中。

基本用法: 
1. 这对套接字可以用于全双工通信,每一个套接字既可以读也可以写。例如,可以往sv[0]中写,从sv[1]中读;或者从sv[1]中写,从sv[0]中读; 
2. 如果往一个套接字(如sv[0])中写入后,再从该套接字读时会阻塞,只能在另一个套接字中(sv[1])上读成功; 
3. 读、写操作可以位于同一个进程,也可以分别位于不同的进程,如父子进程。如果是父子进程时,一般会功能分离,一个进程用来读,一个用来写。因为文件描述副sv[0]和sv[1]是进程共享的,所以读的进程要关闭写描述符, 反之,写的进程关闭读描述符。 
举例: 
一、读写操作位于同一进程


 
 
  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <unistd.h>
  4. #include <sys/types.h>
  5. #include <error.h>
  6. #include <errno.h>
  7. #include <sys/socket.h>
  8. #include <stdlib.h>
  9. const char* str = "SOCKET PAIR TEST.";
  10. int main(int argc, char* argv[]){
  11. char buf[ 128] = { 0};
  12. int socket_pair[ 2];
  13. pid_t pid;
  14. if(socketpair(AF_UNIX, SOCK_STREAM, 0, socket_pair) == -1 ) {
  15. printf( "Error, socketpair create failed, errno(%d): %s\n", errno, strerror(errno));
  16. return EXIT_FAILURE;
  17. }
  18. int size = write(socket_pair[ 0], str, strlen(str));
  19. //可以读取成功;
  20. read(socket_pair[ 1], buf, size);
  21. printf( "Read result: %s\n",buf);
  22. return EXIT_SUCCESS;
  23. }

二、读写操作位于不同进程


 
 
  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <unistd.h>
  4. #include <sys/types.h>
  5. #include <error.h>
  6. #include <errno.h>
  7. #include <sys/socket.h>
  8. #include <stdlib.h>
  9. const char* str = "SOCKET PAIR TEST.";
  10. int main(int argc, char* argv[]){
  11. char buf[ 128] = { 0};
  12. int socket_pair[ 2];
  13. pid_t pid;
  14. if(socketpair(AF_UNIX, SOCK_STREAM, 0, socket_pair) == -1 ) {
  15. printf( "Error, socketpair create failed, errno(%d): %s\n", errno, strerror(errno));
  16. return EXIT_FAILURE;
  17. }
  18. pid = fork();
  19. if(pid < 0) {
  20. printf( "Error, fork failed, errno(%d): %s\n", errno, strerror(errno));
  21. return EXIT_FAILURE;
  22. } else if(pid > 0) {
  23. //关闭另外一个套接字
  24. close(socket_pair[ 1]);
  25. int size = write(socket_pair[ 0], str, strlen(str));
  26. printf( "Write success, pid: %d\n", getpid());
  27. } else if(pid == 0) {
  28. //关闭另外一个套接字
  29. close(socket_pair[ 0]);
  30. read(socket_pair[ 1], buf, sizeof(buf));
  31. printf( "Read result: %s, pid: %d\n",buf, getpid());
  32. }
  33. for(;;) {
  34. sleep( 1);
  35. }
  36. return EXIT_SUCCESS;
  37. }

sendmsg, recvmsg , send函数的使用

sendmsg, recvmsg , send三个函数的头文件:


 
 
  1. #include <sys/types.h>
  2. #include <sys/socket.h>

sendmsg函数 
定义函数

          int sendmsg(int s, const strcut msghdr *msg, unsigned int flags);

函数说明:sendmsg()用来将数据由指定的socket传给对方主机. 
参数s:为已建立好连线的socket, 如果利用UDP协议则不需经过连线操作. 
参数msg:指向欲连线的数据结构内容, 参数flags 一般默认为0, 详细描述请参考send(). 
返回值:成功返回发送的字节数,出错返回-1

recvmsg函数 
定义函数

int recvmsg(int s, struct msghdr *msg, unsigned int flags);
 
 

函数说明:recvmsg()用来接收远程主机经指定的socket 传来的数据. 
参数s 为已建立好连线的socket, 如果利用UDP 协议则不需经过连线操作. 
参数msg 指向欲连线的数据结构内容, 
参数flags 一般设0, 详细描述请参考send(). 
返回值:成功则返回接收到的字符数, 失败则返回-1, 错误原因存于errno 中.

send函数 
定义函数:int send(int s, const void * msg, int len, unsigned int falgs); 
函数说明:send()用来将数据由指定的socket 传给对方主机. 
参数s 为已建立好连接的socket. 
参数msg 指向欲连线的数据内容. 
参数len 则为数据长度. 
参数flags 一般设0, 其他数值定义如下: 
MSG_OOB 传送的数据以out-of-band 送出. 
MSG_DONTROUTE 取消路由表查询 
MSG_DONTWAIT 设置为不可阻断运作 
MSG_NOSIGNAL 此动作不愿被SIGPIPE 信号中断. 
返回值:成功则返回实际传送出去的字符数, 失败返回-1. 错误原因存于errno.


结构msghdr定义如下:


 
 
  1. struct msghdr
  2. {
  3. void *msg_name; //发送或接收数据的地址
  4. socklen_t msg_namelen; //地址长度
  5. strcut iovec * msg_iov; //要发送或接受数据
  6. size_t msg_iovlen; //容器数据长度
  7. void * msg_control; //附属数据
  8. size_t msg_controllen; //附属数据长度
  9. int msg_flags; //接收消息的标志
  10. };

返回值:成功则返回实际传送出去的字符数, 失败返回-1, 错误原因存于errno 
错误代码:


 
 
  1. 1、EBADF 参数 s 非合法的 socket 处理代码.
  2. 2、EFAULT 参数中有一指针指向无法存取的内存空间
  3. 3、ENOTSOCK 参数 s 为一文件描述词, 非socket.
  4. 4、EINTR 被信号所中断.
  5. 5、EAGAIN 此操作会令进程阻断, 但参数 ssocket 为不可阻断.
  6. 6、ENOBUFS 系统的缓冲内存不足
  7. 7、ENOMEM 核心内存不足 EINVAL 传给系统调用的参数不正确.

附属数据msg_control结构 
控制信息头部本身由下面的C结构定义:


 
 
  1. struct cmsghdr {
  2. socklen_t cmsg_len;
  3. int cmsg_level;
  4. int cmsg_type;
  5. /* u_char cmsg_data[]; */
  6. };

其成员描述如下:


 
 
  1. 成员 描述
  2. cmsg_len 附属数据的字节计数,这包含结构头的尺寸。这个值是由 CMSG_LEN()宏计算的。
  3. cmsg_level 这个值表明了原始的协议级别(例如,SOL_SOCKET)。
  4. cmsg_type 这个值表明了控制信息类型(例如,SCM_RIGHTS)。
  5. cmsg_data 这个成员并不实际存在,用来指明实际的额外附属数据所在的位置。

用sendmsg来传递数据程序实例


 
 
  1. /*sendmsg.c*/
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <unistd.h>
  5. #include <errno.h>
  6. #include <string.h>
  7. #include <sys/types.h>
  8. #include <sys/socket.h>
  9. int main(int argc,char *argv[])
  10. {
  11. int ret; /* 返回值 */
  12. int sock[ 2]; /* 套接字对 */
  13. struct msghdr msg;
  14. struct iovec iov[1];
  15. char send_buf[ 100] = "it is a test";
  16. struct msghdr msgr;
  17. struct iovec iovr[1];
  18. char recv_buf[ 100];
  19. /* 创建套接字对 */
  20. ret = socketpair(AF_LOCAL,SOCK_STREAM, 0,sock);
  21. if(ret == -1){
  22. printf( "socketpair err\n");
  23. return 1;
  24. }
  25. /* sock[1]发送数据到本地主机 */
  26. bzero(&msg, sizeof(msg));
  27. msg.msg_name = NULL; /* void*类型 NULL本地地址*/
  28. msg.msg_namelen = 0;
  29. iov[ 0].iov_base = send_buf;
  30. iov[ 0].iov_len = sizeof(send_buf);
  31. msg.msg_iov = iov; //要发送或接受数据设为iov
  32. msg.msg_iovlen = 1; //1个元素
  33. printf( "开始发送数据:\n");
  34. printf( "发送的数据为: %s\n", send_buf);
  35. ret = sendmsg(sock[ 1], &msg, 0 );
  36. if(ret == -1 ){
  37. printf( "sendmsg err\n");
  38. return -1;
  39. }
  40. printf( "发送成功!\n");
  41. /* 通过sock[0]接收发送过来的数据 */
  42. bzero(&msg, sizeof(msg));
  43. msgr.msg_name = NULL;
  44. msgr.msg_namelen = 0;
  45. iovr[ 0].iov_base = &recv_buf;
  46. iovr[ 0].iov_len = sizeof(recv_buf);
  47. msgr.msg_iov = iovr;
  48. msgr.msg_iovlen = 1;
  49. ret = recvmsg(sock[ 0], &msgr, 0);
  50. if(ret == -1 ){
  51. printf( "recvmsg err\n");
  52. return -1;
  53. }
  54. printf( "接收成功!\n");
  55. printf( "收到数据为: %s\n", recv_buf);
  56. /* 关闭sockets */
  57. close(sock[ 0]);
  58. close(sock[ 1]);
  59. return 0;
  60. }

执行程序结果:


 
 
  1. yu@ubuntu :~/Linux/ 217/pro_pool/socketpair$ gcc -o sendmsg sendmsg.c
  2. yu@ubuntu :~/Linux/ 217/pro_pool/socketpair$ ./sendmsg
  3. 开始发送数据:
  4. 发送的数据为: it is a test
  5. 发送成功!
  6. 接收成功!
  7. 收到数据为: it is a test

程序分析:由套接字sock[1]发数据到本地主机,由套接字sock[0]接收发送过来的数据。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值