tcpdump详解&实战

tcpdump详解&实战作为程序员,网路问题经常遇到,比如(time_wait,比如连接超时),熟悉抓包工具,会使我们排查网络问题时候,事半功倍 !

作为程序员,网路问题经常遇到,熟悉抓包工具,会使我们排查网络问题时候,事半功倍!

由于抓包我们经常还会查看设备以及端口的一些信息,所以我们先介绍下netstat工具

netstat工具简介和使用

netstat是一个监控TCP/IP网络的非常有用的工具,它可以显示路由表、网络连接以及每一个网络接口设备的状态信息。netstat用于显示与IP、TCP、UDP和ICMP协议相关的统计数据,一般用于检验本机各端口的网络连接情况。

  • 参数解释
-a: (all) 显示所有活动中的网络连接和机器正在监听的 TCP/UDP 端口
-r: (route): 显示路由表
-n: 以数字方式显示,禁用反向域名解析,加快查询速度
(默认是不带-n参数的,如果带了-n 输出将会很快,但是只有数字类型的ip,域名是没法看到的,如果不带-n将会看到域名(如有域名的话))
-t: (tcp) 显示tcp连接
-u: (udp) 显示udp连接
-l: (Listen) 只显示监听状态的套接字
-p: 输出中显示 PID 和进程名称
-s: 显示所有端口的统计信息
-i: 查看网卡接口信息
  • 输出列名解释
Proto:显示连接使用的协议。
Recv-Q:发送队列
Send-Q:接收队列
Local Address:本地地址:端口。
Foreign Address:外部地址:端口。
state:显示套接口当前的状态。
  • 常用命令
netstat -at          #查看当前已经建立的tcp连接
netstat -au          #列出所有 udp 端口
netstat -lt          #只列出所有监听中的 tcp类型的 端口 
netstat -lu          #只列出所有监听中的 udp类型的端口
netstat -antp        #查看所有已经建立的tcp连接,并且域名以数字形式显示
netstat -tupl        #查看所有监听的tcp和udp及进程名信息(udp不能监听,但可以查看是否在占用)
netstat -s           #显示所有端口的统计信息
netstat -st 或 -su   #显示 TCP 或 UDP 端口的统计信息
netstat -i           #查看网卡接口信息
netstat -tulnp |grep nginx   #查看nginx服务的网络信息
  • 演示几个 查看所有监听状态的tcp端口(以ip形式显示) image.png

查看所有监听状态的tcp端口(如有域名的话以域名形式显示) image.png

查看所有端口情况(不进行反向域名解析) image.png

……还有很多,这里就不一一演示了,只需要使用时根据需要灵活组合即可

TCP报文基本格式

image.png

上图中 TCP 报文中每个字段的含义如下:

源端口和目的端口字段

  • TCP源端口(Source Port):源计算机上的应用程序的端口号,占 16 位。
  • TCP目的端口(Destination Port):目标计算机的应用程序端口号,占 16 位。

序列号字段

序列号字段确认号字段这两个字段是TCP可靠传输服务的关键部分,序列号是该报文段首字节的字节流编号(TCP把数据看成是有序的字节流,TCP隐式地对数据流的每个字节进行编号)。这样理解可能更直观: 当报文被分解(拆包后)成多个报文段时,序列号就是报文段首字节在整个报文的偏移量。而确认号字段则是指发送确认(ack)的一端所期望收到的下一个序号值

序列号(Sequence Number):占 32 位。 一次TCP通信(从TCP连接建立到断开)过程中某一个传输方向上的字节流的每个字节的编号。每发送一次数据,就累加一次该数据字节的大小。序列号不会从零开始,而是在建立连接时通过计算机随机生成一个数,由SYN包传给接收端主机。
假设主机A和主机B进行TCP通信,A发送给B的第一个TCP报文段中,序号值被系统初始化为某个随机值ISN(Initial Sequence Number,初始序号值)。那么在该传输方向上(从A到B),后续的TCP报文段中序号值将被系统设置成ISN加上该报文段所携带数据的第一个字节在整个字节流中的偏移。例如,某个TCP报文段传送的数据是字节流中的第1025~2048字节,那么该报文段的序号值就是ISN+1025.另外一个传输方向(从B到A)的TCP报文段的序号值也具有相同的含义

