下面我们用最简单的一对一的客户server编程模型重现遇到的一些问题:

初学者socket当写作socket名其妙的问题。比方说bind函数返回的常见错误是EADDRINUSE

使用以下的程序重现这个状态:

client:

  1. int main(int argc, const char * argv[])
  2. {
  3.  
  4. struct sockaddr_in serverAdd;
  5.  
  6. bzero(&serverAdd, sizeof(serverAdd));
  7. serverAdd.sin_family = AF_INET;
  8. serverAdd.sin_addr.s_addr = inet_addr(SERV_ADDR);
  9. serverAdd.sin_port = htons(SERV_PORT);
  10.  
  11. int connfd = socket(AF_INET, SOCK_STREAM, 0);
  12.  
  13. int connResult = connect(connfd, (struct sockaddr *)&serverAdd, sizeof(serverAdd));
  14. if (connResult < 0) {
  15. printf("连接失败\n");
  16. close(connfd);
  17. return -1;
  18. }
  19.  
  20. ssize_t writeLen;
  21. ssize_t readLen;
  22. char recvMsg[65535] = {0};
  23. char sendMsg[20] = "I am client";
  24.  
  25. writeLen = write(connfd, sendMsg, sizeof(sendMsg));
  26. if (writeLen < 0) {
  27. printf("发送失败\n");
  28. close(connfd);
  29. return -1;
  30. }
  31. else
  32. {
  33. printf("发送成功\n");
  34. }
  35.  
  36. while (1) {
  37.  
  38. // sleep(1);
  39.  
  40. readLen = read(connfd, recvMsg, sizeof(recvMsg));
  41. if (readLen < 0) {
  42. printf("读取失败\n");
  43. close(connfd);
  44. return -1;
  45. }
  46. if (readLen == 0) {
  47. printf("服务器关闭\n");
  48. close(connfd);
  49. return -1;
  50. }
  51.  
  52. printf("server said:%s\n",recvMsg);
  53.  
  54. }
  55.  
  56. close(connfd);
  57. return 0;
  58. }

server:

  1. int main(int argc, const char * argv[])
  2. {
  3.  
  4. struct sockaddr_in serverAdd;
  5. struct sockaddr_in clientAdd;
  6.  
  7. bzero(&serverAdd, sizeof(serverAdd));
  8. serverAdd.sin_family = AF_INET;
  9. serverAdd.sin_addr.s_addr = htonl(INADDR_ANY);
  10. serverAdd.sin_port = htons(SERV_PORT);
  11.  
  12. socklen_t clientAddrLen;
  13.  
  14. int listenfd = socket(AF_INET, SOCK_STREAM, 0);
  15.  
  16. if (listenfd < 0) {
  17. printf("创建socket失败\n");
  18. close(listenfd);
  19. return -1;
  20. }
  21.  
  22. int bindResult = bind(listenfd, (struct sockaddr *)&serverAdd, sizeof(serverAdd));
  23. if (bindResult < 0) {
  24. close(listenfd);
  25. printf("绑定port失败,errno = %d\n",errno);
  26. return -1;
  27. }
  28. else
  29. {
  30. printf("绑定port成功\n");
  31. }
  32.  
  33. listen(listenfd, 20);
  34.  
  35. int connfd;
  36. unsigned char recvMsg[65535];
  37. char replyMsg[20] = "I am server";
  38.  
  39. clientAddrLen = sizeof(clientAdd);
  40. connfd = accept(listenfd,(struct sockaddr *)&clientAdd,&clientAddrLen);
  41. if (connfd < 0) {
  42. close(listenfd);
  43. printf("连接失败\n");
  44. return -1;
  45. }
  46. else
  47. {
  48. printf("连接成功\n");
  49. }
  50.  
  51. ssize_t readLen = read(connfd, recvMsg, sizeof(recvMsg));
  52. printf("readLen:%ld\n",readLen);
  53. if (readLen < 0) {
  54. printf("读取失败\n");
  55. return -1;
  56. }
  57. else if (readLen == 0) {
  58. printf("读取完毕\n");
  59. close(listenfd);
  60. return 0;
  61. }
  62.  
  63. printf("client said:%s\n",recvMsg);
  64.  
  65. while (1)
  66. {
  67. write(connfd, replyMsg, sizeof(replyMsg));
  68. }
  69.  
  70. close(connfd);
  71.  
  72. return 0;
  73. }

首先执行server程序,再执行client。然后关闭服务端后立刻再打开服务端,就会打印例如以下信息:48相应EADDRINUSE错误码

绑定port失败,errno
48

