精华内容
下载资源
问答
  • TIME_WAIT过多是因为什么 (首先需要注意的是,客户机、服务器均可以发起对 TCP 连接的关闭,以下以服务器发起关闭为例。) TIME_WAIT 是什么:关闭 TCP 连接过程中,第 4 次挥手时,服务器发送了 ACK 报文段之后...

    TIME_WAIT过多是因为什么

    (首先需要注意的是,客户机、服务器均可以发起对 TCP 连接的关闭,以下以服务器发起关闭为例。)

    TIME_WAIT 是什么:关闭 TCP 连接过程中,第 4 次挥手时,服务器发送了 ACK 报文段之后,服务器会进入 TIME_WAIT 状态,这是为了确保客户机能够正确地关闭、释放资源。TIME_WAIT状态将持续2个MSL(Max Segment Lifetime),TIME_WAIT状态下的socket不能被回收使用。

    TIME_WAIT 过多的原因:对于一个处理大量短连接的服务器,如果是由服务器主动关闭客户端的连接,将导致服务器存在大量的处于TIME_WAIT状态的socket,严重影响服务器的处理能力,甚至耗尽可用的socket。
    解决:修改参数TcpTimedWaitDelay,缩短 TIME_WAIT 的等待时间。

    在这里插入图片描述

    1. 《HTTP 权威指南》 - P90;
    2. 《1 计算机网络之探赜索隐》 - 哈工大 - 网易慕课 - 15.314 TCP连接管理 - 09分12秒;
    3. 《计算机网络 - 自顶向下》 - 3.5.6 TCP连接管理 - P169——P174。);
    4. 发现大量的TIME_WAIT解决办法
    5. 理解TIME_WAIT,彻底弄清解决TCP: time wait bucket table overflow
    展开全文
  • Linux和windows下TIME_WAIT过多的解决办法 如果使用了nginx代理,那么系统TIME_WAIT的数量会变得比较多,这是由于nginx代理使用了短链接的方式和后端交互的原因,使得nginx和后端的ESTABLISHED变得很少而TIME_WAIT...

    Linux和windows下TIME_WAIT过多的解决办法

    如果使用了nginx代理,那么系统TIME_WAIT的数量会变得比较多,这是由于nginx代理使用了短链接的方式和后端交互的原因,使得nginx和后端的ESTABLISHED变得很少而TIME_WAIT很多。这不但发生在安装nginx的代理服务器上,而且也会使后端的app服务器上有大量的TIME_WAIT。查阅TIME_WAIT资料,发现这个状态很多也没什么大问题,但可能因为它占用了系统过多的端口,导致后续的请求无法获取端口而造成障碍。

    虽然TIME_WAIT会造成一些问题,但是要完全枪毙掉它也是不正当的,虽然看起来这么做没什么错。具体可看这篇文档:

    http://hi.baidu.com/tim_bi/blog/item/35b005d784ca91d5a044df1d.html

    所以目前看来最好的办法是让每个TIME_WAIT早点过期。

    linux上可以这么配置:

    #让TIME_WAIT状态可以重用,这样即使TIME_WAIT占满了所有端口,也不会拒绝新的请求造成障碍
    echo "1" > /proc/sys/net/ipv4/tcp_tw_reuse
    #让TIME_WAIT尽快回收,我也不知是多久,观察大概是一秒钟
    echo "1" > /proc/sys/net/ipv4/tcp_tw_recycle

    很多文档都会建议两个参数都配置上,但是我发现只用修改tcp_tw_recycle就可以解决问题的了,TIME_WAIT重用TCP协议本身就是不建议打开的。

    不能重用端口可能会造成系统的某些服务无法启动,比如要重启一个系统监控的软件,它用了40000端口,而这个端口在软件重启过程中刚好被使用了,就可能会重启失败的。linux默认考虑到了这个问题,有这么个设定:

    #查看系统本地可用端口极限值
    cat /proc/sys/net/ipv4/ip_local_port_range

    用这条命令会返回两个数字,默认是:32768 61000,说明这台机器本地能向外连接61000-32768=28232个连接,注意是本地向外连接,不是这台机器的所有连接,不会影响这台机器的80端口的对外连接数。但这个数字会影响到代理服务器(nginx)对app服务器的最大连接数,因为nginx对app是用的异步传输,所以这个环节的连接速度很快,所以堆积的连接就很少。假如nginx对app服务器之间的带宽出了问题或是app服务器有问题,那么可能使连接堆积起来,这时可以通过设定nginx的代理超时时间,来使连接尽快释放掉,一般来说极少能用到28232个连接。

    因为有软件使用了40000端口监听,常常出错的话,可以通过设定ip_local_port_range的最小值来解决:

    echo "40001 61000" > /proc/sys/net/ipv4/ip_local_port_range

    但是这么做很显然把系统可用端口数减少了,这时可以把ip_local_port_range的最大值往上调,但是好习惯是使用不超过32768的端口来侦听服务,另外也不必要去修改ip_local_port_range数值成1024 65535之类的,意义不大。

    因为使用了nginx代理,在windows下也会造成大量TIME_WAIT,当然windows也可以调整:

    在注册表(regedit)的HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/Tcpip/Parameters上添加一个DWORD类型的值TcpTimedWaitDelay,值就是秒数,即可。

    windows默认是重用TIME_WAIT,我现在还不知道怎么改成不重用的,本地端口也没查到是什么值,但这些都关系不大,都可以按系统默认运作。

    ------------------------------------------------------------------------------------------------------------------------

    TIME_WAIT状态

    根据TCP协议,主动发起关闭的一方,会进入TIME_WAIT状态,持续2*MSL(Max Segment Lifetime),缺省为240秒,在这个post中简洁的介绍了为什么需要这个状态。

    值得一说的是,对于基于TCP的HTTP协议,关闭TCP连接的是Server端,这样,Server端会进入TIME_WAIT状态,可想而知,对于访问量大的Web Server,会存在大量的TIME_WAIT状态,假如server一秒钟接收1000个请求,那么就会积压240*1000=240,000个TIME_WAIT的记录,维护这些状态给Server带来负担。当然现代操作系统都会用快速的查找算法来管理这些TIME_WAIT,所以对于新的TCP连接请求,判断是否hit中一个TIME_WAIT不会太费时间,但是有这么多状态要维护总是不好。

    HTTP协议1.1版规定default行为是Keep-Alive,也就是会重用TCP连接传输多个request/response,一个主要原因就是发现了这个问题。还有一个方法减缓TIME_WAIT压力就是把系统的2*MSL时间减少,因为240秒的时间实在是忒长了点,对于Windows,修改注册表,在HKEY_LOCAL_MACHINE/ SYSTEM/CurrentControlSet/Services/ Tcpip/Parameters上添加一个DWORD类型的值TcpTimedWaitDelay,一般认为不要少于60,不然可能会有麻烦。

    对于大型的服务,一台server搞不定,需要一个LB(Load Balancer)把流量分配到若干后端服务器上,如果这个LB是以NAT方式工作的话,可能会带来问题。假如所有从LB到后端Server的IP包的source address都是一样的(LB的对内地址),那么LB到后端Server的TCP连接会受限制,因为频繁的TCP连接建立和关闭,会在server上留下TIME_WAIT状态,而且这些状态对应的remote address都是LB的,LB的source port撑死也就60000多个(2^16=65536,1~1023是保留端口,还有一些其他端口缺省也不会用),每个LB上的端口一旦进入Server的TIME_WAIT黑名单,就有240秒不能再用来建立和Server的连接,这样LB和Server最多也就能支持300个左右的连接。如果没有LB,不会有这个问题,因为这样server看到的remote address是internet上广阔无垠的集合,对每个address,60000多个port实在是够用了。

    一开始我觉得用上LB会很大程度上限制TCP的连接数,但是实验表明没这回事,LB后面的一台Windows Server 2003每秒处理请求数照样达到了600个,难道TIME_WAIT状态没起作用?用Net Monitor和netstat观察后发现,Server和LB的XXXX端口之间的连接进入TIME_WAIT状态后,再来一个LB的XXXX端口的SYN包,Server照样接收处理了,而是想像的那样被drop掉了。翻书,从书堆里面找出覆满尘土的大学时代买的《UNIX Network Programming, Volume 1, Second Edition: Networking APIs: Sockets and XTI》,中间提到一句,对于BSD-derived实现,只要SYN的sequence number比上一次关闭时的最大sequence number还要大,那么TIME_WAIT状态一样接受这个SYN,难不成Windows也算BSD-derived?有了这点线索和关键字(BSD),找到这个post,在NT4.0的时候,还是和BSD-derived不一样的,不过Windows Server 2003已经是NT5.2了,也许有点差别了。

    做个试验,用Socket API编一个Client端,每次都Bind到本地一个端口比如2345,重复的建立TCP连接往一个Server发送Keep-Alive=false的HTTP请求,Windows的实现让sequence number不断的增长,所以虽然Server对于Client的2345端口连接保持TIME_WAIT状态,但是总是能够接受新的请求,不会拒绝。那如果SYN的Sequence Number变小会怎么样呢?同样用Socket API,不过这次用Raw IP,发送一个小sequence number的SYN包过去,Net Monitor里面看到,这个SYN被Server接收后如泥牛如海,一点反应没有,被drop掉了。

    按照书上的说法,BSD-derived和Windows Server 2003的做法有安全隐患,不过至少这样至少不会出现TIME_WAIT阻止TCP请求的问题,当然,客户端要配合,保证不同TCP连接的sequence number要上涨不要下降。

    ----------------------------------------------------------------------------------------------------------------------------

    Socket中的TIME_WAIT状态

    在高并发短连接的server端,当server处理完client的请求后立刻closesocket此时会出现time_wait状态然后如果client再并发2000个连接,此时部分连接就连接不上了,用linger强制关闭可以解决此问题,但是linger会导致数据丢失,linger值为0时是强制关闭,无论并发多少多能正常连接上,如果非0会发生部分连接不上的情况!(可调用setsockopt设置套接字的linger延时标志,同时将延时时间设置为0。
    TCP/IP的RFC文档。TIME_WAIT是TCP连接断开时必定会出现的状态。
    是无法避免掉的,这是TCP协议实现的一部分。
    在WINDOWS下,可以修改注册表让这个时间变短一些

    time_wait的时间为2msl,默认为4min.
    你可以通过改变这个变量:
    TcpTimedWaitDelay 
    把它缩短到30s
    TCP要保证在所有可能的情况下使得所有的数据都能够被投递。当你关闭一个socket时,主动关闭一端的socket将进入TIME_WAIT状态,而被动关闭一方则转入CLOSED状态,这的确能够保证所有的数据都被传输。当一个socket关闭的时候,是通过两端互发信息的四次握手过程完成的,当一端调用close()时,就说明本端没有数据再要发送了。这好似看来在握手完成以后,socket就都应该处于关闭CLOSED状态了。但这有两个问题,首先,我们没有任何机制保证最后的一个ACK能够正常传输,第二,网络上仍然有可能有残余的数据包(wandering duplicates),我们也必须能够正常处理。
    通过正确的状态机,我们知道双方的关闭过程如下

    假设最后一个ACK丢失了,服务器会重发它发送的最后一个FIN,所以客户端必须维持一个状态信息,以便能够重发ACK;如果不维持这种状态,客户端在接收到FIN后将会响应一个RST,服务器端接收到RST后会认为这是一个错误。如果TCP协议能够正常完成必要的操作而终止双方的数据流传输,就必须完全正确的传输四次握手的四个节,不能有任何的丢失。这就是为什么socket在关闭后,仍然处于 TIME_WAIT状态,因为他要等待以便重发ACK。

    如果目前连接的通信双方都已经调用了close(),假定双方都到达CLOSED状态,而没有TIME_WAIT状态时,就会出现如下的情况。现在有一个新的连接被建立起来,使用的IP地址与端口与先前的完全相同,后建立的连接又称作是原先连接的一个化身。还假定原先的连接中有数据报残存于网络之中,这样新的连接收到的数据报中有可能是先前连接的数据报。为了防止这一点,TCP不允许从处于TIME_WAIT状态的socket建立一个连接。处于TIME_WAIT状态的socket在等待两倍的MSL时间以后(之所以是两倍的MSL,是由于MSL是一个数据报在网络中单向发出到认定丢失的时间,一个数据报有可能在发送图中或是其响应过程中成为残余数据报,确认一个数据报及其响应的丢弃的需要两倍的MSL),将会转变为CLOSED状态。这就意味着,一个成功建立的连接,必然使得先前网络中残余的数据报都丢失了。

    由于TIME_WAIT状态所带来的相关问题,我们可以通过设置SO_LINGER标志来避免socket进入TIME_WAIT状态,这可以通过发送RST而取代正常的TCP四次握手的终止方式。但这并不是一个很好的主意,TIME_WAIT对于我们来说往往是有利的。

     

     

    客户端与服务器端建立TCP/IP连接后关闭SOCKET后,服务器端连接的端口
    状态为TIME_WAIT
    是不是所有执行主动关闭的socket都会进入TIME_WAIT状态呢?
    有没有什么情况使主动关闭的socket直接进入CLOSED状态呢?
    主动关闭的一方在发送最后一个 ack 后
    就会进入 TIME_WAIT 状态 停留2MSL(max segment lifetime)时间
    这个是TCP/IP必不可少的,也就是“解决”不了的。

    也就是TCP/IP设计者本来是这么设计的
    主要有两个原因
    1。防止上一次连接中的包,迷路后重新出现,影响新连接
       (经过2MSL,上一次连接中所有的重复包都会消失)
    2。可靠的关闭TCP连接
       在主动关闭方发送的最后一个 ack(fin) ,有可能丢失,这时被动方会重新发
       fin, 如果这时主动方处于 CLOSED 状态 ,就会响应 rst 而不是 ack。所以
       主动方要处于 TIME_WAIT 状态,而不能是 CLOSED 。

    TIME_WAIT 并不会占用很大资源的,除非受到攻击。

    还有,如果一方 send 或 recv 超时,就会直接进入 CLOSED 状态

     

    socket-faq中的这一段讲的也很好,摘录如下:
    2.7. Please explain the TIME_WAIT state.

    Remember that TCP guarantees all data transmitted will be delivered,
    if at all possible. When you close a socket, the server goes into a
    TIME_WAIT state, just to be really really sure that all the data has
    gone through. When a socket is closed, both sides agree by sending
    messages to each other that they will send no more data. This, it
    seemed to me was good enough, and after the handshaking is done, the
    socket should be closed. The problem is two-fold. First, there is no
    way to be sure that the last ack was communicated successfully.
    Second, there may be "wandering duplicates" left on the net that must
    be dealt with if they are delivered.

    Andrew Gierth (andrew@erlenstar.demon.co.uk) helped to explain the
    closing sequence in the following usenet posting:

    Assume that a connection is in ESTABLISHED state, and the client is
    about to do an orderly release. The client's sequence no. is Sc, and
    the server's is Ss. Client Server
    ====== ======
    ESTABLISHED ESTABLISHED
    (client closes)
    ESTABLISHED ESTABLISHED
    ------->>
    FIN_WAIT_1
    <<-------- 
    FIN_WAIT_2 CLOSE_WAIT
    <<-------- (server closes)
    LAST_ACK
    , ------->>
    TIME_WAIT CLOSED
    (2*msl elapses...)
    CLOSED

    Note: the +1 on the sequence numbers is because the FIN counts as one
    byte of data. (The above diagram is equivalent to fig. 13 from RFC
    793).

    Now consider what happens if the last of those packets is dropped in
    the network. The client has done with the connection; it has no more
    data or control info to send, and never will have. But the server does
    not know whether the client received all the data correctly; that's
    what the last ACK segment is for. Now the server may or may not care
    whether the client got the data, but that is not an issue for TCP; TCP
    is a reliable rotocol, and must distinguish between an orderly
    connection close where all data is transferred, and a connection abort
    where data may or may not have been lost.

    So, if that last packet is dropped, the server will retransmit it (it
    is, after all, an unacknowledged segment) and will expect to see a
    suitable ACK segment in reply. If the client went straight to CLOSED,
    the only possible response to that retransmit would be a RST, which
    would indicate to the server that data had been lost, when in fact it
    had not been.

    (Bear in mind that the server's FIN segment may, additionally, contain
    data.)

    DISCLAIMER: This is my interpretation of the RFCs (I have read all the
    TCP-related ones I could find), but I have not attempted to examine
    implementation source code or trace actual connections in order to
    verify it. I am satisfied that the logic is correct, though.

    More commentarty from Vic:

    The second issue was addressed by Richard Stevens (rstevens@noao.edu,
    author of "Unix Network Programming", see ``1.5 Where can I get source
    code for the book [book title]?''). I have put together quotes from
    some of his postings and email which explain this. I have brought
    together paragraphs from different postings, and have made as few
    changes as possible.

    From Richard Stevens (rstevens@noao.edu):

    If the duration of the TIME_WAIT state were just to handle TCP's full-
    duplex close, then the time would be much smaller, and it would be
    some function of the current RTO (retransmission timeout), not the MSL
    (the packet lifetime).

    A couple of points about the TIME_WAIT state.

    o The end that sends the first FIN goes into the TIME_WAIT state,
    because that is the end that sends the final ACK. If the other
    end's FIN is lost, or if the final ACK is lost, having the end that
    sends the first FIN maintain state about the connection guarantees
    that it has enough information to retransmit the final ACK.

    o Realize that TCP sequence numbers wrap around after 2**32 bytes
    have been transferred. Assume a connection between A.1500 (host A,
    port 1500) and B.2000. During the connection one segment is lost
    and retransmitted. But the segment is not really lost, it is held
    by some intermediate router and then re-injected into the network.
    (This is called a "wandering duplicate".) But in the time between
    the packet being lost & retransmitted, and then reappearing, the
    connection is closed (without any problems) and then another
    connection is established between the same host, same port (that
    is, A.1500 and B.2000; this is called another "incarnation" of the
    connection). But the sequence numbers chosen for the new
    incarnation just happen to overlap with the sequence number of the
    wandering duplicate that is about to reappear. (This is indeed
    possible, given the way sequence numbers are chosen for TCP
    connections.) Bingo, you are about to deliver the data from the
    wandering duplicate (the previous incarnation of the connection) to
    the new incarnation of the connection. To avoid this, you do not
    allow the same incarnation of the connection to be reestablished
    until the TIME_WAIT state terminates.

    Even the TIME_WAIT state doesn't complete solve the second problem,
    given what is called TIME_WAIT assassination. RFC 1337 has more
    details.

    o The reason that the duration of the TIME_WAIT state is 2*MSL is
    that the maximum amount of time a packet can wander around a
    network is assumed to be MSL seconds. The factor of 2 is for the
    round-trip. The recommended value for MSL is 120 seconds, but
    Berkeley-derived implementations normally use 30 seconds instead.
    This means a TIME_WAIT delay between 1 and 4 minutes. Solaris 2.x
    does indeed use the recommended MSL of 120 seconds.

    A wandering duplicate is a packet that appeared to be lost and was
    retransmitted. But it wasn't really lost ... some router had
    problems, held on to the packet for a while (order of seconds, could
    be a minute if the TTL is large enough) and then re-injects the packet
    back into the network. But by the time it reappears, the application
    that sent it originally has already retransmitted the data contained
    in that packet.

    Because of these potential problems with TIME_WAIT assassinations, one
    should not avoid the TIME_WAIT state by setting the SO_LINGER option
    to send an RST instead of the normal TCP connection termination
    (FIN/ACK/FIN/ACK). The TIME_WAIT state is there for a reason; it's
    your friend and it's there to help you :-)

    I have a long discussion of just this topic in my just-released
    "TCP/IP Illustrated, Volume 3". The TIME_WAIT state is indeed, one of
    the most misunderstood features of TCP.

    I'm currently rewriting "Unix Network Programming" (see ``1.5 Where
    can I get source code for the book [book title]?''). and will include
    lots more on this topic, as it is often confusing and misunderstood.

    An additional note from Andrew:

    Closing a socket: if SO_LINGER has not been called on a socket, then
    close() is not supposed to discard data. This is true on SVR4.2 (and,
    apparently, on all non-SVR4 systems) but apparently not on SVR4; the
    use of either shutdown() or SO_LINGER seems to be required to
    guarantee delivery of all data.

    -------------------------------------------------------------------------------------------

    讨厌的 Socket TIME_WAIT 问题

    netstat -n | awk '/^tcp/ {++state[$NF]} END {for(key in state) print key,"/t",state[key]}'

    会得到类似下面的结果,具体数字会有所不同:

    LAST_ACK 1
    SYN_RECV 14
    ESTABLISHED 79
    FIN_WAIT1 28
    FIN_WAIT2 3
    CLOSING 5
    TIME_WAIT 1669

    状态:描述
    CLOSED:无连接是活动的或正在进行
    LISTEN:服务器在等待进入呼叫
    SYN_RECV:一个连接请求已经到达,等待确认
    SYN_SENT:应用已经开始,打开一个连接
    ESTABLISHED:正常数据传输状态
    FIN_WAIT1:应用说它已经完成
    FIN_WAIT2:另一边已同意释放
    ITMED_WAIT:等待所有分组死掉
    CLOSING:两边同时尝试关闭
    TIME_WAIT:另一边已初始化一个释放
    LAST_ACK:等待所有分组死掉

    也就是说,这条命令可以把当前系统的网络连接状态分类汇总。

    下面解释一下为啥要这样写:

    一个简单的管道符连接了netstat和awk命令。

    ------------------------------------------------------------------

    每个TCP报文在网络内的最长时间,就称为MSL(Maximum Segment Lifetime),它的作用和IP数据包的TTL类似。

    RFC793指出,MSL的值是2分钟,但是在实际的实现中,常用的值有以下三种:30秒,1分钟,2分钟。

    注意一个问题,进入TIME_WAIT状态的一般情况下是客户端,大多数服务器端一般执行被动关闭,不会进入TIME_WAIT状态,当在服务

    器端关闭某个服务再重新启动时,它是会进入TIME_WAIT状态的。

    举例:
    1.客户端连接服务器的80服务,这时客户端会启用一个本地的端口访问服务器的80,访问完成后关闭此连接,立刻再次访问服务器的

    80,这时客户端会启用另一个本地的端口,而不是刚才使用的那个本地端口。原因就是刚才的那个连接还处于TIME_WAIT状态。
    2.客户端连接服务器的80服务,这时服务器关闭80端口,立即再次重启80端口的服务,这时可能不会成功启动,原因也是服务器的连

    接还处于TIME_WAIT状态。


    检查net.ipv4.tcp_tw当前值,将当前的值更改为1分钟:
    [root@aaa1 ~]# sysctl -a|grep net.ipv4.tcp_tw
    net.ipv4.tcp_tw_reuse = 0
    net.ipv4.tcp_tw_recycle = 0
    [root@aaa1 ~]#

    vi /etc/sysctl
    增加或修改net.ipv4.tcp_tw值:
    net.ipv4.tcp_tw_reuse = 1
    net.ipv4.tcp_tw_recycle = 1

    使内核参数生效:
    [root@aaa1 ~]# sysctl -p

    [root@aaa1 ~]# sysctl -a|grep net.ipv4.tcp_tw
    net.ipv4.tcp_tw_reuse = 1
    net.ipv4.tcp_tw_recycle = 1

    用netstat再观察正常


    这里解决问题的关键是如何能够重复利用time_wait的值,我们可以设置时检查一下time和wait的值
    #sysctl -a | grep time | grep wait
    net.ipv4.netfilter.ip_conntrack_tcp_timeout_time_wait = 120
    net.ipv4.netfilter.ip_conntrack_tcp_timeout_close_wait = 60
    net.ipv4.netfilter.ip_conntrack_tcp_timeout_fin_wait = 120

    问一下TIME_WAIT有什么问题,是闲置而且内存不回收吗?

    是的,这样的现象实际是正常的,有时和访问量大有关,设置这两个参数: reuse是表示是否允许重新应用处于TIME-WAIT状态的

    socket用于新的TCP连接; recyse是加速TIME-WAIT sockets回收

     

    Q: 我正在写一个unix server程序,不是daemon,经常需要在命令行上重启它,绝大 
    多数时候工作正常,但是某些时候会报告"bind: address in use",于是重启失 
    败。 

    A: Andrew Gierth 
    server程序总是应该在调用bind()之前设置SO_REUSEADDR套接字选项。至于 
    TIME_WAIT状态,你无法避免,那是TCP协议的一部分。 

    Q: 如何避免等待60秒之后才能重启服务 

    A: Erik Max Francis 

    使用setsockopt,比如 

    -------------------------------------------------------------------------- 
    int option = 1; 

    if ( setsockopt ( masterSocket, SOL_SOCKET, SO_REUSEADDR, &option, 
    sizeof( option ) ) < 0 ) 

    die( "setsockopt" ); 

    -------------------------------------------------------------------------- 

    Q: 编写 TCP/SOCK_STREAM 服务程序时,SO_REUSEADDR到底什么意思? 

    A: 这个套接字选项通知内核,如果端口忙,但TCP状态位于 TIME_WAIT ,可以重用 
    端口。如果端口忙,而TCP状态位于其他状态,重用端口时依旧得到一个错误信息, 
    指明"地址已经使用中"。如果你的服务程序停止后想立即重启,而新套接字依旧 
    使用同一端口,此时 SO_REUSEADDR 选项非常有用。必须意识到,此时任何非期 
    望数据到达,都可能导致服务程序反应混乱,不过这只是一种可能,事实上很不 
    可能。 

    一个套接字由相关五元组构成,协议、本地地址、本地端口、远程地址、远程端 
    口。SO_REUSEADDR 仅仅表示可以重用本地本地地址、本地端口,整个相关五元组 
    还是唯一确定的。所以,重启后的服务程序有可能收到非期望数据。必须慎重使 
    用 SO_REUSEADDR 选项。 

    Q: 在客户机/服务器编程中(TCP/SOCK_STREAM),如何理解TCP自动机 TIME_WAIT 状 
    态? 

    A: W. Richard Stevens <1999年逝世,享年49岁> 

    下面我来解释一下 TIME_WAIT 状态,这些在<> 
    中2.6节解释很清楚了。 

    MSL(最大分段生存期)指明TCP报文在Internet上最长生存时间,每个具体的TCP实现 
    都必须选择一个确定的MSL值。RFC 1122建议是2分钟,但BSD传统实现采用了30秒。 

    TIME_WAIT 状态最大保持时间是2 * MSL,也就是1-4分钟。 

    IP头部有一个TTL,最大值255。尽管TTL的单位不是秒(根本和时间无关),我们仍需 
    假设,TTL为255的TCP报文在Internet上生存时间不能超过MSL。 

    TCP报文在传送过程中可能因为路由故障被迫缓冲延迟、选择非最优路径等等,结果 
    发送方TCP机制开始超时重传。前一个TCP报文可以称为"漫游TCP重复报文",后一个 
    TCP报文可以称为"超时重传TCP重复报文",作为面向连接的可靠协议,TCP实现必须 
    正确处理这种重复报文,因为二者可能最终都到达。 

    一个通常的TCP连接终止可以用图描述如下: 

    client server 
    FIN M 
    close -----------------> (被动关闭) 
    ACK M+1 
    <----------------- 
    FIN N 
    <----------------- close 
    ACK N+1 
    -----------------> 

    为什么需要 TIME_WAIT 状态? 

    假设最终的ACK丢失,server将重发FIN,client必须维护TCP状态信息以便可以重发 
    最终的ACK,否则会发送RST,结果server认为发生错误。TCP实现必须可靠地终止连 
    接的两个方向(全双工关闭),client必须进入 TIME_WAIT 状态,因为client可能面 
    临重发最终ACK的情形。 


    scz 2001-08-31 13:28 

    先调用close()的一方会进入TIME_WAIT状态 


    此外,考虑一种情况,TCP实现可能面临先后两个同样的相关五元组。如果前一个连 
    接处在 TIME_WAIT 状态,而允许另一个拥有相同相关五元组的连接出现,可能处理 
    TCP报文时,两个连接互相干扰。使用 SO_REUSEADDR 选项就需要考虑这种情况。 

    为什么 TIME_WAIT 状态需要保持 2MSL 这么长的时间? 

    如果 TIME_WAIT 状态保持时间不足够长(比如小于2MSL),第一个连接就正常终止了。 
    第二个拥有相同相关五元组的连接出现,而第一个连接的重复报文到达,干扰了第二 
    个连接。TCP实现必须防止某个连接的重复报文在连接终止后出现,所以让TIME_WAIT 
    状态保持时间足够长(2MSL),连接相应方向上的TCP报文要么完全响应完毕,要么被 
    丢弃。建立第二个连接的时候,不会混淆。 

    A: 小四 

    在Solaris 7下有内核参数对应 TIME_WAIT 状态保持时间 

    # ndd -get /dev/tcp tcp_time_wait_interval 
    240000 
    # ndd -set /dev/tcp tcp_time_wait_interval 1000 

    缺省设置是240000ms,也就是4分钟。如果用ndd修改这个值,最小只能设置到1000ms, 
    也就是1秒。显然内核做了限制,需要Kernel Hacking。 

    # echo "tcp_param_arr/W 0t0" | adb -kw /dev/ksyms /dev/mem 
    physmem 3b72 
    tcp_param_arr: 0x3e8 = 0x0 
    # ndd -set /dev/tcp tcp_time_wait_interval 0 

    我不知道这样做有什么灾难性后果,参看<>的声明。 

    Q: TIME_WAIT 状态保持时间为0会有什么灾难性后果?在普遍的现实应用中,好象也 
    就是服务器不稳定点,不见得有什么灾难性后果吧? 

    D: rain@bbs.whnet.edu.cn 

    Linux 内核源码 /usr/src/linux/include/net/tcp.h 中 

    #define TCP_TIMEWAIT_LEN (60*HZ) /* how long to wait to successfully 
    * close the socket, about 60 seconds */ 

    最好不要改为0,改成1。端口分配是从上一次分配的端口号+1开始分配的,所以一般 

    不会有什么问题。端口分配算法在tcp_ipv4.c中tcp_v4_get_port中


    转载:http://blog.csdn.net/zjl410091917/article/details/7990787

    展开全文
  • 如果FIN_WAIT_1状态下,同时收到待FIN标识和ACK标识的报文时,可以直接进入TIME_WAIT状态,而无需经过FIN_WAIT_2状态。 CLOSE_WAIT:被动关闭的机器收到对方请求关闭连接的FIN报文,在第一次ACK应答后,马上进入...

    本文根据众多互联网博客内容整理后形成,引用内容的版权归原始作者所有,仅限于学习研究使用

    网上查了一下端口状态的资料,我下面总结了一下,自己学习学习:

    TCP状态转移要点
        TCP协议规定,对于已经建立的连接,网络双方要进行四次握手才能成功断开连接,如果缺少了其中某个步骤,将会使连接处于假死状态,连接本身占用的资源不会被释放。网络服务器程序要同时管理大量连接,所以很有必要保证无用连接完全断开,否则大量僵死的连接会浪费许多服务器资源。在众多TCP状态中,最值得注意的状态有两个:CLOSE_WAIT和TIME_WAIT。  
     
    1、LISTENING状态
      FTP服务启动后首先处于侦听(LISTENING)状态。

    2、ESTABLISHED状态
      ESTABLISHED的意思是建立连接。表示两台机器正在通信。

    3、CLOSE_WAIT

        对方主动关闭连接或者网络异常导致连接中断,这时我方的状态会变成CLOSE_WAIT 此时我方要调用close()来使得连接正确关闭

    4、TIME_WAIT

        我方主动调用close()断开连接,收到对方确认后状态变为TIME_WAITTCP协议规定TIME_WAIT状态会一直持续2MSL(即两倍的分段最大生存期),以此来确保旧的连接状态不会对新连接产生影响。处于TIME_WAIT状态的连接占用的资源不会被内核释放,所以作为服务器,在可能的情况下,尽量不要主动断开连接,以减少TIME_WAIT状态造成的资源浪费。

        目前有一种避免TIME_WAIT资源浪费的方法,就是关闭socket的LINGER选项。但这种做法是TCP协议不推荐使用的,在某些情况下这个操作可能会带来错误。

    1. socket的状态

    1.1 状态说明

    CLOSED 没有使用这个套接字[netstat 无法显示closed状态]
    LISTEN 套接字正在监听连接[调用listen后]
    SYN_SENT 套接字正在试图主动建立连接[发送SYN后还没有收到ACK]
    SYN_RECEIVED 正在处于连接的初始同步状态[收到对方的SYN,但还没收到自己发过去的SYN的ACK]
    ESTABLISHED 连接已建立
    CLOSE_WAIT 远程套接字已经关闭:正在等待关闭这个套接字[被动关闭的一方收到FIN]
    FIN_WAIT_1 套接字已关闭,正在关闭连接[发送FIN,没有收到ACK也没有收到FIN]
    CLOSING 套接字已关闭,远程套接字正在关闭,暂时挂起关闭确认[在FIN_WAIT_1状态下收到被动方的FIN]
    LAST_ACK 远程套接字已关闭,正在等待本地套接字的关闭确认[被动方在CLOSE_WAIT状态下发送FIN]
    FIN_WAIT_2 套接字已关闭,正在等待远程套接字关闭[在FIN_WAIT_1状态下收到发过去FIN对应的ACK]
    TIME_WAIT 这个套接字已经关闭,正在等待远程套接字的关闭传送[FIN、ACK、FIN、ACK都完毕,这是主动方的最后一个状态,在过了2MSL时间后变为CLOSED状态]

    1.2 状态变迁图

    摘自《UNIX 网络编程 卷1:套接字联网API》 P35

     

    2. TCP的三次握手和四次挥手

    2.1 总结图

    左边为客户端的状态转变,后边为服务器的状态转变

    2.2 说明

    2.2.1   connect返回-1

                 errno=110(ETIMEDOUT),当服务器端网线拔了的时候,客户端发送SYN过去就会收不到ACK,因此就会出现这个错误,1分钟内就会返  回这个错误。

                 errno=111(ECONNREFUSED),当服务器未listen时,就会报这个错

    2.2.2 ESTABLISHED不一定真的establish

                  会出现这种情况:client为ESTABLISHED状态而server为SYN_REVD状态。

                 这是因为LINUX不像其他操作系统在收到SYN为该连接立马分配一块内存空间用于存储相关的数据和结构,而是延迟到接收到client的ACK,即三次握手    真正完成后才分配空间,这是为了防范SYN flooding攻击。 如果是这种情况,那么就会出现client端未ESTABLISHED状态,server为SYN_RECV状态。   

                 并且server的SYN_RECV状态在一定时间后会消失,client的established状态也会消失。这是因为server在SYN_RECV状态时,会像client发送多次的SYN+ACK(因为他以为自己的这个包对方没收到),发送的次数定义在/proc/sys/net/ipv4/tcp_synack_retries中,默认为5.在发送5次之后还没有收到ACK,就将其回收了,所以用netstat查看就看不到这个SYN_RECV状态了。并且会像client发送RST信号。这就会导致client的这种半连接最后也会消失。这个可以通过tcpdump抓包得到(最好知道src这样看到的包比较集中)。

     

    TIME_WAIT处理方法

      实现的目标就是不要让处于TIME_WAIT的端口占满所有本地端口,导致没有新的本地端口用来创建新的客户端。
      1. 别让客户端的速率太快
      似乎上面的案例告诉我们别优化用力过猛,否则容易扯到蛋……将客户端请求的速率降下来就可以避免端时间占用大量的端口,吞吐量限制就是470tps或者235tps,具体根据系统TIME_WAIT默认时长决定,如果考虑到其他服务正常运行这个值还要保守一些才行;此外还需要注意,如果客户端和服务端增加了一层NAT或者L7负载均衡,那么这个限制可能会在负载均衡器上面;
      2. 客户端改成长连接的形式
      长连接效率高又不会产生大量TIME_WAIT端口。目前对我们来说还是不太现实的,虽然HTTP支持长连接,但是CGI调用应该是不可能的了,除非用之前的介绍的方式将CGI的请求转换成HTTP服务来实现。对于一般socket直连的程序来说,短连接改成长连接就需要额外的封装来标识完整请求在整个字节流中的起始位置,需要做一些额外的工作;
      3. SO_LINGER选项
      通常我们关闭socket的时候,即使该连接的缓冲区有数据要发送,close调用也会立即返回,TCP本身会尝试发送这些未发送出去的数据,只不过应用程序不知道也无法知道是否发送成功过了。如果我们将套接字设置SO_LINGER这个选项,并填写linger结构设置参数,就可以控制这种行为:
      如果linger结构的l_onoff==0,则linger选项就被关闭,其行为就和默认的close相同;如果打开,那么具体行为依据另外一个成员l_linger的值来确定:如果l_linger!=0,则内核会将当前close调用挂起,直到数据都发送完毕,或者设置的逗留时间超时返回,前者调用会返回0并且正常进入TIME_WAIT状态,后者调用会返回EWOULDBLOCK,所有未发送出去的数据可能会丢失(此处可能会向对端发送一个RST而快速关闭连接);如果l_linger==0,则直接将缓冲区中未发送的数据丢弃,且向对等实体发送一个RST,自己不经过TIME_WAIT状态立即关闭连接。
      我们都认为TIME_WAIT是TCP机制的正常组成部分,应用程序中不应该依赖设置l_linger=0这种机制避免TIME_WAIT。
      4. 修改系统参数
      (a). 增加本地端口范围,修改net.ipv4.ip_local_port_range,虽然不能解决根本问题但情况可以得到一定的缓解;
      (b). 缩短TIME_WAIT的时间。这个时长在书中描述到RFC推荐是2min,而BSD实现通常是30s,也就说明这个值是可以减小的,尤其我们用在内网通信的环境,数据包甚至都流不出路由器,所以根本不需要设置那么长的TIME_WAIT。这个很多资料说不允许修改,因为是写死在内核中的;也有说可以修改netfilter.ip_conntrack_tcp_timeout_time_wait(新版本nf_conntrack_tcp_timeout_time_wait)的,他们依赖于加载nf_conntract_ipv4模块,不过我试了一下好像不起作用。
      (c). 像之前在项目中推荐的,做出如下调整

    net.ipv4.tcp_tw_reuse = 1
    net.ipv4.tcp_timestamps=1
    net.ipv4.tcp_tw_recycle=1

           很多文献说这种设置是不安全的,所以在测试环境以外就别尝试了,因为这些选项还涉及到timestamp特性,我还不清楚什么回事,后面有时间再看什么吧。
      我们在开发服务端的时候,通常都会设置SO_REUSEADDR这个选项。其实像上面描述到的,该选项也牵涉到侦听socket端口处于TIME_WAIT的情况,设置这个选项将允许处于TIME_WAIT的端口进行绑定

     

    另外一文:

            记得以前面试的时候被面试官问起TIME_WAIT有什么痛点,当时只记得TCP三次握手、四次挥手之类的,至于其中的某个状态还真是记不起来,之前也没有过多关注过,还有对于拥塞控制的概念也比较模糊。

    TCP报文格式

    TCP大家都知道是什么东西,这个协议的具体报文格式如下:

    标志位

    1. URG:指示报文中有紧急数据,应尽快传送(相当于高优先级的数据)。
    2. PSH:为1表示是带有push标志的数据,指示接收方在接收到该报文段以后,应尽快将这个报文段交给应用程序,而不是在缓冲区排队。
    3. RST:TCP连接中出现严重差错(如主机崩溃),必须释放连接,在重新建立连接。
    4. FIN:发送端已完成数据传输,请求释放连接。
    5. SYN:处于TCP连接建立过程。 (Synchronize Sequence Numbers)
    6. ACK:确认序号标志,为1时表示确认号有效,为0表示报文中不含确认信息,忽略确认号字段。


    窗口

          滑动窗口大小,这个字段是接收端用来告知发送端自己还有多少缓冲区可以接受数据。于是发送端可以根据这个接收端的处理能力来发送数据,而不会导致接收端处理不过来。(以此控制发送端发送数据的速率,从而达到流量控制。)窗口大小时一个16bit字段,因而窗口大小最大为65535。

    头部长度(首部长度)

          由于TCP首部包含一个长度可变的选项和填充部分,所以需要这么一个值来指定这个TCP报文段到底有多长。或者可以这么理解:就是表示TCP报文段中数据部分在整个TCP报文段中的位置。该字段的单位是32位字,即:4个字节。TCP的滑动窗口大小实际上就是socket的接收缓冲区大小的字节数。

    选项和填充部分

          TCP报文的字段实现了TCP的功能,标识进程、对字节流拆分组装、差错控制、流量控制、建立和释放连接等。其最大长度可根据TCP首部长度进行推算。TCP首部长度用4位表示,那么选项部分最长为:(2^4-1)*(32/8)-20=40字节。

    三次握手

     


          最开始的时候客户端和服务器都是处于CLOSED状态。主动打开连接的为客户端,被动打开连接的是服务器。

          TCP服务器进程先创建传输控制块TCB,时刻准备接受客户进程的连接请求,此时服务器就进入了**LISTEN(监听)**状态;
          TCP客户进程也是先创建传输控制块TCB,然后向服务器发出连接请求报文,这是报文首部中的**同部位SYN=1,同时选择一个初始序列号 seq=x **,此时,TCP客户端进程进入了 SYN-SENT(同步已发送状态)状态。TCP规定,SYN报文段(SYN=1的报文段)不能携带数据,但需要消耗掉一个序号。
          TCP服务器收到请求报文后,如果同意连接,则发出确认报文。确认报文中应该 ACK=1,SYN=1,确认号是ack=x+1,同时也要为自己初始化一个序列号 seq=y,此时,TCP服务器进程进入了**SYN-RCVD(同步收到)**状态。这个报文也不能携带数据,但是同样要消耗一个序号。
          TCP客户进程收到确认后,还要向服务器给出确认。确认报文的ACK=1,ack=y+1,自己的序列号seq=x+1,此时,TCP连接建立,客户端进入**ESTABLISHED(已建立连接)**状态。TCP规定,ACK报文段可以携带数据,但是如果不携带数据则不消耗序号。
          当服务器收到客户端的确认后也进入ESTABLISHED状态,此后双方就可以开始通信了。
          三次握手主要目的是:信息对等和防止超时。防止超时导致脏连接。如果使用的是两次握手建立连接,假设有这样一种场景,客户端发送了第一个请求连接并且没有丢失,只是因为在网络结点中滞留的时间太长了,由于TCP的客户端迟迟没有收到确认报文,以为服务器没有收到,此时重新向服务器发送这条报文,此后客户端和服务器经过两次握手完成连接,传输数据,然后关闭连接。此时此前滞留的那一次请求连接,网络通畅了到达了服务器,这个报文本该是失效的,但是,两次握手的机制将会让客户端和服务器再次建立连接,这将导致不必要的错误和资源的浪费。如果采用的是三次握手,就算是那一次失效的报文传送过来了,服务端接受到了那条失效报文并且回复了确认报文,但是客户端不会再次发出确认。由于服务器收不到确认,就知道客户端并没有请求连接。

    四次挥手


          数据传输完毕后,双方都可释放连接。最开始的时候,客户端和服务器都是处于ESTABLISHED状态,然后客户端主动关闭,服务器被动关闭。

    1. 客户端进程发出连接释放报文,并且停止发送数据。释放数据报文首部,FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入FIN-WAIT-1(终止等待1)状态。 TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。
    2. 服务器收到连接释放报文,发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号seq=v,此时,服务端就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。
    3. 客户端收到服务器的确认请求后,此时,客户端就进入**FIN-WAIT-2(终止等待2)**状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据)。
    4. 服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1,ack=u+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时,服务器就进入了**LAST-ACK(最后确认)**状态,等待客户端的确认。
    5. 客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。
    6. 服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。

          TIME_WAIT:主动要求关闭的机器表示收到了对方的FIN报文,并发送出了ACK报文,进入TIME_WAIT状态,等2MSL后即可进入到CLOSED状态。如果FIN_WAIT_1状态下,同时收到待FIN标识和ACK标识的报文时,可以直接进入TIME_WAIT状态,而无需经过FIN_WAIT_2状态。

          CLOSE_WAIT:被动关闭的机器收到对方请求关闭连接的FIN报文,在第一次ACK应答后,马上进入CLOSE_WAIT状态。这种状态其实标识在等待关闭,并且通知应用发送剩余数据,处理现场信息,关闭相关资源。

    为什么客户端最后还要等待2MSL?

          MSL(Maximum Segment Lifetime),TCP允许不同的实现可以设置不同的MSL值。**第一,保证客户端发送的最后一个ACK报文能够到达服务器,因为这个ACK报文可能丢失。**站在服务器的角度看来,我已经发送了FIN+ACK报文请求断开了,客户端还没有给我回应,应该是我发送的请求断开报文它没有收到,于是服务器又会重新发送一次,而客户端就能在这个2MSL时间段内收到这个重传的报文,接着给出回应报文,并且会重启2MSL计时器。如果客户端收到服务端的FIN+ACK报文后,发送一个ACK给服务端之后就“自私”地立马进入CLOSED状态,可能会导致服务端无法确认收到最后的ACK指令,也就无法进入CLOSED状态,这是客户端不负责任的表现。**第二,防止失效请求。**防止类似与“三次握手”中提到了的“已经失效的连接请求报文段”出现在本连接中。客户端发送完最后一个确认报文后,在这个2MSL时间中,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失。这样新的连接中不会出现旧连接的请求报文。

           在TIME_WAIT状态无法真正释放句柄资源,在此期间,Socket中使用的本地端口在默认情况下不能再被使用。该限制对于客户端机器来说是无所谓的,但对于高并发服务器来说,会极大地限制有效连接的创建数量,称为性能瓶颈。所以建议将高并发服务器TIME_WAIT超时时间调小。RFC793中规定MSL为2分钟。但是在当前的高速网络中,2分钟的等待时间会造成资源的极大浪费,在高并发服务器上通常会使用更小的值。
          在服务器上通过变更/etc/sysctl.conf文件来修改该默认值net.ipv4.tcp_fin_timout=30(建议小30s)。修改完之后执行 /sbin/sysctl -p 让参数生效。
          通过如下命令查看各连接状态的技术情况:

    [root@node1 ~]# netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
    TIME_WAIT 63
    ESTABLISHED 13

    为什么建立连接是三次握手,关闭连接确是四次挥手呢?

          建立连接的时候, 服务器在LISTEN状态下,收到建立连接请求的SYN报文后,把ACK和SYN放在一个报文里发送给客户端。 而关闭连接时,服务器收到对方的FIN报文时,仅仅表示对方不再发送数据了但是还能接收数据,而自己也未必全部数据都发送给对方了,所以己方可以立即关闭,也可以发送一些数据给对方后,再发送FIN报文给对方来表示同意现在关闭连接,因此,己方ACK和FIN一般都会分开发送,从而导致多了一次。

    滑动窗口

          TCP滑动窗口技术通过动态改变窗口大小来调节两台主机间数据传输。每个TCP/IP主机支持全双工数据传输,因此TCP有两个滑动窗口:一个用于接收数据,另一个用于发送数据。TCP使用肯定确认技术,其确认号指的是下一个所期待的字节。 假定发送方设备以每一次三个数据包的方式发送数据,也就是说,窗口大小为3。发送方发送序列号为1、2、3的三个数据包,接收方设备成功接收数据包,用序列号4确认。发送方设备收到确认,继续以窗口大小3发送数据。当接收方设备要求降低或者增大网络流量时,可以对窗口大小进行减小或者增加,本例降低窗口大小为2,每一次发送两个数据包。当接收方设备要求窗口大小为0,表明接收方已经接收了全部数据,或者接收方应用程序没有时间读取数据,要求暂停发送。发送方接收到携带窗口号为0的确认,停止这一方向的数据传输。当链路变好了或者变差了这个窗口还会发生变话,并不是第一次协商好了以后就永远不变了。

          滑动窗口协议,是TCP使用的一种流量控制方法。该协议允许发送方在停止并等待确认前可以连续发送多个分组。由于发送方不必每发一个分组就停下来等待确认,因此该协议可以加速数据的传输。 只有在接收窗口向前滑动时(与此同时也发送了确认),发送窗口才有可能向前滑动。收发两端的窗口按照以上规律不断地向前滑动,因此这种协议又称为滑动窗口协议。

          流量控制:端到端,接收端的应用层处理速度决定和网速无关,由接收端返回的rwnd控制

    cwnd:发送端窗口( congestion window )
    rwnd:接收端窗口(receiver window)

    拥塞控制

          拥塞控制: 发送端主动控制cwnd,有慢启动(从cwnd初始为1开始启动,指数启动),拥塞避免(到达ssthresh后,为了避免拥塞开始尝试线性增长),快重传(接收方每收到一个报文段都要回复一个当前最大连续位置的确认,发送方只要一连收到三个重复确认就知道接收方丢包了,快速重传丢包的报文,并TCP马上把拥塞窗口 cwnd 减小到1),快恢复(直接从ssthresh线性增长)。

          如果网络上的延时突然增加,那么TCP对这个事作出的应对只有重传数据,但是重传会导致网络的负担更重,于是会导致更大的延迟以及更多的丢包,于是这个情况就会进入恶性循环被不断地放大。试想一下,如果一个网络内有成千上万的TCP连接都这么行事,那么马上就会形成“网络风暴”,TCP这个协议就会拖垮整个网络。所以TCP不能忽略网络上发生的事情,而无脑地一个劲地重发数据,对网络造成更大的伤害。对此TCP的设计理念是:TCP不是一个自私的协议,当拥塞发生的时候,要做自我牺牲。就像交通阻塞一样,每个车都应该把路让出来,而不要再去抢路了。

    慢启动

          只有在TCP连接建立和网络出现超时时才使用。每经过一个传输轮次,拥塞窗口 cwnd 就加倍。一个传输轮次所经历的时间其实就是往返时间RTT。不过“传输轮次”更加强调:把拥塞窗口cwnd所允许发送的报文段都连续发送出去,并收到了对已发送的最后一个字节的确认。另外,慢开始的“慢”并不是指cwnd的增长速率慢,而是指在TCP开始发送报文段时先设置cwnd=1,使得发送方在开始时只发送一个报文段(目的是试探一下网络的拥塞情况),然后再逐渐增大cwnd。

          为了防止拥塞窗口cwnd增长过大引起网络拥塞,还需要设置一个慢开始门限ssthresh状态变量(如何设置ssthresh)。慢开始门限ssthresh的用法如下:

    1. 当 cwnd < ssthresh 时,使用上述的慢开始算法。
    2. 当 cwnd > ssthresh 时,停止使用慢开始算法而改用拥塞避免算法。
    3. 当 cwnd = ssthresh 时,既可使用慢开始算法,也可使用拥塞控制避免算法。

          拥塞避免算法:让拥塞窗口cwnd缓慢地增大,即每经过一个往返时间RTT就把发送方的拥塞窗口cwnd加1,而不是加倍。这样拥塞窗口cwnd按线性规律缓慢增长,比慢开始算法的拥塞窗口增长速率缓慢得多。

          无论在慢开始阶段还是在拥塞避免阶段,只要发送方判断网络出现拥塞(其根据就是没有收到确认),就要把慢开始门限ssthresh设置为出现拥塞时的发送方窗口值的一半(但不能小于2)。然后把拥塞窗口cwnd重新设置为1,执行慢开始算法。这样做的目的就是要迅速减少主机发送到网络中的分组数,使得发生拥塞的路由器有足够时间把队列中积压的分组处理完毕。

          当TCP连接进行初始化时,把拥塞窗口cwnd置为1。前面已说过,为了便于理解,图中的窗口单位不使用字节而使用报文段的个数。慢开始门限的初始值设置为16个报文段,即 cwnd = 16 。
           在执行慢开始算法时,拥塞窗口 cwnd 的初始值为1。以后发送方每收到一个对新报文段的确认ACK,就把拥塞窗口值另1,然后开始下一轮的传输(图中横坐标为传输轮次)。因此拥塞窗口cwnd随着传输轮次按指数规律增长。当拥塞窗口cwnd增长到慢开始门限值ssthresh时(即当cwnd=16时),就改为执行拥塞控制算法,拥塞窗口按线性规律增长。
          假定拥塞窗口的数值增长到24时,网络出现超时(这很可能就是网络发生拥塞了)。更新后的ssthresh值变为12(即变为出现超时时的拥塞窗口数值24的一半),拥塞窗口再重新设置为1,并执行慢开始算法。当cwnd=ssthresh=12时改为执行拥塞避免算法,拥塞窗口按线性规律增长,每经过一个往返时间增加一个MSS的大小。
          强调:“拥塞避免”并非指完全能够避免了拥塞。利用以上的措施要完全避免网络拥塞还是不可能的。“拥塞避免”是说在拥塞避免阶段将拥塞窗口控制为按线性规律增长,使网络比较不容易出现拥塞。

           如果发送方设置的超时计时器时限已到但还没有收到确认,那么很可能是网络出现了拥塞,致使报文段在网络中的某处被丢弃。这时,TCP马上把拥塞窗口 cwnd 减小到1,并执行慢开始算法,同时把慢开始门限值ssthresh减半。这是不使用快重传的情况。快重传算法首先要求接收方每收到一个失序的报文段后就立即发出重复确认(为的是使发送方及早知道有报文段没有到达对方)而不要等到自己发送数据时才进行捎带确认。

           接收方收到了M1和M2后都分别发出了确认。现在假定接收方没有收到M3但接着收到了M4。显然,接收方不能确认M4,因为M4是收到的失序报文段。根据可靠传输原理,接收方可以什么都不做,也可以在适当时机发送一次对M2的确认。但按照快重传算法的规定,接收方应及时发送对M2的重复确认,这样做可以让发送方及早知道报文段M3没有到达接收方。发送方接着发送了M5和M6。接收方收到这两个报文后,也还要再次发出对M2的重复确认。这样,发送方共收到了接收方的四个对M2的确认,其中后三个都是重复确认。快重传算法还规定,发送方只要一连收到三个重复确认就应当立即重传对方尚未收到的报文段M3,而不必继续等待M3设置的重传计时器到期。由于发送方尽早重传未被确认的报文段,因此采用快重传后可以使整个网络吞吐量提高约20%。

          与快重传配合使用的还有快恢复算法,其过程有以下两个要点:

    1. 当发送方连续收到三个重复确认,就执行“乘法减小”算法,把慢启动门限ssthresh减半。这是为了预防网络发生拥塞。请注意:接下去不执行慢开始算法。
    2. 由于发送方现在认为网络很可能没有发生拥塞,因此与慢开始不同之处是现在不执行慢开始算法(即拥塞窗口cwnd现在不设置为1),而是把cwnd值设置为慢开始门限ssthresh减半后的数值,然后开始执行拥塞避免算法(“加法增大”),使拥塞窗口缓慢地线性增大。

          上图给出了快重传和快恢复的示意图,并标明了“TCP Reno版本”。区别:新的 TCP Reno 版本在快重传之后采用快恢复算法而不是采用慢开始算法。

    发送方窗口的上限值 = Min [ rwnd, cwnd ]

    当rwnd < cwnd 时,是接收方的接收能力限制发送方窗口的最大值。
    当cwnd < rwnd 时,则是网络的拥塞限制发送方窗口的最大值。

    差错控制

    TCP使用差错控制来提供可靠性。差错控制包括以下的一些机制:检测和重传受到损伤的报文段、重传丢失的报文段、保存失序到达的报文段直至缺失的报文到期,以及检测和丢弃重复的报文段。TCP通过三个简单的工具来完成其差错控制:检验和、确认以及超时。


    参考:

    https://hiddenpps.blog.csdn.net/article/details/84001583 

     

    请我喝咖啡

    如果觉得文章写得不错,能对你有帮助,可以扫描我的微信二维码请我喝咖啡哦~~哈哈~~

     

    展开全文
  • TIME_WAIT和CLOSE_WAIT的区别

    千次阅读 2019-04-18 14:35:25
    系统上线之后,通过如下语句查看服务器时,发现有不少TIME_WAIT和CLOSE_WAIT。 netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}' TIME_WAIT 297 ESTABLISHED 53 CLOSE_WAIT 5 解释 TIME_...

    系统上线之后,通过如下语句查看服务器时,发现有不少TIME_WAIT和CLOSE_WAIT。

    netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}' 
    

    TIME_WAIT 297
    ESTABLISHED 53
    CLOSE_WAIT 5

    解释
    TIME_WAIT:表示主动关闭,通过优化系统内核参数可容易解决。
    CLOSE_WAIT:表示被动关闭,需要从程序本身出发。
    ESTABLISHED:表示正在通信

    TIME_WAIT(通过优化系统内核参数可容易解决)

    TIME_WAIT是主动关闭连接的一方保持的状态,对于服务器来说它本身就是“客户端”,在完成一个爬取任务之后,它就会发起主动关闭连接,从而进入TIME_WAIT的状态,然后在保持这个状态2MSL(max segment lifetime)时间之后,彻底关闭回收资源。为什么要这么做?明明就已经主动关闭连接了为啥还要保持资源一段时间呢?这个是TCP/IP的设计者规定的,主要出于以下两个方面的考虑:
    1.防止上一次连接中的包,迷路后重新出现,影响新连接(经过2MSL,上一次连接中所有的重复包都会消失)
    2.可靠的关闭TCP连接。在主动关闭方发送的最后一个 ack(fin) ,有可能丢失,这时被动方会重新发fin, 如果这时主动方处于 CLOSED 状态 ,就会响应 rst 而不是 ack。所以主动方要处于 TIME_WAIT 状态,而不能是 CLOSED 。另外这么设计TIME_WAIT 会定时的回收资源,并不会占用很大资源的,除非短时间内接受大量请求或者受到攻击。
    解决方案很简单,通过修改/etc/sysctl.conf文件,服务器能够快速回收和重用那些TIME_WAIT的资源

    #表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭    
    net.ipv4.tcp_syncookies = 1    
    #表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭    
    net.ipv4.tcp_tw_reuse = 1    
    #表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭    
    net.ipv4.tcp_tw_recycle = 1  
    #表示如果套接字由本端要求关闭,这个参数决定了它保持在FIN-WAIT-2状态的时间    
    net.ipv4.tcp_fin_timeout=30  
    

    生效,如下命令

    /sbin/sysctl -p  
    

    CLOSE_WAIT(需要从程序本身出发)

    TCP状态转移要点

       TCP协议规定,对于已经建立的连接,网络双方要进行四次握手才能成功断开连接,如果缺少了其中某个步骤,将会使连接处于假死状态,连接本身占用的资源不会被释放。网络服务器程序要同时管理大量连接,所以很有必要保证无用连接完全断开,否则大量僵死的连接会浪费许多服务器资源.
    
        客户端TCP状态迁移: CLOSED->SYN_SENT->ESTABLISHED->FIN_WAIT_1->FIN_WAIT_2->TIME_WAIT->CLOSED
        服务器TCP状态迁移:CLOSED->LISTEN->SYN收到->ESTABLISHED->CLOSE_WAIT->LAST_ACK->CLOSED
        
        但是CLOSE_WAIT就不一样了,如果一直保持在CLOSE_WAIT状态,那么只有一种情况,就是在对方关闭连接之后服务器程序自己没有进一步发出ack信号。换句话说,就是在对方连接关闭之后,程序里没有检测到,或者程序压根就忘记了这个时候需要关闭连接,于是这个资源就一直被程序占着。个人觉得这种情况,通过服务器内核参数也没办法解决,服务器对于程序抢占的资源没有主动回收的权利,除非终止程序运行。
    
       什么情况下,连接处于CLOSE_WAIT状态呢?
    
       答案一:在被动关闭连接情况下,在已经接收到FIN,但是还没有发送自己的FIN的时刻,连接处于CLOSE_WAIT状态。通常来讲,CLOSE_WAIT状态的持续时间应该很短,正如SYN_RCVD状态。但是在一些特殊情况下,就会出现连接长时间处于CLOSE_WAIT状态的情况。
    
       答案二:出现大量close_wait的现象,主要原因是某种情况下对方关闭了socket链接,但是我方忙与读或者写,没有关闭连接。代码需要判断socket,一旦读到0,断开连接,read返回负,检查一下errno,如果不是AGAIN,就断开连接。
       
       转载https://dengqsintyt.iteye.com/blog/2086485
    
    展开全文
  • TIME_WAIT过多危害 网络情况不好时,如果主动方无TIME_WAIT等待,关闭前个连接后,主动方与被动方又建立起新的TCP连接,这时被动方重传或延时过来的FIN包过来后会直接影响新的TCP连接; 同样网络情况不好并且无TIME...
  • 先给出四次挥手过程中C/S的状态变化示意图。有了图理解起来就容易许多。...TIME_WAIT 表示主动关闭,CLOSE_WAIT 表示被动关闭。 CLOSE_WAIT状态的生成原因 首先我们知道,如果我们的服务器程序APACHE处于CLOS
  • 命令查看TIME_WAIT连接数 netstat -ae|grep "TIME_WAIT" |wc -l 早上登陆服务器的时候输入netstat -an|grep mysql 发现存在大量TIME_WAIT状态的连接 tcp 0 0 127.0.0.1:3306 127.0.0.1:41378 T
  • TCP四次挥手之FIN_WAIT_2和CLOSE_WAIT,TIME_WAIT以及LAST_ACK的细节
  •   TIME_WAIT是TCP状态转换中的一个非常重要的状态,TIME_WAIT状态的或多或少会极大的影响客户端与服务端的性能,在真实的应用场景中往往需要根据实际需求来对TIME_WAIT进行优化,因此在学习过程中有必要掌握TIME_...
  • 最近项目中客户端连接服务器时死活都连不上了,服务器查询出现大量time_wait状态,于是迫在眉睫解决问题的着手点是如何避免出现大量的tima_wait状态。下面我将为大家介绍解决的方法: 下面让我们先来了温习下tcp的...
  • TCP通信过程中time_wait,和close_wait产生过多的原因和解决方法1. time_wait过多产生原因2.time_wait过多解决方法3.close_wait过多原因4.close_wait过多的解决方案 1. time_wait过多产生原因   正常的TCP客户端...
  • 文章目录存在close_wait的原因和解决办法存在FIN_WAIT2的原因和解决办法存在TIME_WAIT的原因和解决办法处理这类问题的实用命令 存在close_wait的原因和解决办法 close_wait这个状态存在于服务端,当服务端发送FIN...
  • 服务器 TIME_WAIT和CLOSE_WAIT

    千次阅读 2016-11-26 16:10:50
    1、查询服务器端口占用情况 命令行: netstat -ano 》D:\YSH.txtESTABLISHED:表示...time_wait以及close_wait过多可能导致系统端口用尽的情况,无法正常建立TCP 连接。tcp 状态变化图如下:2、(通过优化系统内核参
  • 服务器TIME_WAIT和CLOSE_WAIT详解和解决办法

    千次阅读 热门讨论 2017-11-28 16:09:14
    服务器TIME_WAIT和CLOSE_WAIT详解和解决办法
  • 今天登陆服务器想查看一个端口的占用情况,...time_wait的作用 1 2 3 4 5 6 7 8 9 10 TIME_WAIT状态存在的理由: 1)可靠地实现TCP全双工连接的终止 ...
  • TIME_WAIT和CLOSE_WAIT状态区别

    千次阅读 2016-04-27 12:10:33
    在服务器的日常维护过程中,会经常用到下面的命令: netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in ...TIME_WAIT 814 CLOSE_WAIT 1 FIN_WAIT1 1 ESTABLISHED 634 SYN_RECV 2 LAST_ACK 1 常用的三个状态是:...
  • 解决进程的time_wait状态 查看进程连接状态 #netstat -an|awk ‘/tcp/ {print $6}’|sort|uniq -c 16 CLOSING 130 ESTABLISHED 298 FIN_WAIT1 13 FIN_WAIT2 9 LAST_ACK 7 LISTEN 103 SYN_RECV 5204 TIME_WAIT 状...
  • 你遇到过TIME_WAIT的问题吗? 我相信很多都遇到过这个问题。一旦有用户在喊:网络变慢了。第一件事情就是,netstat -a | grep TIME_WAIT | wc -l 一下,哎呀妈呀,几千个TIME_WAIT。 然后,做的第一件事情...
  • 我们平常工作中遇到的,除了针对SYN的拒绝服务攻击,如果有异常,大概率是TIME_WAIT和CLOSE_WAIT的问题。 TIME_WAIT一般通过优化内核参数能够解决;CLOSE_WAIT一般是由于程序编写不合理造成的,更应该引起开发者注意...
  • 系统上线之后,通过如下语句查看服务器时,发现有不少TIME_WAIT和CLOSE_WAIT。   netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'   打印显示如下: TIME_WAIT 297 ESTABLISHED 53 CLOSE_...
  • 先来看下一台生产环境中的各种tcp状态的连接数: netstat -n| awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}' LAST_ACK 6 SYN_RECV 77 CLOSE_WAIT 5793 ESTABLISHED 8540 ...TIME_WAIT 22...
  • 修改Time_Wait参数的方法 (在服务端修改) Windows下在HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/Tcpip/Parameters,添加名为TcpTimedWaitDelay的 DWORD键,设置为30,以缩短TIME_WAIT的等待时间  ...
  • Windows中CLOSE_WAITTIME_WAIT网络连接

    千次阅读 2016-08-19 16:00:51
    Windows下写网络程序时遇到很多TIME_WAIT、CLOSE_WAIT的网络连接,可以通过调整 注册表中的TCP参数来处理: C:\Users\user1>netstat -ano | findstr 110:80 TCP 192.168.1.110:80 192.168.1.7:33083 TIME_...
  • [root@localhost ~]#netstat -an|awk ‘/tcp/ {print $6}’|sort|uniq -c 16 CLOSING 130 ESTABLISHED 298 FIN_WAIT1 13 FIN_WAIT2 ...5204 TIME_WAIT 状态:描述 CLOSED:无连接是活动的或正在进行 LIS...
  • TCP协议中的TIME_WAIT详细说明

    千次阅读 2020-04-26 11:38:43
    文章目录4.3设置TIME_WAIT状态的目的4.3.1 实现TCP全双工连接的关闭4.3.2 使过时的重复报文段失效4.3.3 TIME_WAIT状态的自结束4.3.4 TIME_WAIT状态的影响(补充) 4.3设置TIME_WAIT状态的目的 TIME_WAIT状态是TCP中...
  • TCP连接和 time_wait、close_waite

    万次阅读 2018-05-16 09:51:25
    TCP连接和 time_wait、close_waite tags:time_wait close_waite RST TCP 引言:前两天朋友公司的服务器垮掉了,最后查出的原因是发现大量的time_wait网络状态。被问起来time_wait是什么,当时就简单的给解释了...
  • 下面接着《详解TCPIP断开连接后的Time-wait状态》一文的内容,我们讨论下socket在断开连接中的另外三种状态FIN_WAIT1、FIN_WAIT2状态和CLOSE_WAIT状态。 首先启动socket服务端,然后用客户端进行连接,用netstat –...
  • Linux上的TIME_WAIT和tcp_fin_timeout

    千次阅读 2018-04-17 09:29:37
    当Linux服务器的TIME_WAIT过多时, 通常会想到去修改参数降低TIME_WAIT时长, 以减少TIME_WAIT数量,但Linux并没有提供这样的接口, 除非重新编译内核。...#define TCP_TIMEWAIT_LEN (60*HZ) /* how long ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 462,685
精华内容 185,074
关键字:

time_wait