确认号字段

TCP 确认号(Acknowledgment Number,ACK Number):占 32 位。发送确认ack的一端所期望收到的下一个序号,标识期望收到的下一个段的第一个字节,并声明此前的所有数据已经正确无误地收到,因此,确认号应该是上次已成功收到的数据字节序列号加1。收到确认号的源计算机会知道特定的段已经被收到。确认号只有当ACK标志为1时才有效。比如建立连接时,SYN报文的ACK标志位为0。

数据偏移字段

TCP 首部长度(Header Length):由于首部可能含有可选项内容,因此TCP报头的长度是不确定的,报头不包含任何字段则长度为20字节,4位首部长度字段所能表示的最大值为1111,转化为10进制为15,15*4 = 60,故报头最大长度为60字节。首部长度也叫数据偏移,是因为首部长度实际上指示了数据区在报文段中的起始偏移值

保留字段

保留(Reserved):占 4 位。为 TCP 将来的发展预留空间,目前必须全部为 0。

标志位字段 CWR、ECE、URG、 ACK、PSH 、 RST 、 SYN、 FIN

  • CWR(Congestion Window Reduce):拥塞窗口减少标志,用来表明它接收到了设置 ECE 标志的 TCP 包。并且,发送方收到消息之后,通过减小发送窗口的大小来降低发送速率。
  • ECE(ECN Echo):用来在 TCP 三次握手时表明一个 TCP 端是具备 ECN 功能的。在数据传输过程中,它也用来表明接收到的 TCP 包的 IP 头部的 ECN 被设置为 11,即网络线路拥堵。
  • URG(Urgent):紧急指针标志,为1时表示紧急指针有效,该报文应该优先传送,为0则忽略紧急指针。
  • ACK:确认序号标志,为1时表示确认号有效,为0表示报文中不含确认信息,忽略确认号字段。我们称携带ACK标识的TCP报文段为确认报文段。TCP 规定,连接建立后,ACK 必须为 1。
  • PSH(Push):为1表示是带有push标志的数据,指示接收方在接收到该报文段以后,应优先将这个报文段交给应用程序,而不是在tcp内核缓冲区排队。
  • RST:表示是否重置连接。如果 RST=1,说明 TCP 连接出现了严重错误(如主机崩溃),必须释放连接,然后再重新建立连接。
  • SYN:在建立连接时使用,用来同步序号。当 SYN=1,ACK=0 时,表示这是一个请求建立连接的报文段;当 SYN=1,ACK=1 时,表示对方同意建立连接。SYN=1 时,说明这是一个请求建立连接或同意建立连接的报文。
  • FIN:标记数据是否发送完毕。如果 FIN=1,表示数据已经发送完成,可以释放连接,我们称携带FIN标志的TCP报文段为结束报文段。

窗口大小字段

窗口大小(Window Size):占 16 位。它表示从 Ack Number 开始还可以接收多少字节的数据量,也表示当前接收端的接收窗口还有多少剩余空间(注意:实际的窗口大小 也和Window Scale选项有关)。该字段可以用于 TCP 的流量控制。

TCP 校验和字段

校验和(TCP Checksum):占 16 位。它用于确认传输的数据是否有损坏。发送端基于数据内容校验生成一个数值,接收端根据接收的数据校验生成一个值。两个值必须相同,才能证明数据是有效的。如果两个值不同,则丢掉这个数据包。Checksum 是根据伪头 + TCP 头 + TCP 数据三部分进行计算的。
伪首部中包含: 4个字节保存源ip信息,4个字节目的ip信息,一个字节的保留位置,一个字节保存协议号(6代表tcp,17代表udp),2个字节保存tcp首部+数据的长度。

紧急指针字段

