详解TCP连接

1、TCP最大连接数(参考)

2、TCP连接的几种状态(参考)

3、 为什么需要TIME_WAIT?

4、TIME_WAIT很多对服务器的影响(参考)


1、TCP最大连接数(参考

    计算机通信,有一个问题必须解决,就是如何唯一识别一个会话,英文名称是session,TCP/IP协议里没有一个字段可以完全胜任这个工作,意味着,无法遴选出一个主键,我们可以退而求其次,遴选出组合键来完成这个使命。既然通信是双方的事,最终遴选出五元组的这个组合键:服务器IP + 服务器端口 + TCP + 客户端IP + 客户端端口,只要保证这个五元组是唯一的,就可以唯一识别一个session,不是吗?

    对于一个特定的服务器,一般IP、端口号都是固定的,比如提供网页服务的默认端口是80不会变。如果相同的客户端来连接这个服务器,客户端的IP是相同的,多个TCP连接都使用相同的端口号,那么这个组合键是不是都是一样的?这是绝对不行的!因为无法唯一识别一个session!客户端的操作系统TCP/IP必须做出硬性的限制,每次连接,如果本地端口已经被别的session占用,要从空闲的端口号池子里选取一个端口号,以此方式,无论是在客户端的眼里,还是服务器的眼里,最终的五元组肯定是唯一的。

    端口号在TCP协议字段里,一共两个字节,二进制的16位,意味着有2^16= 65536个端口号可用,但0-1023系统通常保留为知名服务端口,所以最多有64512个端口做为端口池资源。一个问题:如果相同的客户端使用相同的端口号来连接同一个服务器(IP相同)的不同端口、或不同服务器(IP不同)相同端口,那可不可以呢?理论上,按照五元组的精神,根据五元组的哈希值,只要有任何一项是不同的,session ID 就是不同的,所以是完全可行的,但为何操作系统也要限制?是为了简化实现,试想客户端每个TCP连接硬性规定,使用不同的端口号,服务器一点也不需要担心session ID 的唯一性。此外,64512个端口难道还不够一个客户端使用?

    总结:如果是你作为客户端去连别人,那么总共可以建立64512个连接,因为每一个连接需要一个端口号。而如果你作为服务器接受客户端的连接,那么服务器在监听端口(比如80)可以接受来自五湖四海的客户端TCP连接,只考虑 IPv4 的情况下,并发数的理论上限是 2**48。一个 TCP 连接有两个 end points,每个 end point 是 {ip, port},题目说其中一个 end point 已经固定,那么留下一个 end point 的自由度,即 2 ** 48。客户端 IP 的上限是 2**32 个,每个客户端IP发起连接的上限是 2**16,乘到一起得理论上限。考虑某些 IP 段被保留了,这个上界可适当缩小,但数量级不变。实际的限制是操作系统全局文件描述符的数量(ulimit -n 可以查看系统允许当前用户进程打开的文件数限制,以及取决于服务器的CPU、Memory 资源限制。

2、TCP连接的几种状态(参考

    TCP连接的三次握手和四次挥手的流程图如下:

                  

(1)SYN_RECV

    服务端收到建立连接的SYN没有收到ACK包的时候处在SYN_RECV状态。有两个相关系统配置:

    1)net.ipv4.tcp_synack_retries :INTEGER
    默认值是5,对于远端的连接请求SYN,内核会发送SYN + ACK数据报,以确认收到上一个 SYN连接请求包。这是所谓的三次握手( threeway handshake)机制的第二个步骤。这里决定内核在放弃连接之前所送出的 SYN+ACK 数目。不应该大于255,默认值是5,对应于180秒左右时间。通常我们不对这个值进行修改,因为我们希望TCP连接不要因为偶尔的丢包而无法建立。

    2)net.ipv4.tcp_syncookies
    一般服务器都会设置net.ipv4.tcp_syncookies=1(开启syncookie功能之后,半连接队列的大小设置无效)。来防止SYN Flood攻击。假设一个用户向服务器发送了SYN报文后突然死机或掉线,那么服务器在发出SYN+ACK应答报文后是无法收到客户端的ACK报文的(第三次握手无法完成),这种情况下服务器端一般会重试(再次发送SYN+ACK给客户端)并等待一段时间后丢弃这个未完成的连接,这段时间的长度我们称为SYN Timeout,一般来说这个时间是分钟的数量级(大约为30秒-2分钟)。
    这些处在SYNC_RECV的TCP连接称为半连接,并存储在内核的半连接队列中,在内核收到对端发送的ack包时会查找半连接队列,并将符合的requst_sock信息存储到完成三次握手的连接的队列中,然后删除此半连接。大量SYNC_RECV的TCP连接会导致半连接队列溢出,这样后续的连接建立请求会被内核直接丢弃,这就是SYN Flood攻击。

 3)如何防御syn flood攻击(参考   

    第一种方法是SYN Cache,它的出发点主要是针对“鸠占鹊巢”问题,基本原理如下:构造一个全局的Hash Table,用来缓存系统当前所有的半开连接信息,连接成功则从Cache中清除相关信息;Hash Table中每个桶(bucket)的容量大小也有限制,当桶“满”时做除旧迎新操作。当B收到一个SYN消息后,会将半开连接信息加入到Hash Table中,其中key的生成很关键,既要用到SYN消息中包含的信息(如:Source IP,Port等)又要做到很难被攻击者猜到,一般会通过一个秘密的函数生成,这样所有的半开连接无论好坏,都看似随机地被平均分配到了不同的“桶”中,使攻击难度大增,因为为达到DoS效果,攻击者需要使每个桶都达到填满状态,并且还要有足够快的“填桶”速度,使得正常的半开连接在还未完成建立前就被踢出桶,这样的攻击行为估计在达到目的前早就暴露了。

    第二种方法是SYN Cookies,SYN Cookie由D. J. Bernstain和 Eric Schenk发明。SYN Cookie是对TCP服务器端的三次握手协议作一些修改,专门用来防范SYN Flood攻击的一种手段。它的着眼点主要是设法消除半开连接的资源消耗,原理与HTTP Cookies技术类似,B通过特定的算法把半开连接信息编码成“Cookie”,用作B给A的消息编号(SequenceNum),随SYN-ACK消息一同返回给连接发起方A,这样在连接完全建立前B不保存任何信息。如果A是正常用户,则会向B发送最后一次握手消息(ACK),B收到后验证“Cookie”的内容并建立连接;如果A是攻击者,则不会向B反馈ACK消息,B也没任何损失,也就说是单纯的SYN攻击不会造成B的连接资源消耗。
    当然这种方案也有一定缺点,最明显的就是B不保存连接的半开状态,就丧失了重发SYN-ACK消息的能力,这一方面会降低正常用户的连接成功率,另一方面会导致某些情况下正常通信的双方会对连接是否成功打开产生误解,如A发给B的第三次握手消息(ACK)半路遗失,A认为连接成功了,B认为没收到ACK,连接没成功,这种情况就需要上层应用采取策略特别处理了。此外,由于计算cookie有一定的运算量,增加了连接建立的延迟时间,因此,SYN Cookie技术不能作为高性能服务器的防御手段。通常采用动态资源分配机制,当分配了一定的资源后再采用cookie技术。还有一个问题是,当我们避免了SYN Flood攻击的同时,也提供了另一种拒绝服务攻击方式,攻击者发送大量的ACK报文,服务器忙于计算验证。尽管如此,在预防SYN Flood供给方面,SYN Cookie技术仍然是有效的。

(2)CLOSE_WAIT

    发起TCP连接关闭的一方称为client,被动关闭的一方称为server。被动关闭的server收到FIN后,但未发出ACK的TCP状态是CLOSE_WAIT。出现这种状况一般都是由于server端代码的问题,如果你的服务器上出现大量CLOSE_WAIT,应该要考虑检查代码。

(3)TIME_WAIT

 和TIME_WAIT状态有关的系统参数有如下几个:

  1) net.ipv4.tcp_tw_recycle = 1  :表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。当开启了这个配置后,内核会快速的回收处于TIME_WAIT状态的socket连接。多快?不再是2MSL,而是一个RTO(retransmission timeout,数据包重传的timeout时间)的时间,这个时间根据RTT动态计算出来,但是远小于2MSL。

  2) net.ipv4.tcp_tw_reuse = 1 :表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭。一条socket连接出现TIME_WAIT状态的连接,一定出现在主动关闭连接的一方,即首先调用close()的一方。所以,当主动关闭连接的一方,再次向对方发起连接请求的时候(例如,客户端关闭连接,客户端再次连接服务端,此时可以复用了;负载均衡服务器,主动关闭后端的连接,当有新的HTTP请求,负载均衡服务器再次连接后端服务器,此时也可以复用),可以复用TIME_WAIT状态的连接。

  3) net.ipv4.tcp_fin_timeout = 30 :默认60s,减小fin_timeout,减少TIME_WAIT连接数量。

    根据TCP协议定义的3次握手断开连接规定,发起socket主动关闭的一方 socket将进入TIME_WAIT状态。TIME_WAIT状态将持续2个MSL(Max Segment Lifetime),1分钟,即60秒。TIME_WAIT状态下的socket不能被回收使用.,具体现象是对于一个处理大量短连接的服务器,如果是由服务器主动关闭客户端的连接,将导致服务器端存在大量的处于TIME_WAIT状态的socket, 甚至比处于Established状态下的socket多的多,严重影响服务器的处理能力,甚至耗尽可用的socket,停止服务。

