连接建立:
1. 客户端发送SYN segment,目的是:通告自己的ISN和MSS
2. 服务端发送SYN+ACK segment,目的是:除了通告自己的ISN和MSS外,顺带确认已收到客户端发送的SYN segment
3. 客户端发送ACK segment,目的是:确认收到服务端发送的SYN+ACK segment
上述过程也被称为:three-way handshake
几点说明:
1. 第1步的SYN segment是由connect函数触发的
2. 第2步的SYN+ACK segment是TCP模块自动生成的
3. 客户端收到SYN+ACK segment后,connect函数才返回
4. 服务端收到ACK segment后,accept函数才返回
先发送SYN segment的一端(一般是客户端)称为执行一个active open
后发送SYN+ACK segment的一端(一般是服务端)称为执行一个passive open
另外还可能发生同时打开(simultaneous open)的情况,也就是两端都执行active open
连接终止:
为了便于描述,下文称两端为A端和B端
1. A端发送FIN segment,通知B端我已经没有数据要发送了
2. B端发送ACK segment,确认已收到A端发送的FIN segment
3. B端发送FIN segment,通知A端我已经没有数据要发送了
4. A端发送ACK segment,确认已收到B端发送的FIN segment
几点说明:
1. FIN segment是由close或shutdown函数触发的
2. ACK segment是TCP模块自动生成的
3. 收到FIN的一端的应用的read/recv/recvmsg等操作会返回EOF
先发送FIN segment的一端称为执行一个active close
后发送FIN segment的一端称为执行一个passive close
另外还可能发生同时关闭(simultaneous close)的情况,也就是两端都执行active close
两端都需要发送一个FIN segment,是因为TCP是全双工协议,两端都要执行关闭连接的操作,每一端的关闭操作称为half-close(关闭连接的发送方向)
观察连接建立与终止
代码: 全选
$ sudo ./mytcpdump port 8888
0.000 ( 0.000) IP 192.168.0.3.50396 > 192.168.0.6.8888: flags [S], seq 634664549, win 65535, options [mss 1460], length 0
0.000 ( 0.000) IP 192.168.0.6.8888 > 192.168.0.3.50396: flags [SA], seq 3060868865, ack 634664550, win 65160, options [mss 1460], length 0
0.001 ( 0.001) IP 192.168.0.3.50396 > 192.168.0.6.8888: flags [A], seq 634664550, ack 3060868866, win 2058, options [], length 0
5.990 ( 5.989) IP 192.168.0.3.50396 > 192.168.0.6.8888: flags [FA], seq 634664550, ack 3060868866, win 2058, options [], length 0
5.990 ( 0.000) IP 192.168.0.6.8888 > 192.168.0.3.50396: flags [FA], seq 3060868866, ack 634664551, win 510, options [], length 0
5.990 ( 0.000) IP 192.168.0.3.50396 > 192.168.0.6.8888: flags [A], seq 634664551, ack 3060868867, win 2058, options [], length 0
1. SYN segment和FIN segment 都消耗一个sequence number
2. SYN segment除了通告对端自己的ISN外,还通告对端自己期望接收的MSS(表示TCP segment里data的最大字节数)
TCP模块发送的segment大小主要由两个值决定:
1.对端通告的MSS
2.自己发现的pMTU
TCP模块发送的segment大小由这两个值中的较小值决定
注意:
MSS指的对segment里data的大小限制
pMTU指的是对整个IP Datagram的大小限制
观察连接建立超时重传
在arp cache里添加一个不存在的主机的硬件地址(避免IP模块调用ARP请求)
代码: 全选
$ sudo arp -s 192.168.0.8 90:9c:4a:c0:be:d1
$ arp -n
Address HWtype HWaddress Flags Mask Iface
192.168.0.3 ether 90:9c:4a:c0:be:d0 C enp0s3
192.168.0.8 ether 90:9c:4a:c0:be:d1 CM enp0s3
192.168.0.1 ether 2c:61:04:ba:ff:fa C enp0s3
代码: 全选
$ time nc 192.168.0.8 8888
real 2m9.874s
user 0m0.002s
sys 0m0.000s
代码: 全选
$ sudo ./mytcpdump -i enp0s3 port 8888
0.000 ( 0.000) IP 192.168.0.6.39996 > 192.168.0.8.8888: flags [S], seq 1324841680, win 64240, options [mss 1460], length 0
1.005 ( 1.005) IP 192.168.0.6.39996 > 192.168.0.8.8888: flags [S], seq 1324841680, win 64240, options [mss 1460], length 0
3.025 ( 2.020) IP 192.168.0.6.39996 > 192.168.0.8.8888: flags [S], seq 1324841680, win 64240, options [mss 1460], length 0
7.309 ( 4.284) IP 192.168.0.6.39996 > 192.168.0.8.8888: flags [S], seq 1324841680, win 64240, options [mss 1460], length 0
15.438 ( 8.129) IP 192.168.0.6.39996 > 192.168.0.8.8888: flags [S], seq 1324841680, win 64240, options [mss 1460], length 0
31.569 (16.131) IP 192.168.0.6.39996 > 192.168.0.8.8888: flags [S], seq 1324841680, win 64240, options [mss 1460], length 0
64.428 (32.859) IP 192.168.0.6.39996 > 192.168.0.8.8888: flags [S], seq 1324841680, win 64240, options [mss 1460], length 0
1. time命令报告的时间大概130s(最后一个SYN segment需要再过大约32.859*2=65.718秒后超时,总时间大约为64.428+65.718=130.146)
2. tcp模块一共重传了6次,每次重传的时间间隔(RTO)都是上次的2倍(括号里的时间是距离前一个packet的时间)
建立连接过程中的syn的重传次数可以通过/proc/sys/net/ipv4/tcp_syn_retries文件配置
代码: 全选
$ cat /proc/sys/net/ipv4/tcp_syn_retries
6
我理解half-close有两个含义:
1. TCP的half-close:指的是一端关闭发送方向,即触发FIN segment,从这个角度来说,close和shutdown(how=1)都执行TCP的half-close
2. FD的half-close:指的是关闭文件描述符的write方向,这个只能通过shudown函数(how=1),同样触发TCP的half-close,即:触发FIN segment
另外:
shutdown还可以指定how=0,关闭文件描述符的读方向,但是并不会触发TCP的half-close,即:不会触发FIN segment
我们通常说的half-close,一般指的是通过shudown函数(how=1)来触发TCP的half-close