紧急指针(Urgent Pointer):仅当前面的 URG 控制位为 1 时才有意义。它指出本数据段中为紧急数据的字节数,占 16 位。当所有紧急数据处理完后,TCP 就会告诉应用程序恢复到正常操作。即使当前窗口大小为 0,也是可以发送紧急数据的,因为紧急数据无须缓存。

可选项字段-Options (这里仅罗列出常用的一些)

  1. MSS(Maximum Segment Size) tcp报文最大传输长读,tcp在三次握手建立阶段,在SYN报文交互该值,注意的是,这个数值并非协商出来的,而是由网络设备属性得出。MSS一个常见的值是1460(MTU1500 – IP头部 – TCP头部)。

  2. SACK(Selective Acknowledgements) 选择性ACK,用于处理segment不连续的情况,这样可以减少报文重传。比如: A 向B发送4个segment,B收到了1,2,4个segment,网络丢失了3这个segment。B收到1,2segment后,回应ACK 3,表示1,2这两个ACK已经收到,同时在选项字段里面,包括4这个段,表示4这个segment也收到了。于是A就重传3这个segment,不必重传4这个segment。B收到3这个segment后,直接ACK 5,表明3,4都收到了。

  3. WS(Window Scale) 在tcp头部,Window Size(16Bit)表面接收窗口大小,但是对于现代网络而言,这个值太小了。所以tcp通过选项来增加这个窗口的值。WS值的范围0~14,表示Window Size(16Bit)数值先向左移动的位数。这样实际上窗口的大小可达31位。在程序网络设计时,有个SO_RECVBUF,表示设置接收缓冲的大小,然而需要注意的是,这个值和接收窗口的大小不完全相等,但是这个数值和接收窗口存在一定的关系,在内核配置的范围内,大小比较接近。

  4. TS(Timestamps) Timestamps在tcp选项中包括两个32位的timestamp: TSval(Timestamp value)和TSecr(Timestamp Echo Reply)。如果设置了TS这个选项,发送方发送时,将当前时间填入TSval,接收方回应时,将发送方的TSval填入TSecr即可(注意发送或接收都有设置TSvalTSecr )。TS 选项的存在有两个重要作用:一是可以更加精确计算RTT(Round-Trip-Time),只需要在回应报文里面用当前时间减去TSecr即可;二是PAWS(Protection Against Wrapped Sequence number, 防止sequence回绕),什么意思呢?比如说,发送大量的数据:0-10G,假设segment比较大为1G而且sequence比较小为5G,接收端接收1,3,4,5数据段正常接收,收到的发送时间分别1,3,4,5,第2 segment丢失了,由于SACK,导致2被重传,在接收6时,sequence由于回绕变成了1,这时收到的发送时间为6,然后又收到迷途的2,seq为2,发送时间为2,这个时间比6小,是不合法的,tcp直接丢弃这个迷途的报文。

  5. UTO(User Timeout) UTO指的是发送SYN,收到ACK的超时时间,如果在UTO内没有收到,则认为对端已挂。 在网络程序设计的时候,为了探测对端是否存活,经常涉及心跳报文,通过tcp的keepalive和UTO机制也可以实现,两者的区别是,前者可以通过心跳报文实时知道对端是否存活,二后者只有等待下次调用发送或接收函数才可以断定: 1) SO_KEEPALIVE相关选项 设置SO_KEEPALIVE 选项,打开keepalive机制。 设置TCP_KEEPIDLE 选项,空闲时间间隔启动keepalive机制,默认为2小时。 设置TCP_KEEPINTVL选项,keepalive机制启动后,每隔多长时间发送一个keepalive报文。默认为75秒。 设置TCP_KEEPCNT选项,设置发送多少个keepalive数据包都没有正常响应,则断定对端已经崩溃。默认为9。 由于tcp有超时重传机制,如果对于ACK丢失的情况,keepalive机制将有可能失效

TCP状态简介

状态图

image.png

状态解释

CLOSED: 初始状态。

(三次握手)

