socketpair原理_socket方法

socketpair原理_socket方法今天跟人谈到socketpair的问题,晚上回来写了个程序验证下自己的猜测!   先说说我的理解:socketpair创建了一对无名的套接字描述符(只能在AF_UNIX域中使用),描述符存储于一个二元数组,eg.s[2].这对套接字可以进行双工通信,每一个描述符既可以读也可以写。这个在同一个进程中也可以进行通信,向s[0]中写入,就可以从s[1]中读取(只能从s[1]中读取),也可以在

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE稳定放心使用
   今天跟人谈到socketpair的问题,晚上回来写了个程序验证下自己的猜测!

     先说说我的理解:socketpair创建了一对无名的套接字描述符(只能在AF_UNIX域中使用),描述符存储于一个二元数组,eg. s[2] .这对套接字可以进行双工通信,每一个描述符既可以读也可以写。这个在同一个进程中也可以进行通信,向s[0]中写入,就可以从s[1]中读取(只能从s[1]中读取),也可以在s[1]中写入,然后从s[0]中读取;但是,若没有在0端写入,而从1端读取,则1端的读取操作会阻塞,即使在1端写入,也不能从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.  
  10. #define BUF_SIZE 30 
  11.  
  12. int main(){ 
  13.         int s[2]; 
  14.         int w,r; 
  15.         char * string = "This is a test string"
  16.         char * buf = (char*)calloc(1 , BUF_SIZE); 
  17.  
  18.         if( socketpair(AF_UNIX,SOCK_STREAM,0,s) == -1 ){ 
  19.                 printf("create unnamed socket pair failed:%s\n",strerror(errno) ); 
  20.                 exit(-1); 
  21.         } 
  22.  
  23.         /*******test in a single process ********/ 
  24.         if( ( w = write(s[0] , string , strlen(string) ) ) == -1 ){ 
  25.                 printf("Write socket error:%s\n",strerror(errno)); 
  26.                 exit(-1); 
  27.         } 
  28.         /*****read*******/ 
  29.         if( (r = read(s[1], buf , BUF_SIZE )) == -1){ 
  30.                 printf("Read from socket error:%s\n",strerror(errno) ); 
  31.                 exit(-1); 
  32.         } 
  33.         printf("Read string in same process : %s \n",buf); 
  34.           if( (r = read(s[0], buf , BUF_SIZE )) == -1){ 
  35.                           printf("Read from socket s0 error:%s\n",strerror(errno) ); 
  36.                                           exit(-1); 
  37.                                                   } 
  38.                                                   printf("Read from s0 :%s\n",buf); 
  39.  
  40.         printf("Test successed\n"); 
  41.         exit(0); 

 

     若fork子进程,然后在服进程关闭一个描述符eg. s[1] ,在子进程中再关闭另一个 eg. s[0]    ,则可以实现父子进程之间的双工通信,两端都可读可写;当然,仍然遵守和在同一个进程之间工作的原则,一端写,在另一端读取;

     这和pipe有一定的区别,pipe是单工通信,一端要么是读端要么是写端,而socketpair实现了双工套接字,也就没有所谓的读端和写端的区分

验证代码:

 
 
 
  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.  
  10. #define BUF_SIZE 30 
  11.  
  12. int main(){ 
  13.         int s[2]; 
  14.         int w,r; 
  15.         char * string = "This is a test string"
  16.         char * buf = (char*)calloc(1 , BUF_SIZE); 
  17.         pid_t pid; 
  18.  
  19.         if( socketpair(AF_UNIX,SOCK_STREAM,0,s) == -1 ){ 
  20.                 printf("create unnamed socket pair failed:%s\n",strerror(errno) ); 
  21.                 exit(-1); 
  22.         } 
  23.  
  24.         /***********Test : fork but don't close any fd in neither parent nor child process***********/ 
  25.         if( ( pid = fork() ) > 0 ){ 
  26.                 printf("Parent process's pid is %d\n",getpid()); 
  27.              close(s[1]); 
  28.                 if( ( w = write(s[0] , string , strlen(string) ) ) == -1 ){ 
  29.                         printf("Write socket error:%s\n",strerror(errno)); 
  30.                         exit(-1); 
  31.                 } 
  32.         }else if(pid == 0){ 
  33.                 printf("Fork child process successed\n"); 
  34.                 printf("Child process's pid is :%d\n",getpid()); 
  35.                 close(s[0]); 
  36.         }else
  37.                 printf("Fork failed:%s\n",strerror(errno)); 
  38.                 exit(-1); 
  39.         } 
  40.  
  41.         /*****read***In parent and child****/ 
  42.         if( (r = read(s[1], buf , BUF_SIZE )) == -1){ 
  43.                 printf("Pid %d read from socket error:%s\n",getpid() , strerror(errno) ); 
  44.                 exit(-1); 
  45.         } 
  46.         printf("Pid %d read string in same process : %s \n",getpid(),buf); 
  47.         printf("Test successed , %d\n",getpid()); 
  48.         exit(0); 

以上代码中在父子进程之间各关闭了一个描述符,则在父进程写可从子进程读取,反之若子进程写,父进程同样可以读取;大家可以验证下

另外,我也测试了在父子进程中都不close(s[1]),也就是保持两个读端,则父进程能够读到string串,但子进程读取空串,或者子进程先读了数据,父进程阻塞于read操作!

 

之所以子进程能读取父进程的string,是因为fork时,子进程继承了父进程的文件描述符的,同时也就得到了一个和父进程指向相同文件表项的指针;若父子进程均不关闭读端,因为指向相同的文件表项,这两个进程就有了竞争关系,争相读取这个字符串.父进程read后将数据转到其应用缓冲区,而子进程就得不到了,只有一份数据拷贝(若将父进程阻塞一段时间,则收到数据的就是子进程了,已经得到验证,让父进程sleep(3),子进程获得string,而父进程获取不到而是阻塞)

有网友"笨笨"回复:

“若将父进程阻塞一段时间,则收到数据的就是子进程了,已经得到验证,让父进程sleep(3),子进程获得string,而父进程获取不到”

我验证的情况是,父进程一直阻塞在read上。我想不明白,为什么这时候父进程不能读取数据呢。
而上一种情况,父进程先读取数据,子进程仍然可以读取数据(数据为空),但子进程不会阻塞在read上。

关于这个问题,解释如下:

1.该网友说的情况的确存在,如果先让子进程sleep,此时父进程获得数据,子进程被唤醒之后读到EOF返回;若是让父进程sleep先,子进程先获取数据,之后父进程被唤醒却是一直阻塞不能返回.按理来说这两种情况应该没差别,这个区别下文描述.

2.对于网友提到问题的这个测试,我最初的目的是想说明如果通过产生子进程的方式,对一个写端同时有多个读端,这这些读端之间相互竞争.我们可以用个更有说服力的测试方法来看出这个问题.原来的测试是让一个进程sleep然后另一个进程读完所有字符,可以看到之后醒来的进程就读不到任何字符了.更好的方法是先有一个进程读取一部分的字符,然后第二个进程被唤醒,会发现这第二个进程还能读到一些字符,而这些字符是第一个进程读完剩下的.

3.第一条中的遗留问题,为什么这两种情况有不同的表现.

  原因是:如果子进程先sleep,父进程读取完数据之后,父进程退出,此时写端s[0]的引用计数变为0(之前子进程已主动close了一次),被系统释放,根据read的语义,当子进程被唤醒后会读取到EOF;但是当我们先让父进程sleep的时候,子进程读取完后退出,由于写端在父进程,没有被释放,所以父进程此时阻塞在读操作上.

  用另外一个测试来证明,我们在子进程中不主动执行close[0],也就是有两个写端,然后其他不变,子进程先sleep,父进程先读取到数据然后退出,但此时更刚刚有个区别,父进程退出的时候s[0]这个写端的描述符并不会减到0,因为子进程中还持有一个引用,所以写端健在,子进程被唤醒之后不会读到EOF返回,而是阻塞在读操作上

 

最后,有关socketpair在内核中实现的一点点描述:

socketpair会创建两个描述符,但改描述符不属于任何的实际文件系统,而是网络文件系统,虚拟的.同时内核会将这两个描述符彼此设为自己的peer即对端(这里即解决了如何标识读写端,可以想象,两个描述符互为读写缓冲区,即解决了这个问题).然后应用相应socket家族里的read/write函数执行读写操作.

有了这个基础,即可明白为什么试用fork产生的两个子进程都不关闭读端的时候会竞争,如上所述,他们共享相同的文件表项,有相同的inode和偏移量,两个进程的操作当然是相互影响的.

本文出自 “流离and逍遥” 博客,请务必保留此出处http://liulixiaoyao.blog.51cto.com/1361095/533469

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/181964.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 初识js中的闭包_Js闭包中变量理解

    初识js中的闭包_Js闭包中变量理解今天看了关于js闭包方面的文章,还是有些云里雾里,对于一个菜鸟来说,学习闭包确实有一定的难度,不说别的,能够在网上找到一篇优秀的是那样的不易。当然之所以闭包难理解,个人觉得是基础知识掌握的不牢,因为

  • 面试官,不要再问我“Java虚拟机类加载机制”了

    面试官,不要再问我“Java虚拟机类加载机制”了关于Java虚拟机类加载机制往往有两方面的面试题:根据程序判断输出结果和讲讲虚拟机类加载机制的流程。其实这两类题本质上都是考察面试者对Java虚拟机类加载机制的了解。面试题试水现在有这样一道判断程序输出结果的面试题,先看看打印的结果是什么?publicclassSuperClass{static{System.out.println(“SuperCla…

  • pycharm运行py文件报错_项目怎么运行

    pycharm运行py文件报错_项目怎么运行换电脑和换了Python解释器后,Pycharm运行项目报错:CreateProcesserror=2,系统找不到指定的文件运行其它都可以正常,唯独运行main函数报错file->setting设置如下此时需要Run->Run,修改成与上图一致的…

  • oj在计算机领域中指什么,【计算机专业论文】计算机专业教学中OJ平台的应用(共2762字)…

    oj在计算机领域中指什么,【计算机专业论文】计算机专业教学中OJ平台的应用(共2762字)…摘要:传统的教学模式对计算机专业学生的能力培养存在着诸多问题,而OJ(OnlineJudge在线检测程序源代码)平台为计算机教学提供了新的思路,因为OJ平台在学生日常训练方面有一套行之有效的机制,所以对学生的学习兴趣、分析解决问题能力、创新能力等方面的培养都起到了积极的推动作用,OJ平台还可以对学生实践能力进行最直接的考核,因此将OJ平台引入计算机专业教学,可实现以平台促教学,以平台促教改。关键词…

  • python中如何输入派_用phython语言求圆周率

    python中如何输入派_用phython语言求圆周率关于完全弹性碰撞和圆周率的关系所设计的易语言程序.版本2.程序集窗口程序集_启动窗口.程序集变量n,整数型.程序集变量m1,整数型.程序集变量m2,整数型.程序集变量v1,小数型.程序集变量v2,小数型.程序集变量v11,小数型.程序集变量v22,小数型.子程序_按钮1_被单击n=0m1=到数值(编辑框1.内容)m2=1v1=-1v2=0.判…

  • java用什么软件_Java编程什么软件最好用?

    java用什么软件_Java编程什么软件最好用?原标题:Java编程什么软件最好用?“工欲善其事必先利其器”,想要学好Java编程开发,除了要有好的学习资源之外,还要有一套适合自己的Java编程软件,好的编程软件能极大提高你的学习和工作效率。那么,Java编程软件有哪些?Java编程用什么软件好呢?下面整理了一些华清Java学院为大家推荐的Java编程软件。Java编程软件有哪些?1、JDKJDK是java初学时用的,比较麻烦,建议开始学习J…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号