最近工作过程中,遇到了在TCP建立好连接以后,发送和接收过程中,网络断开引起的socket无法关闭的问题。

ps:TCP的发送和接收都使用的是阻塞模式

一、设置发送和接收的超时时间
最开始想到的解决办法是设置发送和接收的超时时间,这样超时时间到了,发送和接收都会返回,socket就能正常关闭了。但是设置超时时间会引起其他问题。

1、设置发送超时以后,客户端(上位机)会出现,Interrupted system call问题,产生原因是send函数在阻塞模式下,一定要等到有可用空间将send发送的数据拷贝到发送缓冲中,但是超时了,还没发送,就出现Interrupted system call。

2、设置接收超时以后,客户端(上位机)会出现,TCP读取 [WinError 10053] 你的主机中的软件中止了一个已建立的连接问题

二、加大发送缓冲区
上面试验了,发送改为非阻塞行不通,所以还是改为阻塞了。既然是因为发送缓冲区太小了,导致send函数阻塞在等待可用缓冲区上了,那我直接加大发送缓冲区,让send函数能立即返回,这样send就不会阻塞了。

1.设置socke的发送缓冲区大小,实验了不生效

 
int sendbuf_len = (320 * 1024);
 
setsockopt(newsockfd, SOL_SOCKET, SO_SNDBUF, &sendbuf_len, sizeof(int));
最后通过直接修改系统的TCP发送缓冲区解决

/proc/sys/net/core/rmem_max        #收缓冲区最大值
/proc/sys/net/core/wmem_max        #发缓冲区最大值
/proc/sys/net/core/rmem_default    #收缓冲区默认值
/proc/sys/net/core/wmen_default    #发缓冲区默认值
1.一次能send的最大数据是发送缓冲区最大值的两倍(x2)
2.默认发送缓冲区最大值默认是(160 * 1024)
所以这里一次能发送的最大数据就是(320 * 1024)
3.观察应用层,发现发送的最大数据大小是 (450 * 1024)
所以这里设置发送缓冲区最大值为 (320 * 1024 = )
echo 327680 > /proc/sys/net/core/wmem_max
 
三、设置tcp的keepalive属性
接收阻塞的问题还没有解决,最后发现通过设置tcp的keepalive属性能解决网络断开以后,接收阻塞不退出问题。

int keepalive = 1; // 开启keepalive属性
  
 int keepidle = 10; // 如该连接在10秒内没有任何数据往来,则进行探测
  
 int keepinterval = 3; // 探测时发包的时间间隔为3秒
  
 int keepcount = 3; // 探测尝试的次数.如果第1次探测包就收到响应了,则后2次的不再发.
  
 setsockopt(newsockfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepalive, sizeof(keepalive));
  
 setsockopt(newsockfd, SOL_TCP, TCP_KEEPIDLE, (void *)&keepidle, sizeof(keepidle));
  
 setsockopt(newsockfd, SOL_TCP, TCP_KEEPINTVL, (void *)&keepinterval, sizeof(keepinterval));
  
 setsockopt(newsockfd, SOL_TCP, TCP_KEEPCNT, (void *)&keepcount, sizeof(keepcount));