LISTEN: 表示服务器端的某个SOCKET处于监听状态,可以接受连接了。

SYN_SENT: 这个状态与SYN_RCVD相呼应,当客户端SOCKET执行CONNECT连接时,它首先发送SYN报文,因此也随即它会进入到了SYN_SENT状态,并等待服务端的 [S.] 报文。SYN_SENT状态表示客户端已发送SYN报文。

SYN_RCVD: 这个状态表示服务端接收到了SYN报文,在正常情况下,这个状态是服务器端的SOCKET在建立TCP连接时的三次握手会话过程中的一个中间状态,很短暂,基本 上用netstat你是很难看到这种状态的,除非你特意写了一个客户端测试程序,故意将三次TCP握手过程中最后一个ACK报文不予发送。因此这种状态 时,当收到客户端的ACK报文后,它会进入到ESTABLISHED状态。

ESTABLISHED: 表示连接建立成功。

(send data  ......)

(四次挥手)

FIN_WAIT_1: 本质上FIN_WAIT_1和FIN_WAIT_2状态的真正含义都是表示等待对方的FIN报文。而这两种状态的区别是:FIN_WAIT_1状态实际上是当SOCKET在ESTABLISHED状态时,它想主动关闭连接,向对方发送了FIN报文,此时该SOCKET即进入到FIN_WAIT_1状态。而当对方回应ACK报文后,则进入到FIN_WAIT_2状态,当然在实际的正常情况下,无论对方何种情况下,都应该马上回应ACK报文,所以FIN_WAIT_1状态一般是比较难见到的,而FIN_WAIT_2状态还有时常常可以用netstat看到。

CLOSE_WAIT: 这种状态的含义其实是表示在等待关闭。怎么理解呢?当对方close一个SOCKET后发送FIN报文给自己,你系统毫无疑问地会回应一个ACK报文给对 方,此时则进入到CLOSE_WAIT状态。接下来呢,实际上你真正需要考虑的事情是查看你是否还有数据发送给对方,如果没有的话,那么你也就可以 close这个SOCKET,发送FIN报文给对方,也即关闭连接。所以你在CLOSE_WAIT状态下,需要完成的事情是等待你去关闭连接。

FIN_WAIT_2: 实际上FIN_WAIT_2状态下的SOCKET,表示半连接,也即有一方(比如client)要求close连接,但我(server)告诉对方,我暂时还有点数据需要传送给你,稍后再关闭连接。

TIME_WAIT: 表示收到了对方的FIN报文,并发送出了ACK报文。如果FIN_WAIT_1状态下,收到了对方同时带 FIN标志和ACK标志的报文时,可以直接进入到TIME_WAIT状态,而无须经过FIN_WAIT_2状态。

LAST_ACK: 这个状态还是比较容易好理解的,它是被动关闭一方在发送FIN报文后,最后等待对方的ACK报文。当收到ACK报文后,即可关闭连接了。


CLOSING: 这种状态比较特殊,实际情况中应该是很少见,属于一种比较罕见的例外状态。正常情况下,当你发送FIN报文后,按理来说是应该先收到(或同时收到)对方的 ACK报文,再收到对方的FIN报文。但是CLOSING状态表示你发送FIN报文后,并没有收到对方的ACK报文,反而却也收到了对方的FIN报文。什么情况下会出现此种情况呢?其实细想一下,也不难得出结论:那就是如果双方几乎在同时close一个SOCKET的话,那么就出现了双方同时发送FIN报文的情况,也即会出现CLOSING状态,表示双方都正在尝试关闭SOCKET连接。

tcpdump使用

tcpdump的参数介绍