这里要说明一个问题:

当一个Unix进程不管自愿的(调用exit或者从main函数返回)还是非自愿的(收到一个终止本进程的信号)终止时,全部打开的描写叙述符都被关闭,这也将导致仍然打开的不论什么TCP连接上发出一个FIN。

非常明显server已经关闭了。为什么会绑定port失败呢。以下是TCP连接终止的四个分节:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvanVuanVuMTUwMDEzNjUy/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">

某些情况下第一个分节的FIN随数据一起发送。另外,第二个和第三个分节有可能被合并成一个分节。

这里我们的server是主动关闭的一端。当主动发送FIN分节以后等待确认,状态变为FIN_WAIT_1,收到确认以后状态变为FIN_WAIT_2,收到client的FIN分节以后状态变为TIME_WAIT状态。这里在TIME_WAIT状态会停留2MSL后才会进入CLOSED状态。所以我们再立刻启动server的时候,之前的连接还没有处于CLOSED状态,还存在者,所以就会绑定失败了;后面会讲到为什么会存在TIME_WAIT状态;

这里client是被动关闭的一端,收到服务端的FIN之后状态进入CLOSE_WAIT,这个时候read方法会返回0,然后发送对第一个分节的确认。此时client调用close方法发送FIN分节给服务端进入LAST_ACK状态,等待确认到达。收到确认以后连接状态变为CLOSED;

TIME_WAIT状态:停留在该状态的持续时间是最长分节生命期(maximum segment lifetime,MSL)的两倍。有时候称为2MSL。MSL是不论什么IP数据报可以在因特网中存活的最长时间,最大值为255。这是一个跳数限制而不是真正的时间限制。

TIME_WAIT状态存在理由:

可靠地实现TCP全双工连接的终止:可能不得不重传终于那个ack,TIME_WAIT后是CLOSED,假设没有TIME_WAIT的2MSL,直接CLOSED,那么假设最后一个ACK丢失了,是不会又一次再发送ACK的,那服务端收不到ACK就会又一次发送终于那个FIN,这个时候client已经是CLOSED了,就会响应一个RST,这个RST就会被server解释成一个错误。

同意来的反复分节在网络中消逝:保证每成功建立一个TCP连接时。来自该连接先前化身的老的反复分组都已经在网络中消逝了。从而不会被误解成新连接的分组。

这里另一种情况:打开clientwhile里面的sleep,(或者屏蔽掉client以下的代码)然后再先执行server程序。再执行client,然后关闭服务端后立刻再打开服务端,仍然会绑定失败,此时的状态和之前的有点不一样

  1. if (readLen == 0) {
  2. printf("server关闭\n");
  3. close(connfd);
  4. return -1;
  5. }

我们从终端信息打印例如以下,此时server处于FIN_WAIT_2状态,就如上面说的由于client还没有关闭连接,没有发送第三个FIN分节,此时client由于已经收到来自服务端的FIN分节而处于CLOSE_WAIT状态;

wanglijuntekiMac-mini:~ wanglijun$ netstat -an |grep 8000

tcp4       0      0  192.168.1.103.8000     192.168.1.103.49632    FIN_WAIT_2

tcp4  290960      0  192.168.1.103.49632    192.168.1.103.8000     CLOSE_WAIT


解决方法:

这里有一个SO_REUSEADDR套接字选项,打开之后就能解决如上的问题,我们在band之前加入例如以下设置代码:

  1. <span style="font-size:12px;">int yes = 1;
  2. setsockopt(listenfd,
  3. SOL_SOCKET, SO_REUSEADDR,
  4. (void *)&yes, sizeof(yes));</span>

server重新启动监听时,试图捆绑现有连接上的port会失败,(另一种情况可能之前派生出来的子进程还处理着连接)假设设置了SO_REUSEADDR套接字选项。就会bind成功,全部的TCPserver都应该指定SO_REUSEADDR套接字选项,以同意server在这样的情形下被又一次启动;SO_REUSEADDR同意在同一port上启动同一server的多个实例,仅仅要每一个实例捆绑一个不同的本地IP地址就可以。

对于TCP,我们绝不可能启动捆绑同样IP地址和同样port号的多个server。

參考:

《UNIX Network ProgrammingVolume 1, Third Edition: TheSockets Networking API》

版权声明:本文博客原创文章,博客,未经同意,不得转载。

