Chinaunix首页 | 论坛 | 博客
  • 博客访问: 916385
  • 博文数量: 299
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 2493
  • 用 户 组: 普通用户
  • 注册时间: 2014-03-21 10:07
个人简介

Linux后台服务器编程。

文章分类

全部博文(299)

文章存档

2015年(2)

2014年(297)

分类: 系统运维

2014-08-07 11:41:33

原文地址:TCP time_wait 清晰解释 作者:skylway

近来网站随着访问量的增加,服务器的压力也随之上升,一个很明显的状况便是服务端产生了大量的TIME_WAIT状态,它究竟是什么、对系统有何影响、为什么很多人对它如此敏感? 
Google一下TIME_WAIT会有成千上万的文章,但总是别人的,今天自己整理一下对TIME_WAIT的认识。
 

TCP连接的终止 
TCP建立一个连接至少需要交换三个分组,也因此称之为TCP的三路握手(three-way handshake),然而在TCP终止连接时,由于双方都需要发送一个FIN分节给对端确认,因此TCP终止连接一般是需要交换四个分节。具体来看: 

1、 应用进程(active close)首先调用close,于是导致TCP发送一个FIN分节,表示数据已分送完毕,请求关闭套接字。 
2、 另一端应用进程(passive close)接受收到FIN,并由该端的TCP确认(确认的过程是TCP发送ACK分节给对端套接字)。FIN的接受也作为文件结束符传递给上层应用进 程。这里的文件结束符并非应用进程的EOF,在TCP字节流中,EOF的读或写通过收发一个特殊的FIN分节来实现。 
3、 另端(passive close)应用进程在接受到文件束符后,会调用close关闭它的套接字,这导致该端的TCP也发送了一个FIN分节。 
4、 主动关闭端(active close)接受到这个FIN后,TCP对它进行确认。(TCP发送ACK分节,值得注意的是主动关闭端在未接受到FIN之前,它的状态就是TIME_WAIT)。
 

 

这张图在google image中,花了五六分钟才找到,觉得这张图是最直观、易懂的。 

TIME_OUT状态的存在的意义 
从图中,很清晰的看到TIME_WAIT状态发生在了active close 端,产生的时间点是发送ACK K+1 分节之后,原因是防止ACK分节在网络中丢失(lost),此时passive close进入LAST_ACK状态,意为等待ACK分节,如果此时ACK分节真的丢失了(passive close端的LAST_ACK超时),那么passive close端将会再次发送一个FIN K分节给对端。这就是为什么在图中,出现两次FIN的分节。
 

这里有RFC的原文: 
Once the final ACK has been sent on an active close, the port/connection cannot be relaeased and re-used for the time period 2MSL. This is twice the maximum segment life and this constraint is imposed in case the the final ACK is lost. If the final ACK is lost then the passive closing host will time out awaiting an ACK in response to the closing FIN and will resend the FIN. If this arrives before the 2MSL time has expired there is no problem, after this time the FIN does not appear to belong to whatever connection might exist between the two clients. 

TIME_OUT存在的理由用术语来描述,摘自UNIX Network Programming Vol1 中: 
1、 可靠地实现TCP全双工连接的终止。 
2、 允许老的重复分节在网络中消逝。
 

TIME_OUT状态的持续时间 
图中标明了TIME_OUT状态的持续时间是最长分节生命周期(MSL)的两倍,即2MSL。RFC中的建议值是2分钟,Berkeley的实现传统上使用的是30秒,那么这意味着TIME_WAIT状态的延迟是在1~4分钟之间。
 

既然TIME_OUT状态的存在是有其意义的,为什么这么多人对其如此敏感,对于CS的模式,大多是由客户机主动关闭连接,这也避免了TIME_OUT产生于服务端,但对于某些协议,如HTTP则是由服务器执行主动关闭的。 

TCP的SO_LINKGER 选项 
相信只要提到TIME_OUT,SO_LINKGER就会现身,没错,该选项的设定控制着TCP的关闭形态,TCP默认是在close立即返回后,如果有数据残留在套接字的发送缓冲区中,系统将试着把这些数据发送给对端。 
JDK对该选项的定义为:
 
Java代码  收藏代码
  1. public void setSoLinger(boolean on, int linger) throws SocketException;  

两个参数将产生下列三种情形: 
1、 on 为 false,则该选项关闭,linger 的值被忽略,这就是TCP的缺省设置,close立即返回,如果可能将会传输未发送的数据给对端; 

2、 设置on为true,linger大于0(我在很多文章中看到这里写的是非0,但Java中,给该选项设置小于0会抛出” invalid value for SO_LINGER”异常)那么当close某个连接时,内核将拖延一段时间。即linger的时间(linger的单位为秒,最大值为65535)。这 里的拖延(close 阻塞)是相对于BIO来讲,如果套接字先前被设置为非阻塞(NIO),那么将不等待close完成,即使linger > 0也是如此。如果套接字发送缓冲区中仍然残留数据,那么close线程将被投入睡眠,直到所有数据都已发送完,并且均被对端确认或者拖延时间 到,close才会被唤醒。 
这里有一个原则:设置SO_LINGER套接字选项后,close的成功返回只是告诉我们早先发送的数据(包括FIN)已由对端TCP确认,而不能告诉我们对端的应用进程是否已读取到数据,但如果不设置该套接字选项,那我们连对端TCP是否确认了数据都不知道; 

3、 设置on为true,linger 为0,那么当close某个连接时,TCP夭折该连接。也就是说TCP将丢弃保留在发送缓冲区中的任何数据,仅仅给对端发送一个RST分节,而没有通常所说的四分组终止序列,这样一来避免了TIME_WAIT状态。 
然而在2MSL秒内创建该连接的另一个化身,会导致老的重复分节被不正确地递送到新的化身上。这样的情况,有一个替代,就是TCP的 SO_REUSEADDR选项。 

这个选项留到下一篇中详述   出处:

阅读(1548) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~