实战中的TCP/UDP协议(wireshark抓包)

前言

本篇是上篇《面试中的TCP/UDP协议》的姊妹篇,上篇简单从概念的角度把TCP/UP协议的格式、特性解释了一下,但并没有在实际场景中看到TCP/UDP报文的格式,而走马观花的过一遍只是死记硬背,对于一些易混点还是一知半解,比如:

  1. TCP协议中的序号到底表示什么意义?
  2. 在建立连接后,如果长时间没有数据传输,TCP还会一直连接么?

为了感受实际场景中的TCP/UDP的格式,专门用wireshark来抓包, 实战分析TCP、UDP的协议内容。
看完本文会了解到的知识点:

  1. TCP协议中的序号作用
  2. TCP连接中的KeepAlive
  3. HTTP协议中的KeepAlive

HTTPS抓包实例

这里以我用浏览器访问一次https://www.baidu.com为例, 用wireshark进行抓包,重点看TCP协议在实际场景中的应用。

环境配置

  • 192.168.219.136虚拟机IP,用虚拟机中的浏览器访问https://www.baidu.com
  • 192.168.219.2虚拟机默认网关
  • 180.101.49.11是百度的ip地址
    实战中的TCP/UDP协议(wireshark抓包)_第1张图片

DNS数据包

wireshark的自动解析很不错,每个标志位的意义数值都标出来了。

实战中的TCP/UDP协议(wireshark抓包)_第2张图片
注意:

  • 正如我们在理论篇里看到的, UDP协议仅仅规定端口号,以及数据的校验
  • 浏览器首先通过DNS协议,向默认网关发起查询请求,查询www.baidu.com的ip地址

实战中的TCP/UDP协议(wireshark抓包)_第3张图片
注意:

  • 这里有趣的一点是返回了三个查询记录一个CNAME,两个IP地址
  • 经访问,两个IP地址均指向百度的首页(多IP配置可能是为了负载均衡),而www.a.shifen.com指向百度错误页
域名中的A记录和CName记录

为了更好的理解这几个返回结果的关系,先了解下A记录和CNAME记录

A记录是用来指定主机名(或域名)对应的IP地址记录。
CNAME记录用作别名,相当于域名之间的映射(例如www.baidu.com 映射到www.a.shifen.com),而不直接与IP地址对应。

在访问www.baidu.com的时候,dns协议从dns服务器进行解析,查询到www.baidu.com到www.a.shifen.com的映射记录,而与真正IP地址(180.101.49.11, 180.101.49.11)绑定的则是www.a.shifen.com这个A记录
有趣吧,经常访问的www.baidu.com居然只是一个别名

但问题来了,当我们在浏览器中直接输入www.a.shifen.com的时候,并不会出来百度的页面,而是直接倒到百度的错误页面。

猜想可能是,在发起HTTP请求的时候,输入的url会写入HTTP请求头部,这样在服务端会进行判断,发现不是www.baidu.com的时候,会显示错误页。

至于现在百度为什么还要保留这个www.a.shifen.com的DNS配置,就不得而知了,估计又是一个历史遗留问题。

TCP三次握手

第一次握手:

注意:

  • 我们在访问的时候,应用层协议是https,所以目的端口号是443
    实战中的TCP/UDP协议(wireshark抓包)_第4张图片
第二次握手:

注意:

  • 这里我们要特意留心一下序列号的变化,由于seq是随机产生的,wireshark为了便于分析,在解析的时候,使用相对序列号(Relative Sequence number)进行显示,所谓“相对”,即是把第一个TCP包(第一次握手)的序列号置为0,接下来的序列号依次增长。
  • 这里的Acknowledgment number指的是期望下次收到包的序列号(在ACK为置为1时有效),以下简写为Ack(与全大写标志位ACK区分)

因为TCP包是以流水线形式发出的,比如发送端顺序的发出 Seq=1、Seq=2、Seq=3。 那么如果ACK确认的序号和收到的包的序号一致的话,那么需要发回 Ack=1、Ack=2、Ack=3 共三个包。
但是TCP协议对此进行了优化,只需要发送一个ACK包就能代表说自己已经收到了前面三个包, 那就是发送Ack=4 (期望收到Seq为4的包)。这样节省了ACK确认的数量。

实战中的TCP/UDP协议(wireshark抓包)_第5张图片

第三次握手:

注意:

  • 这里有意思的是客户端根据服务端发来的窗口值,对自己的窗口值进行了调整,便于接受发来的数据。
    实战中的TCP/UDP协议(wireshark抓包)_第6张图片

其实TCP协议中比较核心的还是序列号的变化,光用三次握手体现不出这个关系,下面对多个数据包的数据流图来对序列号的变化进行分析。

TCP协议流图

wireshark可以绘制TCP流,放这个图主要是想理清一下包序号的之间的关系。
实战中的TCP/UDP协议(wireshark抓包)_第7张图片

前6个包序号变化
  1. 第一次握手: Seq = 0,客户端发出
  2. 第二次握手: Seq = 0, 服务端发出,Ack=1,表示想接收到下次客户端序号为1的包
  3. 第三次握手:Seq = 1, (确认包,不带数据),客户端发出,Ack=1, 表示想接收到下次服务端序号为1的包
  4. 第4个包: Seq = 1, 客户端开始向服务端发送数据(长度为153),注意!这里的序号并没有变为2,还是1,并且Ack还是1
  5. 第5个包: Seq = 1,(确认包,不带数据),注意!这里的序号并没有变为2,还是1, Ack=154,表示已收到长度为153的数据,期待下次收到序号为154的包
  6. 第6个包: Seq = 1,服务端开始向客户端发送数据(长度为138),注意!这里的序号并没有变为2,还是1, Ack=154,表示已收到长度为153的数据,期待下次收到的154的数据