网络编程Socket它TCP它TIME_WAIT国家具体解释的更多相关文章

  1. 网络编程Socket之TCP之close/shutdown具体解释(续)

    接着上一篇网络编程Socket之TCP之close/shutdown具体解释 如今我们看看对于不同情况的close的返回情况和可能遇到的一些问题: 1.默认操作的close 说明:我们已经知道writ ...

  2. java网络编程socket\server\TCP笔记(转)

    java网络编程socket\server\TCP笔记(转) 2012-12-14 08:30:04|  分类: Socket |  标签:java  |举报|字号 订阅     1 TCP的开销 a ...

  3. python网络编程(Socket、TCP、UDP)

    Socket 是网络编程的一个抽象概念,通常我们用一个Socket表示 "打开了一个网络链接",而打开一个Socket 需要知道目标计算机的IP 地址和端口号,再指定协议类型即可. ...

  4. 网络编程Socket之TCP

            服务端: 1. 创建 ServerSocket 对象并监听一个端口 2. 调用accept()方法等待客户端的连接(阻塞式) 3. 输入流(记取客户端发送过来的数据) 4. 输出流(响 ...

  5. IPv6下网络编程socket, TCP和UDP例子,以及兼容IPV4和IPV6的类

    一.TCP socket ipv6与ipv4的区别 服务器端源代码如下: #include <stdio.h> #include <stdlib.h> #include < ...

  6. 二、网络编程-socket之TCP协议开发客户端和服务端通信

    知识点:之前讲的udp协议传输数据是不安全的,不可靠不稳定的,tcp协议传输数据安全可靠,因为它们的通讯机制是不一样的.udp是用户数据报传输,也就是直接丢一个数据包给另外一个程序,就好比寄信给别人, ...

  7. 网络编程Socket之TCP之connect具体解释

    对TCP套接字调用connect会激发三次握手,例如以下: client是主动打开连接的一端,会发送第一个SYN分节,然后等待确认,此时连接状态为SYN_SENT,当收到服务端的确认后连接建立,状态变 ...

  8. python_网络编程socket(TCP)

    服务端: import socket sk = socket.socket() #创建对象 sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) ...

  9. Python网络编程02 /基于TCP、UDP协议的socket简单的通信、字符串转bytes类型

    Python网络编程02 /基于TCP.UDP协议的socket简单的通信.字符串转bytes类型 目录 Python网络编程02 /基于TCP.UDP协议的socket简单的通信.字符串转bytes ...

随机推荐

  1. mac系统创建.开头文件.htaccess

    thinkphp5 隐藏index.php的时候需要用的.htaccess文件,但是mac默认不让创建这种文件 感谢 https://blog.csdn.net/gyz413977349/articl ...

  2. (八)RabbitMQ消息队列-通过Topic主题模式分发消息

    原文:(八)RabbitMQ消息队列-通过Topic主题模式分发消息 前两章我们讲了RabbitMQ的direct模式和fanout模式,本章介绍topic主题模式的应用.如果对direct模式下通过 ...

  3. ios开发网络学习AFN框架的使用一:get和post请求

    #import "ViewController.h" #import "AFNetworking.h" @interface ViewController () ...

  4. Docker CE for Windows安装使用

    原文:Docker CE for Windows安装使用 官网下载并安装Docker CE for Windows IDEA连接Docker Docker一些常用命令 Docker for windo ...

  5. 【oracle11g ,19】索引管理

    一.索引的分类: 1.逻辑上分为:  单列索引和复合索引  唯一索引和非唯一索引  函数索引 domain索引 2.物理上分:  分区索引和非分区索引 b-tree  bitmap 注意:表和索引最好 ...

  6. Oracle 自己主动内存參数依赖性

    图例:在该图中使用了下面參数名称缩写: MT = MEMORY_TARGET MMT = MEMORY_MAX_TARGET ST = SGA_TARGET PAT = PGA_AGGREGATE_T ...

  7. android 连接USB按power键锁屏2声锁屏音

    alps\frameworks\base\packages\Keyguard\src\com\android\keyguard\KeyguardViewMediator.java #1384 行左右: ...

  8. 开源库Fab-Transformation简单使用解析

    转载请注明出处王亟亟的大牛之路 相似于IPhone的悬浮按钮的操作,仅仅只是是固定的,当然经过自己的改动也能够动.这边仅仅是给伸手党一个福祉,外加加上一些自己的理解.让大家能够拿来就用.看了就懂,废话 ...

  9. 关系型数据库工作原理-快速缓存(翻译自Coding-Geek文章)

    本文翻译自Coding-Geek文章:< How does a relational database work>. 原文链接:http://coding-geek.com/how-dat ...

  10. WPF 针对数据源某个属性进行排序

    原文:WPF 针对数据源某个属性进行排序 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/wanlong360599336/article/detai ...