来源:https://github.com/wangcy6/weekly 每日一题 第二题
老王:小王,最近工作注意力不集中呀!
小王:我在等面试结果呢!
老王:你感觉如何呢?
小王:
当时情况是这样的!
大王:你擅长window,还是liunx?
小王:Linux(这年头谁还在写window程序)
大王;那你对网络编程一定很熟悉 吧?
小王:那是当然(都是小菜一碟)。
大王:请绘制TCP状态转换过程?
小王:。。。。(这个谁能记住他,绞尽脑汁想,5分钟过去了)
大王:还有什么要补充的吗?(耐心等待)
小王:不会写,有几个记不清楚(5分钟过去了)
大王:好,回去等通知。
老王:我来讲一讲,需要解决下面几个问题
自我提问
小王:
socket常用接口 accept,read,write close,我经常用很熟悉呀,没什么可学的了, 还有tcp协议那个图 我看多少遍?
(老王)我这里提示一下,不做深入讨论,时间限时在2分钟。
完整通讯过程
用户态
客户端和服务器同时发现异常,都进行关闭这个连接
我没遇到过
小王:
遇到这个情况,不是epoll, SO_KEEPALIVE read返回 0代表接受。一般都是这么处理的
闲话少说,从四次挥手最有一步异常说起。
老王:RichardStevens说过这样2句话
There are two reasons for the TIME_WAIT state:
一、保证TCP协议的全双工连接能够可靠关闭
To implement TCP's full-duplex connection termination reliably
二、保证这次连接的重复数据段从网络中消失
To allow old duplicate segments to expire in the network
小王:
表示不理解,上面不是同一个意思吗,如果达到不了就消失?
(老王)错误,继续看
根据第三版《UNIX网络编程 卷1》2.7节,TIME_WAIT状态的主要目的有两个:
ACK is lost. The server will resend its final FIN, so the client must maintain state information, allowing it to resend the final ACK. If it did not maintain this information, it would respond with an RST (a different type of TCP segment), which would be interpreted by the server as an error
发生条件:
服务正常,网络正常。
B发送FIN,进入LAST_ACK状态,A收到这个FIN包后发送ACK包,B收到这个ACK包,然后进入CLOSED状态
B发送FIN,进入LAST_ACK状态,A收到这个FIN包后发送ACK包,由于某种原因,这个ACK包丢失了,B没有收到ACK包,然后B等待ACK包超时,又向A发送了一个FIN包 a) 假如这个时候,A还是处于TIME_WAIT状态(也就是TIME_WAIT持续的时间在2MSL内)A收到这个FIN包后向B发送了一个ACK包,B收到这个ACK包进入CLOSED状态
b) 假如这个时候,A已经从TIME_WAIT状态变成了CLOSED状态 A收到这个FIN包后,认为这是一个错误的连接,向B发送一个RST包,当B收到这个RST包,进入CLOSED状态
小王:原来是这样
画外音
网络断了,节点重启了,是无法处理的。只能依靠Rst解决。
下面情况如果ack,不能按时到达,阻止建立新的连接。
小王:原来是这样
画外音:
TCP连接中的一端发送了FIN报文之后如果收不到对端针对该FIN的ACK,则会反复多次重传FIN报文. 处于TIME_WAIT状态的一端在收到重传的FIN时会重新计时(rfc793 以及 linux kernel源代码tcp_timewait_state_process函数
发生条件:
Note that it is very unlikely that delayed segments will cause problems like this.
Firstly the address and port of each end point needs to be the same; which is normally unlikely as the client's port is usually selected for you by the operating system from the ephemeral port range and thus changes between connections.
Secondly, the sequence numbers for the delayed segments need to be valid in the new connection which is also unlikely. However, should both of these things occur then TIME_WAIT
will prevent the new connection's data from being corrupted.
画外音:
必须原来的ip,原来的端口发起的连接,想想一个服务器连接多个客户端,四元组 是唯一的。
*Due to a shortened TIME-WAIT state, a delayed TCP segment has been accepted in an unrelated connection.*
image.png
小王:原来是这样!
画外音:
四次挥手已经完成,最有一个ack顺利达到对方,一方进入closed状态(假如3秒内完成)
对方依然要等待2MSL(剩余28秒),这个等待不是多余等待,而是防止
这个时候双方如果马上同时closed(是允许建立新的连接。这是正常通讯过程)、
还有延迟重发的数据包。对同一个pair连接,新老数据造成混乱。
tcp协议提到内核接受数据是根据port区分是那个,而不是fd。
time_wait 存在的意义有2点
(1) TCP 可靠传输,保证四次挥手最后一个ack 顺利到达对方。
采用方式是:如果获取到对方重新发送fin请求,需要重新计时间,维持TIme_wait状态。
保障每次发送出去ack都最终结果(收到或者消失)
如果在网络出断网,或者服务节点重启,或者对方不启tcp重传机制上面方法是无法处理的
应该超时或者返回Rst包出路 结束last_ack状态。
(2 ) TCP基于四元组建立连接, 假如客户端端口 不随机产生,而是相同ip,相同的
端口,再次连接的话。可能出现,虽然old 连接已经消失,但是在网络中数据可能存在。
以tcp 内核中断处理 网络消息是根据 端口划分的。会造成新旧数据混乱。
TCP不能给处于TIME_WAIT状态的连接启动新的连接。 TIME_WAIT的持续时间是2MSL,保证在建立新的连接之前老的重复分组在网络中消逝。 这个规则有一个例外:如果到达的SYN的序列号大于前一个连接的结束序列号, 源自Berkeley的实现将给当前处于TIME_WAIT状态的连接启动新的化身。 do_time_wait
tcp状态机