-a  将网络地址和广播地址转变成名字
-d  将匹配信息包的代码以人们能够理解的汇编格式给出
-dd  将匹配信息包的代码以c语言程序段的格式给出
-ddd 将匹配信息包的代码以十进制的形式给出
-D   显示所有可用网络接口的列表
-e  在输出行打印出数据链路层的头部信息
-f  将外部的Internet地址以数字的形式打印出来
-l  使标准输出变为缓冲行形式
-L   列出指定网络接口所支持的数据链路层的类型后退出
-n  不把网络地址转换成名字,即不做反向域名解析
-q   简洁地打印输出。即打印很少的协议相关信息, 从而输出行都比较简短
-t   在每行的输出中不输出时间
-tt  在每行的输出中会输出时间戳
-ttt 输出每两行打印的时间间隔(以毫秒为单位)
-tttt 在每行打印的时间戳之前添加日期的打印(此种选项,输出的时间最直观)

-v   产生详细的输出. 比如包的TTL,id标识,数据包长度,以及IP包的一些选项。同时它还会打开一些附加的包完整性检测,比如对IP或ICMP包头部的校验和。
-vv  产生比-v更详细的输出. 比如NFS回应包中的附加域将会被打印, SMB数据包也会被完全解码。
-vvv 产生比-vv更详细的输出。比如 telent 时所使用的SB, SE 选项将会被打印, 如果telnet同时使用的是图形界面,其相应的图形选项将会以16进制的方式打印出来
-c  在收到指定的包的数目后,tcpdump就会停止
-F  从指定的文件中读取表达式,忽略其它的表达式
-i  指定监听的网络接口
-r  从指定的文件中读取包(这些包一般通过-w选项产生)
-w  直接将数据报写入文件中,并不分析和打印出来
-T  将监听到的包直接解释为指定的类型的报文,常见的类型有rpc (远程过程调用)和snmp(简单 网络管理协议)
-s   tcpdump 默认只会截取前 `96` 字节的内容,要想截取所有的报文内容,可以使用 `-s number`, `number` 就是你要截取的报文字节数,如果是 0 的话,表示截取报文全部内容。
-S  seq ack 使用绝对序列号,而不是相对序列号
-Z  后接用户名,在抓包时会受到权限的限制。如果以root用户启动tcpdump,tcpdump将会有超级用户权限



常用命令

tcpdump -i eth0   #捕获指定接口(网卡)的数据包 可通过 netstat -i查看网卡设备
tcpdump -i eth0 -c 3   #捕获指定个数的数据包(3个数据包)
tcpdump -A -i eth0    #用ASCII码格式输出捕获的数据包
tcpdump -D    #显示可用的系统接口
tcpdump -XX -i eth0   #用十六进制和ASCII码格式显示捕获的数据包
tcpdump -w tempDump.pcap -i eth0   #把捕获的数据包写入到一个.pcap后缀的文件中
tcpdump -r tempDump.pcap    #读取捕获数据包文件的内容
tcpdump -n -i eth0    # 单个 n 表示不解析域名,直接显示 IP
tcpdump -i eth0 tcp    #捕获TCP类型的数据包
tcpdump -i eth0 port 22   #捕获指定端口(这里是22)的数据包
tcpdump -i eth0 src 源ip地址   #捕获请求源是 192.169.12.101 的数据包
tcpdump -i eth0 dst 目标ip地址   #捕获指定目的IP的数据包
tcpdump -i eth6 dst host 目标ip地址 and port 8800 -w data.pcap  #抓取指定网卡,指定IP和端口的数据包 并写入到data.pcap文件中
tcpdump host 192.168.12.101 and 192.168.1.201 -w out &  #后台抓取两主机之间的数据
tcpdump -nn 单个 n 表示不解析域名,直接显示 IP;两个 n 表示不解析域名和端口。这样不仅方便查看 IP 和端口号,而且在抓取大量数据时非常高效,因为域名解析会降低抓取速度

tcpdump输出解释

tcpdump的Flags代表了这个数据包的用途,这些标记是TCP首部的内容

  • [S] : SYN(开始连接)
  • [S.]: SYN同步标识,以及确认[S]的ACK
  • [P.] : PSH(推送数据)
  • [F.] : FIN (结束连接)
  • [R.] : RST(重置连接)
  • [.] : 没有 Flag (意思是除上面四种类型外的其他情况,有可能是 ACK 也有可能是 URG:紧急指针)
  • [FP.]: 标记FIN、PUSH、ACK组合,这样做是为了提升网络效率,减少数据来回确认等

