三次握手原因:保证数据传输可靠。通过三次握手对连接双方的序号进行确认,保证未来的可靠数据传输。 四次挥手原因:全双工。
TIME_WAIT:主动关闭端发送最后一个ACK时,需在TIME_WAIT状态停留2MSL时间。(MSL:任何报文段被丢弃前在网络中的最长时间)(该状态下,定义该连接的套接字不能再被使用,即不能建立新连接) 目的:
- 防止最后一个ACK丢失。若丢失,另一端超时重传FIN。假设C端是主动关闭方,C端发送的最后一个ACK丢失,S端超时重传FIN,若无TIME_WAIT,C端则已经断开连接,收到S的FIN时,会返回一个RST报文,则S端会连接异常。(java中为connection reset的SocketException)
- 假设不停留在TIME_WAIT状态,新建了连接,会导致:
2.1 新建连接无法成功。假设C端最后一个ACK超时,但没有TIME_WAIT状态,则C端默认已经关闭了该连接,此时C端复用该套接字,发送SYN新建连接,由于S端未收到上一次连接的ACK,会发送RST报文,新连接无法建立成功,C端异常。 2.2 旧连接的迟到报文段被误解为是新连接的报文段。(而2MSL的时间足够让本次连接所有迟到报文段都消失在网络中,防止其扰乱新连接)
其他情况假设:
- S端的FIN丢失:S端重传FIN,C端收到发送ACK。
- C端的最后一个ACK连续丢失两次,2MSL时间是否够用?够用,C端每次发送报文计时器会重置,重新开始2MSL时间的计时。
TIME_WAIT过多:主动关闭连接一方可能出现大量TIME_WAIT,端口不够用加上停留时间过长,可能会导致无法接受新的连接。解决方法(编辑文件/etc/sysctl.conf修改参数):
- net.ipv4.tcp_timestamps:两个时间戳字段
TSval为发送该数据包的时间,TSecr只在设置了ACK位时有效,值通常为最近收到对方发送的数据包的时间。
rfc1323 3.2节:The Timestamp Echo Reply field (TSecr) is only valid if the ACK bit is set in the TCP header; if it is valid, it echos a times- tamp value that was sent by the remote TCP in the TSval field of a Timestamps option. When TSecr is not valid, its value must be zero. The TSecr value will generally be from the most recent Timestamp option that was received; however, there are exceptions that are explained below.
- net.ipv4.tcp_tw_reuse:复用处于TIME_WAIT状态的连接。
问题在于,该连接被复用,上面出现的ACK丢失或延迟报文段到达等问题如何解决:依赖上述时间戳选项,来判断收到的数据包是否属于旧连接。由于新建连接会收到ACK,则TSecr有效且保存了新建连接的时间,当收到一个数据包时,比较TSecr与该数据包被发送的时间TSval,即可判断该数据包属于哪条连接,若早于TSecr,说明是旧连接的延迟数据包,可以丢弃。 因此该配置依赖通信双方开启net.ipv4.tcp_timestamps参数。 - net.ipv4.tcp_tw_recycle:内核快速回收处于TIME_WAIT状态的连接。
(与复用连接不同,只是可能恰好用了相同的五元组) 同样依赖时间戳判断无效的数据包,但会出现另一个问题。如果客户端是在NAT网络中(多个客户端向该服务器发送请求,显示为同一个IP出口,(由于设置了tcp_tw_recycle字段,会清楚TCP的四元组信息释放内存,因此没法根据端口判断,但可以根据IP判断,即比较该IP的时间戳。)),一个RTO内多个客户端同时建立连接,由于不同客户端发包时间不同,可能导致只有一个客户端能成功建立连接,其他的SYN都被丢掉了,会导致客户端一直重传SYN(因为收不到ACK)。 所以客户端在NAT网络中时不要开启该选项。
CLOSE_WAIT:被动关闭方未关闭连接所处的状态。 出现原因:
- 应用程序没有合适的关闭socket
- 获取不到CPU时间片去关闭连接,可能时间片都被其他进程占用了,或者应用程序被阻塞(锁等)
大量CLOSE_WAIT:由于套接字要打开fd,而机器能打开的fd有限,且内核中的hash table要维护连接,因此大量CLOSE_WAIT会占用过多内存和fd。
keepalive:设置SO_KEEPALIVE选项,则TCP的keepalive保活机制生效。 TCP keepalive保活机制的参数(/etc/sysctl.conf中设置):
- tcp_keepalive_time:发送第一个保活探测包的时间间隔(即正常发送心跳的周期),默认7200s即两小时。
- tcp_keepalive_probes:第一个探测包未收到确认,继续发送的次数,默认9次。
- tcp_keepalive_intvl:探测包未收到确认,继续发送的时间间隔,默认75s。
参考: https://zhuanlan.zhihu.com/p/40013724 https://datatracker.ietf.org/doc/html/rfc1323 http://chenzhenianqing.com/articles/1150.html
|