前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用 tcpdump 来了解 tcp 工作流程

使用 tcpdump 来了解 tcp 工作流程

原创
作者头像
祥祥
修改2021-01-25 10:36:24
1K0
修改2021-01-25 10:36:24
举报
文章被收录于专栏:大写的CPP大写的CPP

环境准备

我用的是 win10 下的 WSL2 环境。

  • WSL2 下安装 tcpdump,netcat
  • 安装 wireshark 

下面示例基本流程是:

  1. 启动 tcpdump 进行抓包,抓包结果写入到文件里
  2. 使用 netcat 启动简单的 tcp 监听服务
  3. 然后使用 netcat 连接此服务,发送若干数据,让 tcpdump 有包可抓
  4. 在 windows 下使用 wireshark 分析包的详细数据

构造样例

进入 WSL2 环境下,使用 ifconfig 查看网卡信息:

代码语言:shell
复制
xiang@MSI:/mnt/c/Users/xiang$ ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.29.184.251  netmask 255.255.240.0  broadcast 172.29.191.255
        inet6 fe80::215:5dff:fe5e:ed92  prefixlen 64  scopeid 0x20<link>
        ether 00:15:5d:5e:ed:92  txqueuelen 1000  (Ethernet)
        RX packets 2091  bytes 306842 (306.8 KB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 79  bytes 6426 (6.4 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 371  bytes 4049004 (4.0 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 371  bytes 4049004 (4.0 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

由于我们接下来直接在 127.0.0.1 下测试,所以我们只需要监听此 ip 下的数据即可。

代码语言:shell
复制
sudo tcpdump -i lo  # 再加上 -w filename 可以把抓包内容写到文件里;可以先不写文件,直接在命令行下查看结果

进入 WSL2 下,执行命令,启动监听 8000 端口的 tcp 服务。

代码语言:shell
复制
nc -l 8000

打开新的 WSL2 窗口,执行命令,连接本机的 8000 端口

代码语言:shell
复制
nc 127.0.0.1 8000

这时,我们看到 tcpdump 下有内容输出:

代码语言:shell
复制
12:07:24.920952 IP localhost.47152 > localhost.8000: Flags [S], seq 2397915828, win 65495, options [mss 65495,sackOK,TS val 2047998107 ecr 0,nop,wscale 7], length 0
12:07:24.920972 IP localhost.8000 > localhost.47152: Flags [S.], seq 3312485390, ack 2397915829, win 65483, options [mss 65495,sackOK,TS val 2047998107 ecr 2047998107,nop,wscale 7], length 0
12:07:24.920979 IP localhost.47152 > localhost.8000: Flags [.], ack 1, win 512, options [nop,nop,TS val 2047998107 ecr 2047998107], length 0

这就是 tcp 的三次握手

01 本机 47152 端口向 本机 8000 端口发送标志位 SYN 的数据包

  • [S] 代表 SYN
  • seq 代表客户端初始序列号,
  • win 是通告窗口大小,
  • options 是 tcp 选项,包括
    • mss tcp 最大分段长度
    • sackOK 代表可以支持 sack 功能
    • wscale 是 7,代表后续传输的 win 的大小要乘以 2 的 7次方,才是真正的通告窗口大小。(因为 tcp 头部通告窗口大小只有 16bit,最大表示 65536,在高速网络时代吃不满带宽,所以在 tcp 选项里加了 wscale 的功能,便于高速传输)
    • TS val 是当前 tcp 分段的时间戳(并不是真正意义的时间戳,只是为了区分包的创建时间,防止接收到)
    • ecr - Echo Reply,是上次对方 TS val 的值。因为这里是第一次 SYN,所以为 0
    • nop 是对齐补位,无实际意义
  • length 0 实际传输的内容是空的

02 8000 服务端发送 ACK 响应 SYN,同时也会发送 SYN

  • [S.] 代表 SYN + ACK
  • seq 是服务端初始序列号
  • ack 是客户端的初始序列号 +1
  • ...
  • options
    • sackOK 代表服务端也支持 sack,则双方可以使用 sack 功能通信
    • ecr ,这里的 ecr 就是上一个包的 TS val值

03 客户端的第三次 ack

  • [.] 代表 ACK

我们在 nc 客户端下输入 hello,然后回车,这时候服务端会收到 hello,tcpdump 数据:

代码语言:javascript
复制
12:34:34.016955 IP localhost.47152 > localhost.8000: Flags [P.], seq 1:7, ack 1, win 512, options [nop,nop,TS val 2049627203 ecr 2047998107], length 6
12:34:34.017003 IP localhost.8000 > localhost.47152: Flags [.], ack 7, win 512, options [nop,nop,TS val 2049627203 ecr 2049627203], length 0

01 客户端发送 hello

  • [p.]代表 PUSH + ACK,客户端向服务端 push 数据
  • seq 显示 1:7 代表发送的是那些字节的数据,
    • 实际 seq 的值并不是 1:7,这里是 tcp 客户端为了便于查看,对初始序列号做了减法
  • 这里的 win 是 512,实际的通告窗口大小是 512 * 2^7(7 是握手时候的 wscale) = 65536

02 服务端 ack

在 nc 客户端下按 ctrl + c 断开连接,tcpdump 的数据:

代码语言:javascript
复制
12:43:32.214698 IP localhost.47152 > localhost.8000: Flags [F.], seq 7, ack 1, win 512, options [nop,nop,TS val 2050165400 ecr 2049627203], length 0
12:43:32.214879 IP localhost.8000 > localhost.47152: Flags [F.], seq 1, ack 8, win 512, options [nop,nop,TS val 2050165401 ecr 2050165400], length 0
12:43:32.214949 IP localhost.47152 > localhost.8000: Flags [.], ack 2, win 512, options [nop,nop,TS val 2050165401 ecr 2050165401], length 0

只有三次挥手!所以说:tcp 断开连接 【一般】是四次挥手。这里是三次是因为 传统意义的 2、3 步骤合并了。

01 客户端主动断开,发送 FIN + ACK

02 服务端接收到后,发送 ACK(=客户端 seq+1),同时发送 FIN(因为服务端也没有数据要发送给客户端了,所以直接合并成一条了)

03 客户端 last ack

分析包详细内容

tcpdump 在命令行下输出只是简要信息,想要看整个数据包内容的话,可以先把数据包写入到文件,然后用 wireshark 分析。

使用命令监听 lo 网卡,并写入 dumpexample 文件里。

代码语言:shell
复制
sudo tcpdump -i lo -w dumpexample

按照上述的步骤:打开监听,连接,发送 hello,断开。

然后在 dumpexample 下 ctrl+c 停止监听,得到 dumpexample 文件。

使用 wireshark 打开文件。

这里能够看到每个请求的非常详细的信息,包括 数据链路层头部、网络层头部,以及完整的 tcp 层头部。

dump 出的请求内容
dump 出的请求内容

比如,这次三次握手的 标志位的变化:

握手1:SYN
握手1:SYN
握手2:ACK + SYN
握手2:ACK + SYN
握手3: ACK
握手3: ACK

这是三次挥手的 标志位的变化:

挥手1:FIN
挥手1:FIN
挥手2:ACK + FIN
挥手2:ACK + FIN
挥手3:ACK
挥手3:ACK

这是发送了 hello 数据的那一条消息:

TCP 部分数据
TCP 部分数据

结语

使用 netcat tcpdump wireshark 可以很方便的 构造 TCP 请求、监听 分析 TCP 报文。(netcat -u 还可以指定 UDP socket)

通过真实的 TCP 报文可以真切的感受到 TCP 的整个工作细节。

上述只是一个简单的例子,TCP 的很多技术点也都没有涉及到,比如:

  • 使用 nc host port < filename 可以把 filename 作为内容进行请求,如果文件够大就可以看到 tcp 请求按照 MSS 进行分段
  • 数据传输过程中,可以看到通告窗口的变化,以及通告窗口可以减小到 0
  • nc 传输过程中,强制 ctrl+c 结束,会发送 RST 标志位的报文,然后直接断开了连接

纸上得来终觉浅,绝知此事要躬行。尝试以下吧。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 环境准备
  • 构造样例
  • 分析包详细内容
  • 结语
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档