常用命令演示

(说明: 由于我们想看到快速输出 所以演示时都 添加了-nn参数 同时,mac中tcpdump命令需要sudo权限,另外我是mac所以以太网网口是en0 并不是linux的eth0)

sudo tcpdump -D

显示可用的系统接口

image.png

sudo tcpdump -nn -i en0

查看指定网卡设备下的报文

image.png

sudo tcpdump -nn -i en0 -c 3

查看指定数量的报文

image.png

sudo tcpdump -A -nn -i en0

以ASCII码来输出数据包 image.png

sudo tcpdump -XX -nn -i en0

以16进制来输出数据包

image.png

sudo tcpdump -w tempDump.pcap -nn -i en0

将抓到的数据报写入到 指定文件: tempDump.pcap image.png 注意这个tempDump.pcap文件 可以直接 读(sudo tcpdump -r tempDump.pcap) 也可以使用Wireshark或者Charles这类的抓包工具打开

sudo tcpdump -nn -i en0 tcp

只抓取tcp协议的数据报文 image.png

sudo tcpdump -nn -i lo0 tcp port 12499

注意由于是本机,不走en0接口,直接从回环接口抓取数据报文 在抓取之前我们先启动一个netty服务端和客户端,用以演示

  • server image.png
  • client image.png

抓取指定协议的,指定端口(12499)的数据报 image.png

我们这里额外看下netty-server和netty-client 三次握手的报文(停掉netty-client,重新启动即可抓到三次握手报文)

image.png

顺便我们对比下启动client前和启动client后连接的状态 image.png

sudo tcpdump -nn -i en0 host 182.61.200.6

(通过curl www.baidu.com:80 来演示不同机器间的抓包 注意此时不是回环接口 lo0 , 而是en0接口了)

根据指定ip抓取报文

image.png

sudo tcpdump -nn -i en0 src host 182.61.200.6

只抓取源ip是 百度(182.61.200.6)的数据报

image.png

sudo tcpdump -nn -i en0 dst host 182.61.200.6

只抓取目标ip是 百度(182.61.200.6)的数据报

image.png

sudo tcpdump -nn -i en0 host 182.61.200.6 and port 80

and 演示 image.png

sudo tcpdump -nn -i en0 host 182.61.200.6 or 180.149.139.248

or演示

image.png

sudo tcpdump -nn -i en0 net 182.61.200.0/24

根据网段抓取数据报

image.png

sudo tcpdump -nn -i en0 icmp

抓取ping包数据报 image.png

sudo tcpdump -nn -i en0 net 182.61.200.0/24 -vv -tttt

输出详细日志 通过-vv参数 输出时间更可读的数据通过 -tttt参数

image.png

另外 tcpdump有很多的过滤器 可以实现精准的过滤数据报文,这里不再一一罗列,在需要时候直接参考官方文档即可 tcpdump过滤器戳这里 ; tcpdump基本操作戳这里

tcpdump输出详解

在看输出前,我们看下这个三次握手图解,会更清晰些:

image.png

在做解释前,我们先启动了netty-server的代码端口 (12499),随后开启tcpdump命令进行报文监听,然后启动netty-client程序(端口 64107) ,得到下边的报文数据

直接在tcpdump分析 image.png

1.客户端请求建立连接 SYN

2022-05-03 16:07:00.004072 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 64, bad cksum 0 (->3cb6)!)
    127.0.0.1.64107 > 127.0.0.1.12499: Flags [S], cksum 0xfe34 (incorrect -> 0xfa05), seq 602197604, win 65535, options [mss 16344,nop,wscale 6,nop,nop,TS val 2538885107 ecr 0,sackOK,eol], length 0

