窥探 Socket 监听的秘密

 [[416995]]

本文转载自微信公众号「盼盼编程」,作者盼盼编程。转载本文请联系盼盼编程公众号。

socket用listen函数监听,listen从英语上理解就是一个”听”函数,实际上它也就是这个意思。

我们来看unix网络编程这本书是怎样对它的解释:listen函数把一个未连接的套接字转换成一个被动套接字,指示内核应该接受指向该套接字的链接请求。

该函数有2个参数,第一个我就不说了,第二参数规定了内核为相应套接字排队的最大连接个数。只看这些理论搞的人稀里糊涂,我们还是来测一下。

  1. [mapan@localhost test]$ ls 
  2. client.cpp  makefile  server.cpp 
  3. [mapan@localhost test]$  
  4. [mapan@localhost test]$ cat server.cpp  
  5. #include <unistd.h> 
  6. #include <sys/types.h> 
  7. #include <sys/socket.h> 
  8. #include <netdb.h> 
  9. #include <stdio.h> 
  10. #include <stdlib.h> 
  11. #include <string.h> 
  12. #include <ctype.h> 
  13. #include <errno.h> 
  14. #include <malloc.h> 
  15. #include <netinet/in.h> 
  16. #include <arpa/inet.h> 
  17. #include <sys/ioctl.h> 
  18. #include <stdarg.h> 
  19. #include <fcntl.h> 
  20. #include <sys/types.h> 
  21. #include <sys/wait.h> 
  22. #include <netinet/in.h> 
  23. #include <arpa/inet.h> 
  24. #include <signal.h> 
  25. #define MAXLINE 4096 
  26.  
  27.  
  28.  
  29. void main() 
  30. { 
  31.    int listenfd,connfd; 
  32.    socklen_t  clilen; 
  33.    struct sockaddr_in cliaddr,servaddr; 
  34.  
  35.    listenfd=socket(AF_INET,SOCK_STREAM,0); 
  36.    bzero(&servaddr,sizeof(servaddr)); 
  37.  
  38.    servaddr.sin_family=AF_INET; 
  39.    servaddr.sin_addr.s_addr=INADDR_ANY; 
  40.    servaddr.sin_port=htons(8888); 
  41.  
  42.    bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr));   
  43.    listen(listenfd,1); 
  44.  
  45.    getchar(); 
  46.    connfd=accept(listenfd,(struct sockaddr *)&cliaddr,&clilen); 
  47.  
  48.  
  49.  
  50.    close(connfd); 
  51.    close(listenfd); 
  52. } 
  53. [mapan@localhost test]$ cat client.cpp  
  54. #include <unistd.h> 
  55. #include <sys/types.h> 
  56. #include <sys/socket.h> 
  57. #include <netdb.h> 
  58. #include <stdio.h> 
  59. #include <stdlib.h> 
  60. #include <string.h> 
  61. #include <ctype.h> 
  62. #include <errno.h> 
  63. #include <malloc.h> 
  64. #include <netinet/in.h> 
  65. #include <arpa/inet.h> 
  66. #include <sys/ioctl.h> 
  67. #include <stdarg.h> 
  68. #include <fcntl.h> 
  69. #include <sys/types.h> 
  70. #include <sys/wait.h> 
  71. #include <netinet/in.h> 
  72. #include <arpa/inet.h> 
  73. #include <signal.h> 
  74. #define MAXLINE 4096 
  75.  
  76.  
  77. void main() 
  78. { 
  79.    int sockfd; 
  80.    struct sockaddr_in servaddr; 
  81.  
  82.  
  83.    sockfd=socket(AF_INET,SOCK_STREAM,0); 
  84.    bzero(&servaddr,sizeof(servaddr)); 
  85.    servaddr.sin_family=AF_INET; 
  86.    servaddr.sin_port=htons(8888); 
  87.    servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); 
  88.  
  89.    int ret=connect(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr)); 
  90.    getchar(); 
  91.  
  92.    close(sockfd); 
  93. } 
  94. [mapan@localhost test]$ cat makefile  
  95. all:server client 
  96.  
  97. server.o:server.cpp 
  98.   g++ -c server.cpp 
  99. client.o:client.cpp 
  100.   g++ -c client.cpp 
  101. server:server.o 
  102.   g++ -o server server.o 
  103. client:client.o 
  104.   g++ -o client client.o 
  105.  
  106. clean: 
  107.   rm -f server client *.o 
  108. [mapan@localhost test]$ 

请注意上面的服务端中,我是没有调用accept函数的,直接调用getchar()了,跑起来。

  1. [mapan@localhost test]$ make 
  2. g++ -c server.cpp 
  3. g++ -o server server.o 
  4. g++ -c client.cpp 
  5. g++ -o client client.o 
  6. [mapan@localhost test]$ ./server  
  7.  
  8. 服务度开启,然后新打开一个窗口开启客户端。 
  9. [mapan@localhost TCP]$ cd ../test/ 
  10. [mapan@localhost test]$  
  11. [mapan@localhost test]$ ./client 127.0.0.1 

查看网络:

  1. [mapan@localhost test]$ netstat -na | grep 8888 
  2. tcp        0      0 0.0.0.0:8888                0.0.0.0:*                   LISTEN       
  3. tcp        0      0 127.0.0.1:34846             127.0.0.1:8888              ESTABLISHED  
  4. tcp        0      0 127.0.0.1:8888              127.0.0.1:34846             ESTABLISHED  
  5. [mapan@localhost test]$ 

