这个是在网上看到的面试题,引用下原文的内容
如果此时ACK在网络中丢失,那么Server端该TCP连接的状态为SYN_RECV,并且依次等待3秒、6秒、12秒后重新发送SYN+ACK包,以便Client重新发送ACK包,以便Client重新发送ACK包。
Server重发SYN+ACK包的次数,可以通过设置/proc/sys/net/ipv4/tcp_synack_retries修改,默认值为5。
如果重发指定次数后,仍然未收到ACK应答,那么一段时间后,Server自动关闭这个连接。
但是Client认为这个连接已经建立,如果Client端向Server写数据,Server端将以RST包响应,方能感知到Server的错误。
嗯。前面都没问题,就是最后一个,client和server段的处理,我很疑惑。
- tcp是累加ack,而且ack在tcp包头,也就是所有tcp包都应该带有ack数据,中间丢一个纯ack包,不应该影响tcp通讯
- 第三个握手包只关乎是否已经收到第二个握手的syn包,所以只要后面带数据的ack,能ack到syn包,应该没问题
- 好像也没在哪里看到说不准在第三个ack里面发数据啊?这个我不确定,有知道的ping我一下
不确定的时候就想到了做实验。那么怎么做呢?
linux tc命令
嗯,模拟网络丢包,本来我想用vmware的丢包模拟的,结果我发现要fusion10 pro才有这个feature,我买的不是pro。。。。好吧,我又找到了tc命令。这个命令的netem模块能模拟网络丢包的情形,但只能控制流出的包。所以我起来两个Linux机器,一个跑server,一个跑client。server是一个简单的socket程序,接到连接之后打印一个success,读一下数据,然后close。client就连上之后,发一个hello,然后close。
具体的丢包模拟命令为
sudo tc qdisc add dev eth0 root netem loss 30%
在client机器上跑这个命令,这样就可以模拟30%丢包率的情况了(本来我用了50%的概率测试的,结果我发现大概率连第一个syn包都发不出来,降低到30%,就比较合适了)
测试结果
在服务器抓包,看起来,至少在ubuntu12.04上面,服务器端是不会发rst的:
可以看到131和132号包,是两个握手包,133号包,len为7,已经携带数据,服务器并没有发rst。由于我的测试程序发完数据就close,所以马上客户端就开始发fin了。而服务器也能正确close这个连接。
TCP Fast Open
这个选项与上面的讨论没有直接关系,只是额外发现的一个补充。
在linux上面有选项
/proc/sys/net/ipv4/tcp_fastopen
用以控制是否允许快速打开。默认为1
该功能的行为是,第一个握手包会带一个cookie和应用数据,服务器收到syn之后,如果cookie正确,允许服务器直接开始返回数据,而不需要等第三个ack。如果syn不正确,客户端需要在第三个ack的时候重新携带数据。当然这个需要服务器客户端同时配合。
可见握手是不是携带数据并不是一定的,携带数据,是不是会被rst,实际上有点看具体tcp ip栈的实现,我相信肯定有坑爹的tcp实现会在这个时候rst的,特别是嵌入式领域。