可以看到client(64107)向server(12499)发起了SYN ( 通过Flags [S]得知),同时可以看到seq号 因为是第一次所以syn号是随机生成的,client接收窗口大小是 65535,我们还能看到mss是16344,即最大报文长度是16344,(mss选项用于在TCP连接建立时,收发双方协商通信时每一个报文段所能承载的最大数据长度),另外wscale代表窗口扩大因子,wscale指出发送端使用的窗口扩大因子为2的6次方 即 窗口最大 65535*64 (但是也和其他一些参数还有关系,具体我现在没深入),TSval并非真正的时间戳,而是由时间戳依据一定算法算出来的一个值,与时间戳有同等的特性,即随时间单调递增,(其作用是更加精确计算RTT和PAWS,这个我没深入分析,参考网络的),scakOk代表开启选择性确认是tcp对于重传时候的一种优化,由于是握手请求,所以length都是0

2.服务端返回 SYN+ACK

2022-05-03 16:07:00.004243 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 64, bad cksum 0 (->3cb6)!)
    127.0.0.1.12499 > 127.0.0.1.64107: Flags [S.], cksum 0xfe34 (incorrect -> 0xd750), seq 3457146506, ack 602197605, win 65535, options [mss 16344,nop,wscale 6,nop,nop,TS val 70676947 ecr 2538885107,sackOK,eol], length 0

通过[S.]我们可以知道这个是syn+ack报文即第二次握手时的报文 ,同时可以看到 服务端给客户端返回的报文中 ack是syn时的seq+1 即 602197605,同时还可看到服务端的接收窗口大小,以及服务端窗口扩大因子,支持的最大报文长度,以及TS val,ecr(我们发现ecr就是syn时发送给服务端的Ts val的值),同时 服务端也是支持 选择性确认的,lenth同样也是0

3.客户端ack

2022-05-03 16:07:00.004253 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 52, bad cksum 0 (->3cc2)!)
    127.0.0.1.64107 > 127.0.0.1.12499: Flags [.], cksum 0xfe28 (incorrect -> 0x385a), seq 1, ack 1, win 6379, options [nop,nop,TS val 2538885107 ecr 70676947], length 0

通过[.]可以看出这个是ack报文,通过报文流向我们知道是 客户端发给服务端的报文,seq为啥是1呢?也是惊到我了,一番查找才知道,原来tcpdump做了处理,这个值是相对值,并不是绝对值,我觉得这样并不直观,我们使用绝对值输出看下

ok加了 -S 后 ,seq 和ack都以绝对值的方式输出了,而wireshark更狠直接从0开始 image.png

这里tcpdump输出的参数就不赘述了,上边 在 1,和2都讲的差不多了

到此一条双向的tcp连接即建立成功了

  • 我们看下已经通过三次握手建立成功的tcp连接 image.png

4.连接建立成功,尝试发送数据

下面我们通过客户端发送一个消息,来看看连接建立成功后发数据时候的报文

发消息,消息内容: wwe

image.png

捕获发消息的报文数据

image.png

解释一下

  1. 客户端向服务端发送消息
2022-05-03 17:41:41.775264 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 80, bad cksum 0 (->3ca6)!)
    127.0.0.1.53823 > 127.0.0.1.12499: Flags [P.], cksum 0xfe44 (incorrect -> 0x4a80), seq 4283375569:4283375597, ack 1126787537, win 6379, options [nop,nop,TS val 2077838041 ecr 1006273516], length 28

[P.] 代表这是推送数据,从流向来看我们知道是客户端想服务端发送数据 ,整体长度为80,seq 冒号前是上次ack的值,下图中标出了上次ack的值:

image.png 冒号后则是当前seq+数据包长度后的值 即 4283375569+28= 4283375597,ack的值是上次服务端发给客户端时候的seq冒号后的值即 1126787537
(但是这里不应该是 1126787537+1吗? 我对这不是很理解,有时间还需要再查找资料理解一下没有+1的原因)

  1. 服务端收到数据后返回给客户端的ack报文:

至于其字段,就不一一描述了,总之,其seq的值和ack的值的变化是有规律的,只要我们理解了一种,其他的也就不难理解了

