它是面向连接的一种协议,任何数据发送之前都需要建立连接。
TCP位于运输层,详见下图
在链接建立和断开不同阶段都有不同的状态,这些状态想必大家也都耳熟能详了,具体可以参考下图。
连接时的三次握手:
第一次握手:
客户端给服务器发送一个SYN段(在 TCP 标头中 SYN 位字段为 1 的 TCP/IP 数据包), 该段中也包含客户端的初始序列号(Sequence number = J),客户端TCP状态为SYN_SENT。
第二次握手:
服务器返回客户端 SYN +ACK 段(在 TCP 标头中SYN和ACK位字段都为 1 的 TCP/IP 数据包), 该段中包含服务器的初始序列号(Sequence number = K);同时使 Acknowledgment number = J + 1来表示确认已收到客户端的 SYN段(Sequence number = J),服务端TCP状态为SYN_RCVD。
第三次握手:
客户端给服务器响应一个ACK段(在 TCP 标头中 ACK 位字段为 1 的 TCP/IP 数据包), 该段中使 Acknowledgment number = K + 1来表示确认已收到服务器的 SYN段(Sequence number = K)。双方TCP进入ESTABLISHED状态。
关闭时的四次挥手:
第一次挥手:
客户端发出释放FIN=1,自己序列号seq=u,进入FIN-WAIT-1状态。
第二次挥手:
服务器收到客户端的后,发出ACK=1确认标志和客户端的确认号ack=u+1,自己的序列号seq=v,进入CLOSE-WAIT状态。
第三次挥手:
客户端收到服务器确认结果后,进入FIN-WAIT-2状态。此时服务器发送释放FIN=1信号,确认标志ACK=1,确认序号ack=u+1,自己序号seq=w,服务器进入LAST-ACK(最后确认态)。
第四次挥手:
客户端收到回复后,发送确认ACK=1,ack=w+1,自己的seq=u+1,客户端进入TIME-WAIT(时间等待)。客户端经过2个最长报文段寿命后,客户端CLOSE;服务器收到确认后,立刻进入CLOSE状态。
SYN:
是同步的缩写,SYN 段是发送到另一台计算机的 TCP 数据包,请求在它们之间建立连接。
ACK:
是“确认”的缩写。 ACK 数据包是任何确认收到一条消息或一系列数据包的 TCP 数据包。
FIN:
结束标志,用于释放连接,为1表示关闭本方数据流。
1)三次握手时,服务器同时把ACK和SYN放在一起发送给了客户端。
2)四次挥手时,当收到客户端的 FIN 报文时,仅仅表示对方不再发送数据了但是还能接收数据,所以服务器只能先回复一个ACK报文,告诉Client端,"你发的FIN报文我收到了"。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。所以关闭连接时多了一步服务侧的挥手。
为了服务侧确认客户端的接收能力正常。
全连接队列
当客户端返回ACK, 服务端接收后,三次握手完成。这个时候连接等待被具体的应用取走,在被取走之前,它会被推入另外一个 TCP 维护的队列,也就是全连接队列(Accept Queue) 。
半连接队列
当客户端发送SYN到服务端,服务端收到以后回复ACK和SYN,状态由LISTEN变为SYN_RCVD,此时这个连接就被推入了SYN队列 ,也就是半连接队列 。
是指,恶意估计者给服务器发送一个SYN后,直接下线,服务器侧则需要默认等63s才会断开连接,这样,攻击者就可以把服务器的syn连接(半连接)的队列耗尽,让正常的连接请求不能处理。
Linux下tcp_syncookies的参数可以应对这个事——当SYN队列满了后,TCP会通过源地址端口、目标地址端口和时间戳打造出一个特别的Sequence Number发回去(又叫cookie),如果是攻击者则不会有响应,如果是正常连接,则会把这个 SYN Cookie发回来,然后服务端可以通过cookie建连接(即使你不在SYN队列中)。请注意,请先千万别用tcp_syncookies来处理正常的大负载的连接的情况。因为,synccookies是妥协版的TCP协议,并不严谨。对于正常的请求,你应该调整三个TCP参数可供你选择,第一个是:tcp_synack_retries 可以用他来减少重试次数;第二个是:tcp_max_syn_backlog,可以增大SYN连接数;第三个是:tcp_abort_on_overflow 处理不过来干脆就直接拒绝连接了。
在大并发的短链接下,TIME_WAIT 就会太多,也会消耗很多系统资源,如果客户端的并发量持续很高,此时部分客户端就会显示连接不上。
如何尽量处理TIMEWAIT过多?
1)长连接 对于反向代理和应用服务器,最好是要配置成支持keepalive长连接,否则在系统并发增加时会导致一系列的连接问题。对于nginx+tomcat长连接的配置前面有一些介绍可以参考,其它服务器一般也是提供支持长连接配置的,设置后建议抓包测试验证。 一般来说长连接设置正确了TIME_WAIT数量不会暴涨,但是长连接最大请求数也是有效的,但如果应用的处理速度很快导致TIME_WAIT的产生的速度远快于TIME_WAIT的消耗速度时系统就会累计TIME_WAIT状态连接。这时候可能就要修改一些系统配置了。
2)ip_conntrack 用于跟踪TCP连接。一旦激活了此模块,就能在系统参数里发现很多用来控制网络连接状态超时的设置,其中自然也包括TIME_WAIT,默认ip_conntrack_max最大为65536,可以将其设置得更大一些。一般不建议此模块,如果系统安装使用iptable会启动该模块。
3)tcp_tw_recycle 在网上搜索TIME_WAIT问题的解决方法,大多都会提到这个参数,不过官方网站上不建议开启这个参数,原因是会导致一些安全问题。例如,当多个客户端通过NAT方式联网并与服务端交互时,服务端看到的是同一个IP,由于这些客户端的时间戳可能存在差异,所以从服务端的视角看,便可能出现时间戳错乱的现象,进而直接导致时间戳小的数据包被丢弃。
4)tcp_tw_reuse 当创建新连接的时候,如果可能的话会考虑复用相应的TIME_WAIT连接。官方文档里提到的是如果从协议视角看它是安全的,那么就可以使用。这个很难判定这个参数是否应该开启,不到万不得已的时候,即使我们要开启这个参数复用连接,也应该在连接的发起方使用,而不能在被连接方使用。
PS:tcp_tw_recycle和tcp_tw_reuse,非极端情况不建议使用这两参数,打开这两个参数会有比较大的坑——后期可能会让TCP连接出一些诡异的问题