总结

从上面这6个包的接收发送,我们可以总结出以下几点:

  1. TCP通信的一方,在接收到数据包后,可能会发出(1. ACK确认包(不带数据), 2. ACK确认包(带数据))或者只发出ACK确认包(带数据),需要明确的一点是,在第一种情况中,两个包的序号并没有改变并没有因为是两个包,第二个包序号就比第一个包序号多一,这有点反直觉)。

    说明了序号的增长,并不是依赖自己发包的数量和顺序,而是依赖对方已接收并确认的数据

  2. TCP序号根据数据流编码,Ack=154不仅仅表示期待收到对方序列号为4的包,另一层意思是,确认已经收到153字节,并期待下次收到第154字节起始的数据(虽然这两种理解产生的序号值都一样)

TCP KeepAlive

这里对上面总结中提到发出两个序列号一样的数据包做出进一步的解释:

仔细回看一下上面的TCP流图,我们会发现一方会向另一方发送ACK包(不带数据),而且序号与接下来要发送的数据包一致,这种数据包一方面是对已收到消息的回复,另外一种含义则是为了刺探通信的对方是否存活。 原因如下:

长连接的环境下,进行一次数据交互后,很长一段时间内无数据交互时,客户端可能意外断电、死机、崩溃、重启,还是中间路由网络无故断开,这些TCP连接并未来得及正常释放,那么,连接的另一方并不知道对端的情况,它会一直维护这个连接,长时间的积累会导致非常多的半打开连接,造成端系统资源的消耗和浪费,且有可能导致在一个无效的数据链路层面发送业务数据,结果就是发送失败。所以服务器端要做到快速感知失败,减少无效链接操作,这就有了TCP的Keepalive(保活探测)机制。 ------理解TCP长连接(Keepalive)

与下文中HTTP的KeepAlive的区别

HTTP的KeepAlive是为了保证TCP连接的连接复用
而TCP的KeepAlive是为了保证TCP连接的可用

这里比较意外的是没有抓到FIN包

猜测HTTP使用长连接,这也符合我们常规的逻辑(进入一个网站,我们通常还会获取这个网站的其他数据,如果频繁的断开,连接,会导致不小的时间开销。

以下是查询资料得到的答案:

HTTP1.1规定了默认保持长连接(HTTP persistent connection ,也有翻译为持久连接),数据传输完成了保持TCP连接不断开(不发RST包、不四次握手),等待在同域名下继续用这个通道传输数据。

HTTP首部的Connection: Keep-alive是HTTP1.0浏览器和服务器的实验性扩展,当前的HTTP1.1 RFC2616文档没有对它做说明,因为它所需要的功能已经默认开启,无须带着它,但是实践中可以发现,浏览器的报文请求都会带上它。
如果HTTP1.1版本的HTTP请求报文不希望使用长连接,则要在HTTP请求报文首部加上Connection: close

此次时间有限,下次实验补上这部分(修改HTTP报文中的Connection,看能否抓到FIN包)。

TCP协议四次挥手

  • 浏览器换成了FireFox, 用Tamper Data插件修改了HTTP包头,把Connection:Keep-alive, 改成close

实战中的TCP/UDP协议(wireshark抓包)_第8张图片

四次握手的数据流图

这里直接上数据流图,主要是看序列号和Ack号的变化:

实战中的TCP/UDP协议(wireshark抓包)_第9张图片

第一次挥手

由服务端的443端口发往客户端的49753端口,这里和我们在课本上看到的只发FIN包不一样,实际情况中为了节省发包次数,一般会在最后一次数据包中把FIN标志位置为1,即是ACK+FIN包(ACK位也置为1)

第二挥手

这里和课本上一致,先回复一个ACK包,表示收到FIN包。

传送数据

第三个包传送长度为31的数据,注意,这里传送数据的包序号和前一个回复ACK的序号一样

第三次挥手

传送数据完毕,发送FIN、ACK均置为1的包。

第四次挥手

接受到客户端发来的FIN包,进行确认。
奇怪的是这里居然回复了两个ACK包,两个包不一样的是Ack号

  1. 在收到Seq=674的包后,返回了一个Ack=674的ACK包
  2. 然后又返回了一个Ack=675的ACK包

理论上,应该只会发第二个ACK包,为什么会发前一个ACK包呢?

在各种搜资料也找不到结果的情况下,做出自己的猜测

出现这种情况是因为,一方发完自己的数据后,紧接着发送FIN包。

而另一方之所以要分为两个包进行确认的原因,是为了保证发来的数据能够完整接收到对每一个携带数据的包,都要单独用一个ACK包进行确认即便对方在发完数据包后马上发了FIN包

因此第一个包是对数据包的确认,第二个包是对FIN包的确认。

为了搞明白这是否是偶然情况,重复上面的步骤又抓了几次包,可以发现在下图中也出现了在接收到FIN包之后,也出现返回两次ACK包的情况

说明并不是偶然。
实战中的TCP/UDP协议(wireshark抓包)_第10张图片
实战中的TCP/UDP协议(wireshark抓包)_第11张图片

后记

纸上得来终觉浅,绝知此事要躬行

虽然wireshark把很多数据含义都进行了标识,但是其中的一些内在联系还是需要我们自己去挖掘(如序列号关系、DNS解析流程),而比较重要的东西在课本上要么是一笔带过,要么缺少实例分析,对细节提及甚少。所以学习还是还要自己主动a …

你可能感兴趣的:(面试准备,TCP,面试,网络,wireshark)