TCP协议(Transmission Control Protocol,传输控制协议),为应用层提供可靠的、面向连接的和基于流(stream)的服务。TCP协议使用超时重传、数据确认等方式来确保数据包正确地发送至目的端,因此TCP服务是可靠的
下面利用tcpdump来观察和分析TCP连接的建立与关闭过程。首先介绍测试环境:开启两台Linux虚拟机,然后将网络均连接到同一局域网下,这里是均连接至路由器。机器A的IP地址为:192.168.1.106;机器B的IP地址为:192.168.1.149
配置环境,在机器B上进行如下操作:
首先查看防火墙80端口是否打开
sudo iptables -L -n
这里看出80端口防火墙未打开
开放80端口防火墙:
sudo iptables -A INPUT -ptcp --dport 80 -j ACCEPT
操作过程如下图:
接下来打开指定端口,这里使用80端口登录,因此打开80端口,Linux下打开某个端口的操作为:
sudo nc -lp 80 & #打开80端口,即telnet
sudo netstat -an | grep 80 #查看是否打开80端口
结果如图所示:
这里看到80端口已经处于LISTEN状态。
上述操作也可用来解决以下问题:
telnet: Unable to connect to remote host: Connection refused
参考谢希仁版计算机网络,TCP连接的建立过程如下:
连接过程分析:
(1)A的TCP向B发出连接请求报文段,其首部中的同步位SYN = 1,并选择序号seq = x,表明传送数据时的第一个数据字节的序号是x。
(2)B的TCP收到连接请求报文段后,如同意,则发回确认。B在确认报文段中应使SYN = 1,使 ACK = 1,其确认号ack = x + 1,自己选择的序号seq = y
(3)A收到此报文段后向B给出确认,其 ACK = 1,确认号 ack = y + 1。A的TCP通知上层应用进程,连接已经建立。
验证连接过程
在机器A上开启两个终端,在终端1中输入如下指令,用来抓包:
sudo tcpdump -i ens33 -nt '(src 192.168.1.106 and dst 192.168.1.149) or (src 192.168.1.149 and dst 192.168.1.106)'
在另外一个界面中输入如下指令开始建立连接:
$ telnet 192.168.1.149 80
抓包分析:
IP 192.168.1.106.39512 > 192.168.1.149.80: Flags [S], seq 3069204869, win 29200, options [mss 1460,sackOK,TS val 2244597918 ecr 0,nop,wscale 7], length 0
IP 192.168.1.149.80 > 192.168.1.106.39512: Flags [S.], seq 1445727406, ack 3069204870, win 28960, options [mss 1460,sackOK,TS val 1167317455 ecr 2244597918,nop,wscale 7], length 0
IP 192.168.1.106.39512 > 192.168.1.149.80: Flags [.], ack 1, win 229, options [nop,nop,TS val 2244597975 ecr 1167317455], length 0
第一条TCP报文段:机器A首先发送一个连接请求,Flags[S]表示该TCP报文段包含SYN标志(同步报文段),即表示上图中的SYN = 1,seq = 3069204869,记作x = 3069204869;
第二条TCP报文段:机器B收到了一个连接请求,同意建立连接,Flags[S.]表示该TCP报文段包含SYN标志和应答标志(即SYN = 1,ACK = 1),seq = 1445727406,ack = 3069204870。这里的seq为机器B的序号,记作y = 1445727406,这里的ack = x + 1;
第三条TCP报文段:机器A收到机器B发送的确认,Flags[.]表示该TCP报文段包含应答标志(即ACK = 1),表示收到应答,但是这里的ack为1,为什么??原因是:从第3个TCP报文段开始,tcpdump输出的序号值和确认值(ack)都是相对初始ISN值的偏移,可以开启tcpdum的-S选项来选择打印序号的绝对值,后面关闭过程也是用的相对值。
更改抓包指令结果如下图:
sudo tcpdump -S -i ens33 -nt '(src 192.168.1.106 and dst 192.168.1.149) or (src 192.168.1.149 and dst 192.168.1.106)'
这里可以看到,在tcpdum指令后增加-S选项后,第三条TCP报文段的ack变为绝对值,此时的ack = seq +1。
参考谢希仁版计算机网络,TCP连接的关闭过程如下:
关闭过程分析:
(1)数据传输结束后,通信的双方都可释放连接。现在A的应用进程先向其TCP发出连接释放报文段,并停止再发送数据,主动关闭TCP连接。A 把连接释放报文段首部的 FIN = 1,其序号seq = u,等待B的确认。
(2)B发出确认,确认号 ack = u + 1,而这个报文段自己的序号seq = v。TCP服务器进程通知高层应用进程。从A到B这个方向的连接就释放了,TCP连接处于半关闭状态。B 若发送数据,A仍要接收
(3)若B已经没有要向A发送的数据,其应用进程就通知 TCP 释放连接。
(4)A收到连接释放报文段后,必须发出确认。 在确认报文段中ACK = 1,确认号ack = w + 1,自己的序号 seq = u + 1。
12验证关闭过程
关闭连接:
$ telnet 192.168.1.149 80
Trying 192.168.1.149...
Connected to 192.168.1.149.
Escape character is '^]'.
^]
telnet> quit
Connection closed.
抓包分析:
IP 192.168.1.106.39512 > 192.168.1.149.80: Flags [F.], seq 1, ack 1, win 229, options [nop,nop,TS val 2244612012 ecr 1167317455], length 0
IP 192.168.1.149.80 > 192.168.1.106.39512: Flags [F.], seq 1, ack 2, win 227, options [nop,nop,TS val 1167331541 ecr 2244612012], length 0
IP 192.168.1.106.39512 > 192.168.1.149.80: Flags [.], ack 2, win 229, options [nop,nop,TS val 2244612022 ecr 1167331541], length 0
第一条TCP报文段:机器A首先发送一个关闭连接请求,Flags[F.]表示该TCP报文段包含FIN标志(结束报文段)和应答标志,seq = 1,记作x = 1;
第二条TCP报文段:机器B收到了一个关闭连接请求,确认关闭连接,Flags[F.]表示该TCP报文段包含SYN标志和应答标志(即FIN = 1,ACK = 1),seq = 1,ack = 2。这里的seq为机器B的序号,记作y = 1,这里的ack = x + 1;
第三条TCP报文段:机器A发送确认,Flags[.]表示该TCP报文段包含应答标志(即ACK = 1),表示收到应答,ack = y + 1,按照图示机器B应该给机器A发一次确认报文,但是这里为什么少一次确认,原因是:仅用于确认目的的确认报文段是可以省略的,因为第二条TCP结束报文段也携带了该确认信息,确认报文段是否在连接断开的过程中,取决于TCP的延迟确认特性
更改抓包指令结果如下图:
sudo tcpdump -S -i ens33 -nt '(src 192.168.1.106 and dst 192.168.1.149) or (src 192.168.1.149 and dst 192.168.1.106)'
这里可以看到,在tcpdum指令后增加-S选项后,TCP结束报文段的seq和ack均变为绝对值。
虽然很多书籍教程上都讲了三次握手和四次挥手,以及相关分析。但在实际抓包的过程中可能与理论结果不一样,经过亲自实践,找到其中不一样的原因,再去理解这些相关理论知识会更加深刻。
参考:
计算机网络第六版——谢希仁
Linux高性能服务器编程.pdf
Linux Socket tcpdump/wireshark 只能捕获三次挥手
利用tcpdump抓包工具监控TCP连接的三次握手和断开连接的四次挥手