4. 抓取第一个Socket通信包:编写Python TCP客户端/服务器代码、启动抓包、过滤tcp.port==xxxx、观察三次握手(SYN, SYN-ACK, ACK)
好,到了真正动手的时候了。前面几章我们聊了Wireshark的基本界面、过滤语法,也讲了TCP协议栈的大致模样。但说实话,光看不练是学不会网络分析的。这一章,我们亲手造一个TCP连接出来,然后用Wireshark把它“抓现行”。
我个人的习惯是,学任何协议都先写一个最小可用的Demo。这样你能完全控制通信的每一个细节,出了问题也知道从哪里查起。咱们今天就写一个最简单的Python TCP客户端和服务器,让它们握一次手,然后看看Wireshark里到底长什么样。
4.1 编写Python TCP服务器与客户端
先别急着打开Wireshark。咱们先把代码跑起来,确认能通,再开始抓包。这样能避免抓了一堆无关流量,自己都看花了眼。
4.1.1 服务器端代码
服务器的作用很简单:监听一个端口,等客户端来连。连上之后,打印一条消息,然后发回一个“Hello, Client”的响应。嗯,就这些。
# tcp_server.py
import socket
def main():
# 1. 创建TCP socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2. 绑定地址和端口
server_address = ('127.0.0.1', 8888)
server_socket.bind(server_address)
# 3. 开始监听,最大等待队列长度为5
server_socket.listen(5)
print(f"[服务器] 正在监听 {server_address[0]}:{server_address[1]}")
# 4. 接受客户端连接
client_socket, client_address = server_socket.accept()
print(f"[服务器] 收到来自 {client_address} 的连接")
# 5. 接收数据
data = client_socket.recv(1024)
print(f"[服务器] 收到: {data.decode()}")
# 6. 发送响应
response = "Hello, Client"
client_socket.send(response.encode())
print(f"[服务器] 发送: {response}")
# 7. 关闭连接
client_socket.close()
server_socket.close()
if __name__ == "__main__":
main()
4.1.2 客户端代码
客户端更简单:连上去,发一条消息,收回复,然后关掉。注意,这里我故意让客户端先发数据,这样你能看到完整的请求-响应过程。
# tcp_client.py
import socket
def main():
# 1. 创建TCP socket
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2. 连接服务器
server_address = ('127.0.0.1', 8888)
print(f"[客户端] 正在连接 {server_address[0]}:{server_address[1]}")
client_socket.connect(server_address)
# 3. 发送数据
message = "Hello, Server"
client_socket.send(message.encode())
print(f"[客户端] 发送: {message}")
# 4. 接收响应
data = client_socket.recv(1024)
print(f"[客户端] 收到: {data.decode()}")
# 5. 关闭连接
client_socket.close()
if __name__ == "__main__":
main()
4.2 启动抓包:三步走
代码写好了,怎么抓?我一般按这个顺序来,不容易漏包。
- 先开Wireshark,选对网卡。 我们用的是127.0.0.1(回环地址),所以选Loopback或者叫“Adapter for loopback traffic capture”。不同系统名字不一样,Windows上叫“Npcap Loopback Adapter”,macOS/Linux上直接选“lo0”或“any”。
- 设置过滤条件。 在过滤栏输入
tcp.port == 8888,然后点那个绿色小箭头应用过滤。这样Wireshark就只显示跟8888端口相关的包,其他乱七八糟的流量全被挡在外面。 - 开始抓包。 点那个鲨鱼鳍图标(或者双击网卡名字),Wireshark就开始收了。这时候先别急着跑代码,等个一两秒,确认抓包已经开始。
4.3 运行代码并观察三次握手
好,现在Wireshark已经在监听了。打开两个终端:一个跑服务器,一个跑客户端。
# 终端1:启动服务器
$ python tcp_server.py
[服务器] 正在监听 127.0.0.1:8888
# 终端2:启动客户端
$ python tcp_client.py
[客户端] 正在连接 127.0.0.1:8888
[客户端] 发送: Hello, Server
[客户端] 收到: Hello, Client
客户端跑完,立刻回到Wireshark,点那个红色方块停止抓包。这时候你应该能看到几行记录。如果过滤条件设对了,应该只有4-5个包,非常干净。
咱们把这几个包拆开看。下面这张图是我根据抓包结果画的,帮你理解每个包的角色。
4.4 逐包分析:Wireshark里看到了什么
现在咱们对照着Wireshark的抓包结果,一包一包地看。我假设你抓到的包跟我下面描述的一致。
| 包序号 | 源IP:端口 | 目标IP:端口 | Flags(标志位) | Seq(序列号) | Ack(确认号) | 含义 |
|---|---|---|---|---|---|---|
| 1 | 127.0.0.1:49152 | 127.0.0.1:8888 | SYN | 0 | 0 | 客户端请求建立连接 |
| 2 | 127.0.0.1:8888 | 127.0.0.1:49152 | SYN, ACK | 0 | 1 | 服务器同意连接,并确认收到SYN |
| 3 | 127.0.0.1:49152 | 127.0.0.1:8888 | ACK | 1 | 1 | 客户端确认收到SYN-ACK,连接建立 |
| 4 | 127.0.0.1:49152 | 127.0.0.1:8888 | PSH, ACK | 1 | 1 | 客户端发送数据 "Hello, Server" |
| 5 | 127.0.0.1:8888 | 127.0.0.1:49152 | PSH, ACK | 1 | 14 | 服务器发送数据 "Hello, Client" |
这里有几个细节,我当年学的时候踩过坑,跟你分享一下。
4.4.1 为什么客户端的端口是49152?
你可能会问:我代码里没写客户端端口啊,怎么冒出来一个49152?这是操作系统自动分配的临时端口(Ephemeral Port)。TCP连接需要一对IP和端口:服务器是固定的8888,客户端是系统随机挑一个没被占用的端口。49152是动态端口的起始范围,你每次跑可能不一样,这很正常。
4.4.2 序列号为什么从0开始?
Wireshark默认显示的是“相对序列号”(Relative Sequence Number),把三次握手的初始序列号显示为0。实际上,TCP握手时双方会各自生成一个随机的初始序列号(ISN),但Wireshark为了让你看得清爽,帮你做了归一化。你可以在Wireshark的Preferences里关掉这个功能,看到真正的原始序列号。不过我个人建议新手阶段就开着,省得被大数字搞晕。
4.4.3 确认号为什么是1?
看第2个包:服务器发SYN-ACK,Ack=1。这个1怎么来的?因为客户端发的SYN包虽然Seq=0,但它占用了1个序列号空间。服务器说“我收到了你的0号包,下次请从1号开始发”。同理,第3个包客户端回ACK,Ack=1,意思是“我收到了你的0号包(服务器的SYN),下次请从1号开始”。
4.5 在Wireshark中验证三次握手
光看表格还不够,你得学会在Wireshark的界面里找到这些信息。我建议你按以下步骤操作:
- 选中第一个SYN包,在中间的面板里展开 Transmission Control Protocol 这一行。
- 看 Flags 字段:0x002 (SYN)。这就是SYN标志位被置1的证据。
- 看 Sequence Number (raw):这里显示的是原始序列号,通常是个很大的随机数。但Wireshark在Summary列里帮你算好了相对值。
- 再看第二个包,Flags字段变成0x012 (SYN, ACK)。注意,ACK标志位也亮了。
- 第三个包,Flags字段是0x010 (ACK)。只有ACK,没有SYN了。
嗯,到这里,三次握手就完整了。从第四个包开始,PSH标志位出现,表示开始推送实际数据了。
4.6 本章小结
这一章我们亲手造了一个TCP连接,从代码到抓包到分析,走了一个完整的闭环。你学会了:
- 用Python写一个最小化的TCP客户端和服务器
- 在Wireshark里设置端口过滤,精准捕获目标流量
- 识别三次握手的三个包:SYN、SYN-ACK、ACK
- 理解序列号和确认号在握手过程中的变化规律
说实话,三次握手是TCP协议里最基础也最重要的概念。后面讲连接状态、超时重传、流量控制,全都建立在这个基础之上。你现在能亲手抓到并看懂这三个包,后面的路就好走多了。