连接(为什么不能连接外网会能Time

博客分类:
面试时看到应聘者简历中写精通网络,TCP编程,我常问一个问题,TCP建立连接需要几次握手?95%以上的应聘者都能答对是3次。问TCP断开连接需要几次握手,70%的应聘者能答对是4次通讯。再问CLOSE_WAIT,TIME_WAIT是什么状态,怎么产生的,对服务有什么影响,如何消除?有一部分同学就回答不上来。不是我扣细节,而是在通讯为主的前端服务器上,必须有能力处理各种TCP状态。比如统计在本厂的一台前端机上高峰时间TCP连接的情况,统计命令:
netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
除了ESTABLISHED,可以看到连接数比较多的几个状态是:FIN_WAIT1, TIME_WAIT, CLOSE_WAIT, SYN_RECV和LAST_ACK;下面的文章就这几个状态的产生条件、对系统的影响以及处理方式进行简单描述。
TCP状态如下图所示:
可能有点眼花缭乱?再看看这个时序图
下面看下大家一般比较关心的三种TCP状态
服务端收到建立连接的SYN没有收到ACK包的时候处在SYN_RECV状态。有两个相关系统配置:
1,net.ipv4.tcp_synack_retries :INTEGER
对于远端的连接请求SYN,内核会发送SYN + ACK数据报,以确认收到上一个 SYN连接请求包。这是所谓的三次握手( threeway handshake)机制的第二个步骤。这里决定内核在放弃连接之前所送出的 SYN+ACK 数目。不应该大于255,默认值是5,对应于180秒左右时间。通常我们不对这个值进行修改,因为我们希望TCP连接不要因为偶尔的丢包而无法建立。
2,net.ipv4.tcp_syncookies
一般服务器都会设置net.ipv4.tcp_syncookies=1来防止SYN Flood攻击。假设一个用户向服务器发送了SYN报文后突然死机或掉线,那么服务器在发出SYN+ACK应答报文后是无法收到客户端的ACK报文的(第三次握手无法完成),这种情况下服务器端一般会重试(再次发送SYN+ACK给客户端)并等待一段时间后丢弃这个未完成的连接,这段时间的长度我们称为SYN Timeout,一般来说这个时间是分钟的数量级(大约为30秒-2分钟)。
这些处在SYNC_RECV的TCP连接称为半连接,并存储在内核的半连接队列中,在内核收到对端发送的ack包时会查找半连接队列,并将符合的requst_sock信息存储到完成三次握手的连接的队列中,然后删除此半连接。大量SYNC_RECV的TCP连接会导致半连接队列溢出,这样后续的连接建立请求会被内核直接丢弃,这就是SYN Flood攻击。
能够有效防范SYN Flood攻击的手段之一,就是SYN Cookie。SYN Cookie原理由D. J. Bernstain和 Eric Schenk发明。SYN Cookie是对TCP服务器端的三次握手协议作一些修改,专门用来防范SYN Flood攻击的一种手段。它的原理是,在TCP服务器收到TCP SYN包并返回TCP SYN+ACK包时,不分配一个专门的数据区,而是根据这个SYN包计算出一个cookie值。在收到TCP ACK包时,TCP服务器在根据那个cookie值检查这个TCP ACK包的合法性。如果合法,再分配专门的数据区进行处理未来的TCP连接。
观测服务上SYN_RECV连接个数为:7314,对于一个高并发连接的通讯服务器,这个数字比较正常。CLOSE_WAIT
发起TCP连接关闭的一方称为client,被动关闭的一方称为server。被动关闭的server收到FIN后,但未发出ACK的TCP状态是CLOSE_WAIT。出现这种状况一般都是由于server端代码的问题,如果你的服务器上出现大量CLOSE_WAIT,应该要考虑检查代码。
根据TCP协议定义的3次握手断开连接规定,发起socket主动关闭的一方 socket将进入TIME_WAIT状态。TIME_WAIT状态将持续2个MSL(Max Segment Lifetime),在Windows下默认为4分钟,即240秒。TIME_WAIT状态下的socket不能被回收使用. 具体现象是对于一个处理大量短连接的服务器,如果是由服务器主动关闭客户端的连接,将导致服务器端存在大量的处于TIME_WAIT状态的socket, 甚至比处于Established状态下的socket多的多,严重影响服务器的处理能力,甚至耗尽可用的socket,停止服务。
为什么需要TIME_WAIT?TIME_WAIT是TCP协议用以保证被重新分配的socket不会受到之前残留的延迟重发报文影响的机制,是必要的逻辑保证。
和TIME_WAIT状态有关的系统参数有一般由3个,本厂设置如下:
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_fin_timeout,默认60s,减小fin_timeout,减少TIME_WAIT连接数量。
net.ipv4.tcp_tw_reuse = 1表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;
net.ipv4.tcp_tw_recycle = 1表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。
下一篇: “通讯系统经验谈【二】解读内核参数 - socket/文件句柄资源限制参数”会向大家介绍本厂网络相关设置中的其他选项,特别是系统资源限制相关的内核配置。
浏览 11054
浏览: 259127 次
来自: 北京
Java并发编程和高并发解决方案视频课程网盘地址:https: ...
RingBuffer不存在生产覆盖未消费的数据,或者消费已经消 ...
完全没看懂诶
楼主 为什么是1.27F 求解释
有个疑问,请教下:扩容问题之一:如果不降低命中率?如果使用为垂 ...
(window.slotbydup=window.slotbydup || []).push({
id: '4773203',
container: s,
size: '200,200',
display: 'inlay-fix'7.4k 人阅读
标签:至少1个,最多5个
在构建基于 TCP 协议的 C/S 系统的时候,经常会因为一些简单的错误而导致严重的影响系统的可扩展性。 其中一些错误是因为对TIME_WAIT状态不理解导致的。 在本文中,我将会讲解为什么要存在TIME_WAIT 状态,它的存在所造成的一些问题以及如何解决这些问题。
TIME_WAIT在 TCP 的状态流程图中是一个很容易被误解的状态。它是表示socket可以进入和留存相当长一段时间的状态,如果你的系统中有很多 socket 处于TIME_WAIT状态,那么当你需要创建新的 socket 连接的时候可能会受到影响,这也会影响到你的程序的扩展性。很多人对于如何以及为何 socket 关闭连接的时候要在第一个地方以TIME_WAIT状态结束感到困惑,这里没有什么神秘的,从下面的 TCP 状态流程图中可以看出,TIME_WAIT状态是TCP 客户端断开连接之后的最终状态。
尽管状态图显示的 TIME_WAIT 是客户端结束连接的最终状态,但这并不是说一定是客户端的结束状态才是TIME_WAIT,实际上,这是主动关闭连接(active close)的设备(不管是服务端还是客户端)的最终状态。什么是主动关闭连接呢?
如果一个 TCP 的终端(peer)首先对这个连接调用 Close() 关闭连接,就说这个终端发起了主动关闭。在很多协议和 C/S 系统中,这是指的客户端。在 HTTP 和 FTP 服务器上,通常指的是服务端。实际的终端以 TIME_WAIT状态终止的事件序列图如下所示。
现在我们已经知道socket如何以TIME_WAIT状态结束连接,接下来理解为什么要存在这个状态以及为什么它可能造成一些潜在的问题是非常有用的。
TIME_WAIT通常也称为2MSL等待状态。这是因为切换到TIME_WAIT状态的socket会保持2倍的最大段生命周期(MSL)的延迟时间。MSL是TCP协议数据报中,任意一段数据在网络上被丢弃之前保持可用的最大时间。这个时间使用用于传输TCP段的IP数据报中的TTL字段进行设置,不同的实现为MSL设置了不同的值,通常为 30s,一分钟 或者 两分钟 。 指出MSL为 2分钟 ,在Windows下默认为该值,当然,可以通过修改注册表项设置该值。
之所以TIME_WAIT能够影响系统的扩展性是因为在一个TCP连接中,一个Socket如果关闭的话,它将保持TIME_WAIT状态大约 4分钟 。如果很多连接快速的打开和关闭的话,系统中处于TIME_WAIT状态的socket将会积累很多,你可以使用netstat命令查看处于TIME_WAIT状态的socket。由于本地端口数量的限制,同一时间只有有限数量的socket连接可以建立,如果太多的socket处于TIME_WAIT状态,你会发现,由于用于新建连接的本地端口太缺乏,将会很难再建立新的对外连接。 但是为什么要有TIME_WAIT状态呢?
对于TIME_WAIT的存在,有两个理由。一个原因是为了防止一个连接中延迟的数据段会被后序的连接错误的解析。当一个连接处于2MSL状态的时候,任何到达的数据段都将会被丢弃。
在上图中,我们有从终端1到终端2的两个连接。在每个连接中,每个终端的地址和端口是相同的。第一个主动关闭的连接是由终端2主动发起的。如果终端2没有保持在TIME_WAIT状态足够长的时间以确保先前的连接中所有的数据段(每个数据段都有自己相应的序列号)都已经不可用了的话,可能会错误的成为第二个连接的一部分。
注意的是,延迟的数据段一般不会像这里这样造成问题。首先,每个终端的地址和端口号必须是相同的,这一点是可能性很小的,因为客户端的端口号通常由操作系统自动从可用端口范围中任意选择端口,并且在不同的连接中该端口通常是不同的。其次,延迟的片段的序列号需要在第二个连接中是可用的,这也是不太可能的。但是如果一旦这两个条件同时发生,TIME_WAIT状态可以防止新链接的数据出现问题。
第二个原因是为了实现TCP全双工连接的终止可靠性。如果来自终端2的最后一个ACK被丢弃,那么终端1将会重新发送最后的FIN,如果这时候终端2的连接状态已经转变到了CLOSED,那么唯一的响应将会是发送一个RST告诉它重发FIN是不被期望的,这样的结果会导致尽管所有的数据都已经正确的传输,但是终端1还是会接收到一个错误消息。
不幸的是有些操作系统对TIME_WAIT的实现太过简单(slightly naive)。只有在一个连接中完全匹配的socket(一个连接使用客户端地址,端口,服务端地址,端口进行标识)才需要被TIME_WAIT保护,以减少TIME_WAIT造成的开销。然而,某些操作系统采用了更加严格的限制,并且防止重用处于TIME_WAIT状态的连接所包含的本地端口号。如果有太多的socket结束后处于TIME_WAIT状态的话,由于没有足够的新的本地端口分配给程序,因此无法建立新的对外的连接。
Windows下并不是这样做的,它只防止完全匹配的处于TIME_WAIT状态的出站连接的建立。
入站连接很少会被TIME_WAIT影响。虽然与客户端一样,服务器端主动关闭的连接会进入TIME_WAIT状态,但是服务端监听的端口并不会防止新建的入站连接请求的建立。在Windows下,服务器正在监听的知名端口可以作为后续接受的连接的一部分,如果要从一个远端地址和端口与当前正在处于TIME_WAIT状态的本地地址和端口建立新的连接,那么只要新的连接的序号大于当前处于TIME_WAIT状态的连接的最后的序号,连接就可以建立。但是,累积在服务端的处于TIME_WAIT状态的连接可能会影响性能和资源的使用,因为处于TIME_WAIT状态的连接最终都会超时,这就需要服务器对超时进行处理,并且在TIME_WAIT状态结束之前都会占用服务器的资源(少量)。
由于本地端口的缺乏,TIME_WAIT的存在影响的是出站连接的建立,这些本地端口由操作系统进行自动的分配,因此,优化的方法是增加本地端口的范围,在Windows下,你可以调整
注册表项。注意的是,很多Windows系统下默认的端口范围比较小,大约4000个左右,这对很多客户端服务器系统来说太少了。
虽然可以减少socket在TIME_WAIT状态花费的时间,但通常情况下这都是不会起到什么实际的帮助的。TIME_WAIT只会在服务器建立了很多连接并且主动关闭的情况下会产生影响,调整2MSL的时间只会让服务器可以建立更多的连接并且在给定的时间内关闭,所以你必须继续调整2MSL的时间更低以至于该值太小,导致遇到一些由于延迟片段成为后序连接的一部分而产生的问题,当然,这只会在连接到同一个远端地址和端口号并且非常频繁的使用本地端口,或者是你连接到同样的远程地址和端口,并且绑定了固定的本地端口的时候出现。
修改2MSL的值通常是机器全局的配置修改。你可以在socket级别使用SO_REUSEADDR socket选项解决TIME_WAIT的问题,这使得即使一个有着同样地址和端口的socket存在,也可以创建一个新的socket,新的socket最终将会劫持旧的socket。你可以使用SO_REUSEADDR选项,在一个有着同样端口的socket已经处于TIME_WAIT状态的时候创建新的socket,但这样做可能会造成一些问题,比如拒绝服务攻击或者数据窃取。在Windows平台下,有另一个socket选项SO_EXCLUSIVEADDRUSE,使用它可以避免SO_REUSEADDR选项的.aspx),但是依我之见,最好还是避免处理TIME_WAIT的问题,代之好好设计你的系统,让TIME_WAIT不再成为问题。
前面的TCP状态转换图都显示了连接断开的顺序,这里还有另外一种方式断开TCP连接。通过终止(abort)连接并发送一个RST代替FIN,这可以通过设置socket的SO_LINGER选项为 0 来实现。这样会使未处理的数据直接被丢弃并且连接被RST中断,而不是使用FIN的时候那样,未处理的数据继续完成传输。认识到当连接被RST中断的时候,任何在终端之间未处理的数据都将会被直接丢弃是非常重要的,通常这个RST代表了一个错误消息"connection has been reset by the peer"。远程终端知道连接是被中断还是进入了TIME_WAIT状态。
当然,一个已经使用RST终止的连接可能成为TIME_WAIT所预防的延迟段问题的受害者,但是这种可能性是非常小的,原因见上述的详细描述。要防止一个中断的连接受到延迟段问题的影响,两个终端必须转换到TIME_WAIT状态,因为连接的关闭可能会是由传输中介造成,比如路由器。然而,在没有发生这种情况时两端都会被简单的关闭。
要避免TIME_WAIT成为你的问题也是有办法的,这里假设你有能力修改你的客户端和服务端之间使用的协议,但是,通常情况下需要自己进行服务器设计。
对于从来都不会自身建立出站连接的服务器来说,除了会牺牲部分资源和性能去维护处于TIME_WAIT状态的连接外,你不需要过度的担心其它的问题。
对于需要同时建立出站连接和入站连接的服务器来说,黄金规则是如果需要TIME_WAIT的话,让远端来主动关闭连接而不是本服务器。最好的方式是无论什么原因,永远不要由服务器来初始一个主动关闭。如果你的终端超时了,使用RST中断连接代替关闭它。如果你的终端发送了不可用的数据,中断连接等。这种方法的思想是如果你的服务器永远都不初始发起主动关闭,那当前服务器就不会累积处于TIME_WAIT状态的socket,因此就不会造成扩展性的问题。虽然在出错的情况下中断连接是非常简单的,但是如果是正常连接的终止该如何做呢?李向情况下,你应该在你的服务器协议设计的时候有一种方法能够告诉客户端让客户端主动断开连接,而不是由服务器发起。所以,如果服务器需要中断一个连接的话,服务器发送一个应用级别的消息"we're done"告诉客户端,客户端来关闭这个连接。如果客户端由于某些原因关闭连接失败了,然后服务器直接中断连接。
在客户端,事情就更加复杂一点,毕竟它需要初始发起一个主动关闭去终止TCP连接,如果客户端终止连接,它将以TIME_WAIT状态结束。但是,在客户端以TIME_WAIT终止连接有很多优点。第一,如果因为某些原因,客户端由于TIME_WAIT的问题而造成的连接问题不用影响到其它客户端。第二,如果频繁的打开关闭到同一个服务器的TCP连接的话是非常低效的。不要设计这种客户端每分钟都去连接服务器并且打开一个新的连接的协议。取而代之的是使用持久连接的设计并且只在连接失败的时候重新连接。如果中间的路由器拒绝保持没有数据流的连接的话,你可以实现一个应用级别的ping,使用TCP的keep alive或者接受路由器的重置连接;这样的好处是你不会积累很多TIME_WAIT的socket。如果你所做的操作的生存周期非常短,可以考虑一些连接池的设计,让连接一直保持打开和可重复使用。最后,如果你必须在客户端频繁的打开和关闭到同一个服务器的连接,可能你需要设计应用级别的关闭方式。你的客户端可以发送一个"I'm done"的消息,然后服务器可以发送一个"goodbye"的响应消息,然后客户端终止连接。
TIME_WAIT的存在是有它的理由的,通过缩短2MSL的时间或者使用SO_REUSEADDR允许连接重用并不总是好主意。如果你有能力去设计你的协议避免TIME_WAIT产生的问题的话,你就可以避免这里所有的问题。
你过你希望获取更多关于TIME_WAIT的实现和如何利用它的信息,参考这两篇文章:
1 收藏&&|&&25
你可能感兴趣的文章
不幸的是有些操作系统对TIME_WAIT的实现太过简单(slightly natvie)。
natvie 是什么
& 不幸的是有些操作系统对TIME_WAIT的实现太过简单(slightly natvie)。
natvie 是什么
多谢指正,拼错了,应该是naive,天真幼稚的意思
多谢指正,拼错了,应该是`naive`,天真幼稚的意思
分享到微博?
我要该,理由是:
在 SegmentFault,学习技能、解决问题
每个月,我们帮助 1000 万的开发者解决各种各样的技术问题。并助力他们在技术能力、职业生涯、影响力上获得提升。鍗氬?鍒嗙被锛

我要回帖

更多关于 蓝牙耳机能连接电脑吗 的文章

 

随机推荐