3、 为什么需要TIME_WAIT?

    原因有二:1)保证TCP协议的全双工连接能够可靠关闭,2)保证这次连接的重复数据段从网络中消失;

    先说第一点,如果Client直接CLOSED了,那么由于IP协议的不可靠性或者是其它网络原因,导致Server没有收到Client最后回复的ACK。那么Server就会在超时之后继续发送FIN,此时由于Client已经CLOSED了,就找不到与重发的FIN对应的连接,最后Server就会收到RST而不是ACK,Server就会以为是连接错误把问题报告给高层。这样的情况虽然不会造成数据丢失,但是却导致TCP协议不符合可靠连接的要求。所以,Client不是直接进入CLOSED,而是要保持TIME_WAIT,当再次收到FIN的时候,能够保证对方收到ACK,最后正确的关闭连接。

    再说第二点,如果Client直接CLOSED,然后又再向Server发起一个新连接,我们不能保证这个新连接与刚关闭的连接的端口号是不同的。也就是说有可能新连接和老连接的端口号是相同的。一般来说不会发生什么问题,但是还是有特殊情况出现:假设新连接和已经关闭的老连接端口号是一样的,如果前一次连接的某些数据仍然滞留在网络中,这些延迟数据在建立新连接之后才到达Server,由于新连接和老连接的端口号是一样的,又因为TCP协议判断不同连接的依据是socket pair,于是,TCP协议就认为那个延迟的数据是属于新连接的,这样就和真正的新连接的数据包发生混淆了。所以TCP连接还要在TIME_WAIT状态等待2倍MSL,这样可以保证本次连接的所有数据都从网络中消失。

