tcp三次握手和四次挥手的描述很多,但是感觉大部分文章看下来很难具象化这个过程,下面使用这个wireshark抓包工具,通过抓取到的数据包来理解一下,能够从另一个角度理解tcp三次握手了

完整案例代码已上传github: github.com/neatlife/my…

wireshark下载地址:www.wireshark.org/download.ht…

创建案例项目

可以在idea中创建一个maven项目,创建用来测试3次握手和4次挥手的两个模块,创建模块在idea右键菜单里就有:



把握手和挥手分开测试是为了防止客户端在发送过程中,服务端就提前关闭了,然后挥手的部分包可能会丢掉,这个丢包的效果可以自行尝试

编写握手客户端和服务端

服务端

建立一个socket服务器,并在8881端口上监听,核心代码如下:

ServerSocket socket = new ServerSocket(8881);
socket.accept();
TimeUnit.SECONDS.sleep(2);
socket.close();
复制代码

客户端

连接上面的服务器,并发送几次数据,每次发送几个字节的数据,核心代码如下:

Socket socket = new Socket("127.0.0.1", 8881);
OutputStream outputStream = socket.getOutputStream();
outputStream.write("111".getBytes());
outputStream.write("1111l".getBytes());
outputStream.write("11111l111l".getBytes());
socket.close();
复制代码

编写挥手客户端和服务端

服务端

核心代码如下:

ServerSocket serverSocket = new ServerSocket(8881);
Socket socket = serverSocket.accept();
socket.close();
serverSocket.close();
复制代码

客户端

核心代码如下:

Socket socket = new Socket("127.0.0.1", 8881);
socket.close();
TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
复制代码

编写wireshark捕获过滤器

wireshark抓的包已经上传到github,可以直接使用wireshark打开案例项目里的握手包:handshake.pcapng,和挥手包:wave.pcapng

捕获过滤器如下:

host 127.0.0.1 and port 8881
复制代码

填写到wireshark中:



启动抓包

抓取的握手的包

运行握手模块的服务端和客户端,查看wireshark抓到的包


右键新窗口可以查看大图

注意上面这个[TCP Window Update]包可以忽略,这个包是服务端用来控制客户端发送包的频率的,这个包不一定会有,具体可以参考TCP传输协议抓包经验

抓取挥手的包



分析抓到的包

预备知识: 要看懂上面wireshark抓的包,下面的这个指令需要提前了解下

标志位

作用

SYN

表示请求建立连接

FIN

表示请求断开连接

Seq

表示曾经发送过数据的字节数+1,0表示之前没有发送过数据

Len

本次收到的数据字节大小,0表示本次没有收到数据

ACK

表示对对方过来的请求的确认

Ack

表示期待下次对方发送过来的Seq指令的值

注意:

  1. 即使Seq不为0,并不表示曾经一定发送过数据,是否发送过数据需要依据Len指令的值有没有大于0的来判断。
  2. 3次握手和4次挥手过程中一般是不会传送数据的,所以这个过程中Len指令都是0

经微信读者“R.飞”指正,4次挥手过程中是可以传数据的(未验证,有兴趣的读者可以自行验证)

经微信读者“ヤキモチ”指正,3次握手过程中的第三次握手也可以传数据的(未验证,有兴趣的读者可以自行验证)



怎么确认握手有3次

握手过程中没有只有指令交互,没有数据交互,所以开始的Len=0的所有交互都属于握手,效果如下



通过这个图,可以得出以下结论

  1. 客户端发送了两次请求到服务端,服务端发送了一次请求到客户端
  2. 第一次握手是客户端请求服务端,发送的syn指令表示客户端想要和服务端建立连接
  3. 第二次握手是服务端收到客户端发送的syn指令后,给客户端发送的ack指令,表示接受客户端想要建立连接的请求,同时服务端也给客户端发送了syn指令,表示服务端请求想要和客户端建立连接
  4. 第三次握手是客户端知道了服务端同意连接了,并且也想发送数据给客户端,客户端回应一个ack,表示允许服务端给客户端发送数据

怎么确认挥手有4次

握手过程中没有只有指令交互,没有数据交互,通过这个特性,从后往前数,找到所有Len=0的包即可,可以看到,只有4个这样的包


通过上面这个图,可以得出以下结论

  1. 客户端发送了两次请求到服务端,服务端发送了两次请求到客户端
  2. 挥手首先是客户端发起的
  3. 第一次挥手是客户端向服务端请求想要断开连接(FIN指令)
  4. 第二次挥手是服务端收到客户端的请求,并发送可以断开了的确认消息给客户端(ACK指令)
  5. 第三次挥手是服务端向客户端请求想要断开连接(FIN)
  6. 第四次挥手是客户端收到服务端的请求,并发送可以断开了的确认消息给服务端(ACK)

一些注意的点

即使这是个简单的分析也参考了一些资料

TCP传输协议抓包经验

<<NIO与Socket编程技术指南 (Java核心技术系列)>>

<<TCP/IP详解·卷1:协议(原书第2版)>>

ACK和Ack作用是不同的,ACK表示确认收到了数据,Ack表示下一次发过来的数据的序号

如果idea无法识别子模块中的java文件,可能需要重新import下模块的pom.xml

如果在操作过程中遇到问题,可加作者微信探讨