本文简单对 TCP 协议的三次握手、数据传输、四次挥手过程进行抓包分析。
一. 抓包准备
首先本地通过套接字实现一个 TCP 通信,然后通过 Wireshark 抓包,套接字通信代码如下:
- Server 服务端
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
public class Server {
public static void main(String[] args) throws IOException {
int port = 8919;
ServerSocket socket = new ServerSocket(port);
Socket acceptSocket = socket.accept();
InputStream inputStream = acceptSocket.getInputStream();
Reader reader = new InputStreamReader(inputStream);
char chars[] = new char[40];
int len;
while ((len = reader.read(chars)) != -1) {
System.out.println(chars);
System.out.println(len);
}
reader.close();
acceptSocket.close();
socket.close();
}
}
- Client
import java.io.*;
import java.net.Socket;
public class EchoClient {
public static void main(String[] args) throws IOException {
String host = "127.0.0.1";
int port = 8919;
Socket socket = new Socket(host, port);
Writer writer = new OutputStreamWriter(socket.getOutputStream());
writer.write("Hello From Client");
writer.flush();
writer.close();
socket.close();
}
}
开启 Server 之后打开 Wireshark 准备抓包。首先运行 Server,然后运行 Client,Server 端只处理一次请求就会关闭,其端口为 8919,因此通过 Wireshark 可以获得抓包记录如图所示:
这里有一点需要注意,Wireshark 抓包默认采用的是相对 Seq,而不是真实的 Seq,所以你看到上面的包的 Seq 是从 0 开始的,你可以从 Preferenct -> Protocols -> TCP
中修改,将相对 Seq 关闭即可。如果所示:
现在在抓包的话展示的就是真实的 Seq 了,如图:
OK,完成了基本的数据收发与抓包操作,下面就可以分析了。
二. 建立连接 - TCP 的三次握手
下面是三次握手通信过程的报文,我们逐条解析。
49822 → 8919 [SYN] Seq=0 Win=65535 Len=0 MSS=16344 WS=64 TSval=292831887 TSecr=0 SACK_PERM=1
8919 → 49822 [SYN, ACK] Seq=0 Ack=1 Win=65535 Len=0 MSS=16344 WS=64 TSval=292831887 TSecr=292831887 SACK_PERM=1
49822 → 8919 [ACK] Seq=1 Ack=1 Win=408256 Len=0 TSval=292831887 TSecr=292831887
1. 客户端发送 SYN 同步消息
SYN
表示这是收发数据前的同步消息,表示客户端请求向服务端发送数据。 Seq 表示数据包序号,这里是初始的数据包序号。这句话的含义是:
现在传递的数据包序列号为 0,如果接收无误,请允许我传递序号为 1 的数据包。
2. 服务端 SYN + ACK 应答
服务端接收到发送信息请求之后就要做出响应,其也会有自己的数据包序列号 Seq,同时会对客户端的 Seq 做 ACK 应答。报文中为 ```Seq=0 Ack=1`。这里表示
Seq=0 的含义:我现在传递的数据包序号为 0,请通知我向你传递序号为 1 的包。
Ack=1 的含义:刚才 SEQ 为 0 的数据包接收无误,你可以向我传递序号为 1 的包了。
可以看到服务端对客户端的 SYN 做出了响应,并且向客户端请求发送数据的请求。客户端的请求服务端要做响应,客户端自然也要响应服务端的请求。
3. 客户端 ACK 应答
报文信息是
49822 → 8919 [ACK] Seq=1 Ack=1 Win=408256 Len=0 TSval=292831887 TSecr=292831887
可以看到其 SEQ 为 1,对应了上面的 ACK ,然后其响应的 ACK 为 1,表示服务端可以向其发送序号为 1 的包了。
这样双方都完成了一次 SYN 请求和 ACK 应答,服务端和客户端已经准备就绪,接下来就是收发数据了。
三. TCP 数据发送
抓取到的数据发送的报文为:
49822 → 8919 [PSH, ACK] Seq=1 Ack=1 Win=408256 Len=17 TSval=292831891 TSecr=292831887
8919 → 49822 [ACK] Seq=1 Ack=18 Win=408256 Len=0 TSval=292831891 TSecr=292831891
这里面有几个字段需要了解下:
- Len:表示本次发送数据的字节数,可以看到这里是 17 字节。
- Win:发送端的通告的窗口大小。
首先客户端向服务端发送了 17 字节大小的数据,SEQ 数据包序号为 1,然后服务端的应答的 ACK 为 18。之所以变为 18 原因在于:
ACK号的增量为传输数据的字节数,即应答 ACK 号 = Seq 号 + 传递的字节数。如果字节数为 0,那么 ACK 号 = Seq 号 + 1。
通过字节数可以明确知道一条请求中的数据是否全部传输成功。
四. 断开连接 - TCP 的四次挥手
56337 → 8919 [FIN, ACK] Seq=972348271 Ack=2579650550 Win=408256 Len=0 TSval=305237464 TSecr=305237464
8919 → 56337 [ACK] Seq=2579650550 Ack=972348272 Win=408256 Len=0 TSval=305237464 TSecr=305237464
8919 → 56337 [FIN, ACK] Seq=2579650550 Ack=972348272 Win=408256 Len=0 TSval=305237465 TSecr=305237464
56337 → 8919 [ACK] Seq=972348272 Ack=2579650551 Win=408256 Len=0 TSval=305237465 TSecr=305237465
四条报文表示的信息分别是:
- 客户端发送了一条
FIN
信息表示请求断开连接 - 服务端返回
ACK
应答表示可以断开连接了 - 服务端发送了一条
FIN
信息请求断开连接 - 客户端发送
ACK
应答表示可以断开
以上就是 TCP 通过三次握手、四次挥手进行连接的建立与断开以及数据收发过程的简要分析。果然像 TCP 协议这种朴实无华且枯燥的知识点还是要实际动手操作学起来效果更好一些。