2022-05-03 17:41:41.775292 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 52, bad cksum 0 (->3cc2)!)
    127.0.0.1.12499 > 127.0.0.1.53823: Flags [.], cksum 0xfe28 (incorrect -> 0x2205), seq 1126787537, ack 4283375597, win 6377, options [nop,nop,TS val 1006922699 ecr 2077838041], length 0
  1. 服务端回复客户端(因为在服务端收到消息后有个服务端回复的操作):

image.png

2022-05-03 17:41:41.796202 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 101, bad cksum 0 (->3c91)!)
    127.0.0.1.12499 > 127.0.0.1.53823: Flags [P.], cksum 0xfe59 (incorrect -> 0x1009), seq 1126787537:1126787586, ack 4283375597, win 6377, options [nop,nop,TS val 1006922717 ecr 2077838041], length 49
  1. 客户端返回给服务端的ack:
2022-05-03 17:41:41.796242 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 52, bad cksum 0 (->3cc2)!)
    127.0.0.1.53823 > 127.0.0.1.12499: Flags [.], cksum 0xfe28 (incorrect -> 0x21af), seq 4283375597, ack 1126787586, win 6378, options [nop,nop,TS val 2077838059 ecr 1006922717], length 0

5. 请求结束连接

四次挥手:

image.png

我们停掉netty-client后,将有如下输出:

image.png

  1. [F.] 对应上图 step:1 代表服务端向客户端发起关闭连接请求(因为我是直接停掉server的,所以不是客户端发起,而是由服务端发起的,这里我们不用在意这个细节)
2022-05-03 18:31:05.652064 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 52, bad cksum 0 (->3cc2)!)
    127.0.0.1.12499 > 127.0.0.1.53823: Flags [F.], cksum 0xfe28 (incorrect -> 0x1891), seq 1126787586, ack 4283375597, win 6377, options [nop,nop,TS val 1009480916 ecr 2077838059], length 0
  1. [.] 对应上图step:2 代表客户端向服务端发送一个ack包
2022-05-03 18:31:05.652091 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 52, bad cksum 0 (->3cc2)!)
    127.0.0.1.53823 > 127.0.0.1.12499: Flags [.], cksum 0xfe28 (incorrect -> 0x0f72), seq 4283375597, ack 1126787587, win 6378, options [nop,nop,TS val 2080396258 ecr 1009480916], length 0
  1. [F.] 对应上图step:3 代表客户端已经处理完一些其他的任务,可以关闭了
2022-05-03 18:31:05.730812 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 52, bad cksum 0 (->3cc2)!)
    127.0.0.1.53823 > 127.0.0.1.12499: Flags [F.], cksum 0xfe28 (incorrect -> 0x0f23), seq 4283375597, ack 1126787587, win 6378, options [nop,nop,TS val 2080396336 ecr 1009480916], length 0
  1. [.] 对应上图step:4代表最后一次挥手,接到此报文后,连接资源将被释放!
2022-05-03 18:31:05.730927 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 52, bad cksum 0 (->3cc2)!)
    127.0.0.1.12499 > 127.0.0.1.53823: Flags [.], cksum 0xfe28 (incorrect -> 0x0ed6), seq 1126787587, ack 4283375598, win 6377, options [nop,nop,TS val 1009480994 ecr 2080396336], length 0

至此,关于tcpdump就结束了,然而真正掌握tcpdump还需要我们在实际中大量的实践和应用,其实这玩意没啥难的就是基于各种条件,过滤我们想要的数据报,主要是他的输出,我们想看懂,必须知道tcp的详细内容,所以本文中穿插了很多tcp相关的知识,以便我们更好的理解tcpdump的输出。而对于tcp协议,其实还是很复杂的,想深入必须读很多资料,做大量的实践才行!

参考: 《TCP-IP详解卷一》 www.cnblogs.com/xiaolincodi… www.tcpdump.org/manpages/tc…

今天的文章tcpdump详解&实战分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/22636.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注