关闭连接
关闭,需要四次握手。
建立连接,读写数据,关闭连接
示意图

为什么关闭比建立连接多一次通信?
看示意图发现,关闭比连接多一次通信,示意图上是多了一条箭头线。为什么会多一次通信?首先,服务器端有两个动作,第一是收到客户端的通信,第二是发送通信给客户端。关闭的时候和建立连接的时候,本质区别在于,服务器的两个当做是在一次通信里完成,还是在两次通信里完成。
建立连接是一次,关闭连接是两次。
为什么关闭连接的时候,不能一次处理两个操作?因为服务器接受到客户端的关闭通信之后,很可能自己的任务没有处理完成,所以只能等处理完成了,稍后再发送通信给客户端,因此,关闭的时候,服务器的两个操作实际上是分离的,即第二个操作是在第一个操作的后面完成的,不是同时完成的。
客户端和服务器都要调用.close()方法去关闭自己的连接
并且,还要通知对方。
如果服务器没有关闭,那么服务器状态一直是等待状态。
建立连接
需要三次握手。
通信数据的序号
每次通信都有序号,比如客户端syn(seq=x),服务器收到通信是ack(x+1),服务器给客户端发送通信是syn(seq=y)。为什么要有序号?实际上是因为要给每个连接编号,不然不知道是哪个连接在通信,因为服务器在同一个端口同时在处理多个客户端连接。
为什么服务器还要给客户端发送syn(seq=y)?
服务器收到客户端的通信数据,然后给客户端发送确认数据,这个能理解,但是为什么还要发送syn呢?因为要确保客户端已经收到服务器的ack数据。说白了,就是没有syn(seq=y),就不能确保。
服务器等待
客户端主动关闭连接,服务器就是等待。因为服务器要处理任务。
客户端等待
客户端主动关闭,服务器先是等待,然后接着处理任务,任务处理完成之后,再通知客户端,客户端收到服务器的关闭通知之后,就是客户端等待。
最后一步是,服务器收到客户端的确认,双方最终关闭连接。
服务器等待-原因分析
一般就是因为客户端没有关闭。
具体来说
1.确实没有调用close
2.有耗时操作(火焰图可以非常明显看到),导致超时了
3.mysql的事务没有正确处理,例如:rollback 或者 commit
具体的应用场景
1.服务器出现服务器等待
客户端没有关闭,服务器出现服务器等待
2.客户端出现服务器等待
客户端没有关闭,服务器超时,服务器主动关闭,此时服务器反而成为了客户端,因为谁主动关闭,谁就是客户端,现在客户端反而变成了服务器,所以客户端出现服务器等待(即close_wait)。
(这个是客户端和服务器Mysql的截图,客户端出现服务器等待)

(这个是爬虫客户端和服务器的描述,爬虫客户端出现服务器等待)
作者在那篇文章的日志里头举了个场景,来说明CLOSE_WAIT和TIME_WAIT的区别,这里重新描述一下:
服务器A是一台爬虫服务器,它使用简单的HttpClient去请求资源服务器B上面的apache获取文件资源,正常情况下,如果请求成功,那么在抓取完资源后,服务器A会主动发出关闭连接的请求,这个时候就是主动关闭连接,服务器A的连接状态我们可以看到是TIME_WAIT。如果一旦发生异常呢?假设请求的资源服务器B上并不存在,那么这个时候就会由服务器B发出关闭连接的请求,服务器A就是被动的关闭了连接,如果服务器A被动关闭连接之后程序员忘了让HttpClient释放连接,那就会造成CLOSE_WAIT的状态了。 所以如果将大量CLOSE_WAIT的解决办法总结为一句话那就是:查代码。因为问题出在客户端程序里头没有关闭啊。
如何判断是服务器等待还是客户端等待?
核心是判断是主动关闭还是被动关闭。主动关闭就是客户端等待,被动关闭就是服务器等待。
解决方法
1.客户端等待
增大文件句柄数量参数。因为如果不增大,那么结果就是:正常——客户端等待——打开太多文件句柄异常——tomcat崩溃。
因为linux分配给一个用户的文件句柄是有限的(可以参考:blog.csdn.net/shootyou/ar… 而TIME_WAIT和CLOSE_WAIT两种状态如果一直被保持,那么意味着对应数目的通道就一直被占着,而且是“占着茅坑不使劲”,一旦达到句柄数上限,新的请求就无法被处理了,接着就是大量Too Many Open Files异常,tomcat崩溃。
2.服务器等待
一定是因为自己的程序有问题,而且一定是因为没有关闭。