TCP连接三次握手
TCP协议在双方建立连接的时候需要三次握手, 所谓的三次握手即TCP连接的建立。这个连接必须是一方主动打开,另一方被动打开的。以下为客户端主动发起连接的图解:
其中比较重要的字段有:
SYN(synchronous建立连接) ACK(acknowledgement 确认) PSH(push传送) FIN(finish结束)
RST(reset重置) URG(urgent紧急)
tips
不要将确认序号Ack与标志位中的ACK搞混了。确认方Ack=发起方Seq+1,两端配对
为什么要进行第三次握手?
为了防止服务器端开启一些无用的连接增加服务器开销以及防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误。
抓包剖析
TCP协议是面向连接的通信协议,即传输数据之前,在客户端和服务端需要建立逻辑连接,然后再传输数据。
客户端与服务端在进行连接建立的时候,需要经过三个步骤,这个3个步骤相当于3次握手。
- 客户端到服务端: 我要连接
- 服务端到客户端: 好的,已经连接上了
- 客户端到服务端: 收到,确认已连接上了
我们可以借助于一个工具wireshark来抓取TCP客户端与服务端建立连接时数据传输的过程。
客户端代码:
// 创建Socket对象
Socket socket = new Socket("127.0.0.1", 8080);
// 让线程休眠60s
TimeUnit.SECONDS.sleep(60);
服务端代码
// 创建ServerSocket对象
ServerSocket serverSocket = new ServerSocket(9999) ;
// 让线程休眠60s
TimeUnit.SECONDS.sleep(60);
首先运行服务端,然后在运行客户端,在wireshark工具中捕获完整的通信过程,结果如下图所示:
ip.src == 127.0.0.1 and tcp.port==8080
在第一次"握手"时,客户端向服务端发送SYN标志位,目的是与服务端建立连接。Seq代表sequence number(发送数据流序号), 例如:Seq的值是5,说明在数据流中曾经一共发送了 1, 2, 3,4 这4次数据。而在本次"握手"中, Seq的值是0,代表客户端曾经没有给服务端发送数据。另外Len=0也可以看出来是没有数据可供发送的,客户端仅仅发送一个SYN标志位到服端代表要进行连接。
第二次"握手"时,服务端向客户端发送 SYN ACK 标志位,其中ACK标志位表示是对收到的数据包的确认,说明服务端接收到了客户端的连接。ACK的值是1,表示服务端期待下一次从客户端发送数据流的序列号是1,而Seq=0代表服务端曾经并没有给客户端发送数据,而本次也没有发送数据,因为Len=0也证
明了这一点。
第三次“握手”时,客户端向服务端发送的ACK标志位为1, Seq的值是1。Seq=l代表这正是服务端所期望的Ack=1。Len=0说明客户端这次还是没有向服务端传递数据,而客户端向服务端发送ACK 标志位为1的信息,说明客户端期待服务端下一次传送的Seq的值是1。
个人记忆技巧:
其实可以看成客户端和服务器确认双方接受和发送都没问题的过程
1、客户端第一次握手到服务端,服务端知道客户端发送没问题,服务端也知道自己接受没问题,但客户端还不知道自己的发送有没有问题,因此有了二次
2、服务端第二次握手到客户端,此时客户端知道自己发送没问题了,也知道服务端发送没问题,同时也知道自己接受也没问题,但服务端还不知道自己的发送有没有问题。因此有了三次
3、客户端第三次握手到服务端,服务端知道自己的发送也没问题了,可以传输数据了
TCP的四次挥手
所谓的四次挥手即TCP连接的释放(解除)。连接的释放必须是一方主动释放,另一方被动释放。以下为客户端主动发起释放连接的图解:
其中比较重要的字段有:
SYN(synchronous建立连接) ACK(acknowledgement 确认) PSH(push传送) FIN(finish结束)
RST(reset重置) URG(urgent紧急)
为什么客户端在TIME-WAIT阶段要等2MSL?
MSL指的是Maximum Segment Lifetime:一段TCP报文在传输过程中的最大生命周期。2MSL即是服务器端发出为FIN报文和客户端发出的ACK确认报文所能保持有效的最大时长
抓包剖析
客户端与服务端在断开连接的时候需要进行4次"挥手",4次"挥手"的过程如下:
- 客户端到服务端:我关了
- 服务端到客户端:好的,收到
- 服务端到客户端:我也关了
- 客户端到服务端:好的,收到
我们也可以借助于wireshark这个抓包工具来抓取连接断开的整个数据传输的过程。
ip.src == 127.0.0.1 and tcp.port==8080
客户端代码
// 创建Socket对象
Socket socket = new Socket("127.0.0.1", 8080);
// 线程休眠
TimeUnit.SECONDS.sleep(10);
// 关闭连接
socket.close();
服务端代码
// 创建ServerSocket对象
ServerSocket serverSocket = new ServerSocket(8080);
// 获取连接对象
Socket socket = serverSocket.accept();
// 线程休眠
TimeUnit.SECONDS.sleep(10);
// 释放资源
socket.close();
serverSocket.close();
首先运行服务端,然后在运行客户端,在wireshark工具中捕获完整的通信过程,结果如下图所示:
在第一次"挥手"时,客户端到服务器发送标志位FIN ACK,告知服务端客户端关闭了。Seq=1表示本次数据流的序号为1,Ack=1表示客户端期望服务端下一次发送的数据流的序号为1。len=0,说明没有数据传输到服务端。
在第二次"挥手"时,服务端向客户端发送标志位ACK,Seq=1代表的正是客户端想看的Ack=1。Ack=2表示服务端期望下一次客户端发送的数据流的序号为2。len=0,说明没有数据传输到客户端。
在第三次"挥手"时,服务端向客户端发送标志位FIN ACK,告知客户端服务端关闭了。Seq=1代表的正是客
户端想看的Ack=1。Ack=2表示服务端期望下一次客户端发送的数据流的序号为2。len=0,说明没有数据传输到客户端。
在第四次"挥手"时,客户端向服务端发送标志位ACK,告知服务端客户端已经收到服务端关闭信息。Seq=2代表的正是服务端想看的Ack=2,ACK=2表示客户端期望下一次服务端发送的数据流的序号为2。
个人记忆技巧
可以看成是一个打电话的过程
1、客户端告诉服务端我要挂了
2、服务端说好的,我把要你的事情都办好和你说,你在挂
3、服务端说,办好了,你可以挂了,那我也挂了哈
4、客户端说,好的,挂了吧