4、TIME_WAIT很多对服务器的影响(参考

     服务器在处理客户端请求的时候,如果你的程序设计为服务器主动关闭,那么你才有可能需要关注这个TIMEWAIT状态过多的问题。如果你的服务器设计为被动关闭,那么你首先要关注的是CLOSE_WAIT。  换句话说:在一台负载均衡的服务器上(以Nginx为例),客户端向Nginx的请求,作为服务器来看属于被动连接;Nginx向Web服务器的请求属于主动连接。在讨论TIME_WAIT优化时,我们应该关注的是主动连接,即Nginx对Web服务器的连接。

(1)对主动连接的影响

    TIME_WAIT表示socket可以进入和留存相当长一段时间的状态,如果系统中有很多 socket 处于TIME_WAIT状态,当需要创建新的 socket 连接的时候可能会受到影响,这也会影响到程序的扩展性。之所以TIME_WAIT能够影响系统的扩展性是因为在一个TCP连接中,一个Socket如果关闭的话,它将保持TIME_WAIT状态大约 1分钟 。如果很多连接快速的打开和关闭的话,系统中处于TIME_WAIT状态的socket将会积累很多,由于本地端口数量的限制,同一时间只有有限数量的socket连接可以建立,如果太多的socket处于TIME_WAIT状态,你会发现,由于用于新建连接的本地端口太缺乏,将会很难再建立新的对外连接。

(2)如何避免:
    由于本地端口的缺乏,TIME_WAIT的存在影响的是出站连接的建立,这些本地端口由操作系统进行自动的分配,因此,优化的方法是增加本地端口的范围。其他方法: 1)缩短2MSL的时间 ; 2)使用SO_REUSEADDR允许连接重用 ; 3)设计协议避免TIME_WAIT产生的问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值