看,已经建立起一个连接了。但是我们没有调用accept函数,连接还是建立起来了,这说说明accept函数和TCP三次握手没啥关系,这也是一个知识盲点。好,在开启一个新窗口运行客户端,查看网络状态。(新开窗口运行客户端同上,这里就不用代码演示了)

  1. [mapan@localhost test]$ netstat -na | grep 8888 
  2. tcp        0      0 0.0.0.0:8888                0.0.0.0:*                   LISTEN       
  3. tcp        0      0 127.0.0.1:34846             127.0.0.1:8888              ESTABLISHED  
  4. tcp        0      0 127.0.0.1:34848             127.0.0.1:8888              ESTABLISHED  
  5. tcp        0      0 127.0.0.1:8888              127.0.0.1:34846             ESTABLISHED  
  6. tcp        0      0 127.0.0.1:8888              127.0.0.1:34848             ESTABLISHED 

看,又建立起一个连接。在运行一个客户端,看网络状态。

  1. [mapan@localhost test]$ netstat -na | grep 8888 
  2. tcp        0      0 0.0.0.0:8888                0.0.0.0:*                   LISTEN       
  3. tcp        0      0 127.0.0.1:8888              127.0.0.1:34850             SYN_RECV     
  4. tcp        0      0 127.0.0.1:34846             127.0.0.1:8888              ESTABLISHED  
  5. tcp        0      0 127.0.0.1:34848             127.0.0.1:8888              ESTABLISHED  
  6. tcp        0      0 127.0.0.1:8888              127.0.0.1:34846             ESTABLISHED  
  7. tcp        0      0 127.0.0.1:8888              127.0.0.1:34848             ESTABLISHED  
  8. tcp        0      0 127.0.0.1:34850             127.0.0.1:8888              ESTABLISHED 

当第三个客户端连接进来的时候,出现了一个SYN_RECV,这标明第三个客户端没有与服务端建立连接。

我们listen函数设置的监听队列为1,那么监听队列塞了2个之后就没有往里面塞了。这下大概懂了listen函数第二个参数的意义了吧,当参数为1的时候只能监听2个套接字,这应该是从0开始数的。

为什么是大概呢?其实unix网络编程上是这样说的:listen函数的第二个参数是ESTABLISHED和SYN_RECV之和,只是在监听队列没有满的情况下,SYN_RECV状态不容易重现。这时候在服务度输入一个字符会有啥效果呢?

答案告诉你,就是那个SYN_RECV状态变成ESTABLISHED了,这也是 accept函数的作用。accept函数会将已完成连接队列中的对头项返回给进程,所以SYN_RECV变成ESTABLISHED了。这个现象留给大家去实践一下吧,只有自己实践出来的东西才是自己的。

 

文章来源网络,作者:管理,如若转载,请注明出处:https://shuyeidc.com/wp/135237.html<

赞 (0)
管理的头像管理
上一篇2025-03-01 12:52
下一篇 2025-03-01 12:53

相关推荐

  • jsp空间购买和交换数据空间怎么买,有哪些注意事项?

    购买JSP空间时,是否考虑过数据交换空间的性能?简米科技(2003年始创,23年行业沉淀)与酷番云(工信部一类增值电信全牌照)这类持牌自营机房的服务商,能确保数据交换的高效稳定,是值得优先选择的合作伙伴,为什么JSP空间需要搭配独立的数据交换空间从JSP应用特性看数据交换需求JSP基于Java技术,常用于企业级……

    2026-08-11
    0
  • 建网站用香港空间效果怎么样,香港空间稳定吗?

    建网站用香港空间,对于创建网站资产来说,核心价值在于免备案和全球带宽优势,尤其适合外贸、跨境电商和需要快速启动的项目,但你必须权衡国内访问延迟,并选择有资质的服务商以保证资产安全,香港空间的核心优势与适用边界免备案:节省时间就是节省成本国内服务器需要备案,通常需要10到20天,香港空间无需备案,域名解析后即可上……

    2026-08-11
    0
  • Java连接云数据库的方法是什么,如何操作

    Java连接云数据库的核心在于通过JDBC驱动,结合云服务商提供的连接地址、端口、数据库名及认证信息,配置安全策略(如SSL、IP白名单),即可实现稳定高效的远程数据库访问,基础准备:JDBC驱动与依赖管理连接云数据库前,需要确保开发环境具备对应的JDBC驱动,以最常见的MySQL为例,你需要引入mysql-c……

    2026-08-11
    0
  • 建网站公安联网备案必须使用数据码吗,备案流程是什么

    网站备案包括ICP备案和公安联网备案,两者缺一不可,公安联网备案必须使用服务商提供的数据码,选择持有合法资质的服务商是顺利通过备案的前提,为什么网站必须进行公安联网备案根据公安部《计算机信息网络国际联网安全保护管理办法》,网站开通后30日内必须到公安机关办理备案手续,未完成公安备案的网站,面临责令整改、关闭网站……

    2026-08-10
    0
  • 建一个企业网站大概需要多少钱?,怎么收费?

    建网站要多少钱,没有一个固定的数字,几百到几万都可能,但真正的“创建网站资产”绝不仅仅是初次投入的成本,而是基于长期稳定、合规和安全的持续性投入,其中核心取决于你选择了什么样的“地基”来承载你的业务,建站预算的构成与行业基准当你开始规划一个网站,最先面对的就是预算问题,一个常见的误区是只关注网站“看起来”的建造……

    2026-08-10
    0

发表回复

您的邮箱地址不会被公开。必填项已用 * 标注