XMU《计算机网络与通信》第五次实验报告

实验五 运输层与应用层协议分析

如果需要 Wireshark 捕获到的数据,可以在这里下载,这里面应该还有最后一个任务的两个代码:点击下载

目录

一、个人信息

姓名:XXX

学号:XXXXXXXXXXXXXX

二、实验目的

分析TCP、UDP和重要的应用层协议。

三、实验内容、步骤与结果

任务一 TCP 正常连接观察

见实验书5.1,观察TCP 正常连接。

要求:

  • 利用Wireshark,抓包分析并截图,分析该报文TCP首部各字段的定义、值及其含义。

  • 画出该TCP流的流图(参考课本图5-28 )

    • 在每个报文的箭头线上方标出序列号seq=xxx、确认号ack=xxx、报文段长度len=xxx、当前接收窗口大小win=xxx
    • 标出SYN和FIN报文
    • 将捕获结果保存为pcapng文件,随实验报告提交
  1. 利用 python 自带的 SimpleHTTPServer 模块,在 PC2 上启动一个简易的 web 服务器。

image-20231207175024095

  1. 在 PC1 上打开一个终端,键入 sudo wireshark 启动抓包软件;再打开一个新终端,键入 curl <PC2 的 IP>;停止抓包,在 wireshark 过滤出 TCP 类型报文。

image-20231207151338687

  1. 利用Wireshark,抓包分析并截图。观察首个 TCP 报文头,分析该报文TCP首部各字段的定义、值及其含义。

这里选取了第一个 TCP 报文,也就是三报文握手的第一个报文,建立连接的请求。

image-20231217213500354

  • 前两个字节代表在客户端的端口号 34342,接着的两个字节代表服务端的端口号 80

  • 接着的四个字节(a1 2b ed 7d)是 TCP 序号,这里为 0xa12bed7d,实际上是本报文段所发送的数据的第一个字节的序。这个序号就是记录每个封包的序号,可以让接收端重新将 TCP 的数据组合起来,是 TCP 可靠性的重要保证。

  • 接下来的四个字节(00 00 00 00)是确认号,代表着期望收到对方的下一个报文段的数据的第一个字节的序号。这里是 0x0,因为这是请求建立连接的第一个报文,此时我们还不知道对方应该会以什么样的序号开始连接,因此只能置为 0,同时在后面的 ACK 字段中置这个确认号无效。

  • 接下来的半个字节(4 位)(a)代表首部长度,这里为 0xa = 10,也就是说这个 TCP 报文的首部长度为 \(10 \times 32\text{bit} = 40\text{Byte}\)

  • 接下来理论上应该是 6 位的保留位和 6 位的标志位。但是从 Wireshark 上将这些展开来看:

    image-20231217214943201

    可以发现 Wireshark 认为保留位只有三位。这里我认为是教材上的说法和 Wireshark 上的看法不一致的原因,因此对于 Wireshark 中的 Accurate ECN, Congestion Windows Reduced, ECN-Echo 三个标志位我也不做分析。

    • 第一个标志位是 URG(Urgent)为,这里为 0,代表不存在紧急数据。

    • 第二个标志位是 ACK(Acknowledgment),这里为 0,表示这个报文的确认号是无效的。

    • 第三个标志位是 PSH 位(Push),这里为 0,说明没有启用。接收方 TCP 收到PSH=1 的报文段,就尽快地交付接收应用进程,而不再等到整个缓存都填满了后再向上交付。本报文段没有启用推送操作。

    • 第四个标志位是 RST 位(Reset),这里也为 0,代表不需要重新建立连接(当然不需要,这才是连接建立的请求报文)。

    • 第五个标志位是 SYN 位(Sync),这里为 1,结合 ACK = 0 可以代表这是一个连接请求报文段(如果 ACK = 1 则代表这是一个连接接受报文)。

    • 最后一个标志位是 FIN 位(Finish),Wireshark 中不知为何没有画出来,这里也是 0,因为这不是一个通信结束后释放连接的报文。

  • 接下来两个字节是窗口字段,这里的值为 64240,这个值代表从本报文段首部中的确认号算起,自己作为接收方目前允许对方发送的数据量,这是接收方让发送方设置其发送窗口的依据。

  • 接下来是两个字节是校验和字段,这里的值是 0xc480,可以用来检验 TCP 首部在传递过程中是否出错。

  • 接下来是两个字节是紧急指针,这里的值是 0,因为 URG 标志被设置为 0,即不存在紧急数据,紧急指针无效。(倘若有效,紧急指针指出了紧急数据的末尾在报文段中的位置)

  • 接下来是若干选项字段,长度可变。这里使用了的选项字段及其详细内容如下:

    image-20231218001044771

    • 最大报文段长度 MSS,其值为 1460,代表本主机支持的最大报文段的数据部分长度为 1460
    • 选择确认 SACK:这里设置允许使用选择确认,代表在连接建立以后,以后在TCP报文段的首部中可以增加了SACK选项, 以便报告收到的不连续的字节块的边界。
    • 时间戳:这里的时间戳值为 151221268,时间戳回送回答为 0。时间戳选项主要是用来计算往返时间 RTT 以及防止序号绕回的。
    • No-Operation(NOP):这个选项没有意义,主要是为了填充对齐的,确保 TCP 首部的长度是四字节的整数倍。
    • 窗口扩大:用于扩大前面的窗口字段,这里移位数是 \(7\),因此窗口的大小要乘以 \(128\)
  1. 画出该TCP流的流图。

为了方便绘画,我这里打开了 Wireshark 的相对 TCP 序号,因此对于同一段连接,序号都是从 0 开始的。

image-20231207151823258

首先是由 Wireshark 自动绘制而成的流程图:

image-20231207151838953

由于上图缺少一些关键信息,下面是我自己绘制的流图:
image-20231218104059736

可以发现,这里并没有用常规的四报文挥手。最后的挥手报文只有三次。我猜测是因为在默认情况下,某一端在收到关闭连接的请求后,如果没有操作也会默认自动关闭,因此第二次和第三次挥手被合并了。

  1. 将捕获结果保存为pcapng文件,随实验报告提交。

task1.pcapng

任务二 异常传输观察分析

1. 尝试连接未存活的主机或对未监听端口。

(a) 用 curl 访问一个不存在的主机 IP,抓包观察共发送了几次 SYN 报文。根据每次时间间隔变化,估算 RTO(重传超时)。

值得一提的是,我一开始指定的「不存在的主机 IP」是 192.168.33.100,这个 IP 虽然不存在,但是这从网络前缀上来看,这是本地局域网内的 IP。这会导致根本不会发送 TCP 报文,而是在发送了几个 ARP 询问报文无回应后,就会报告异常。

后来改成了一个局域网外的不存在的 IP 123.23.3.1,才正常测试出 TCP 报文。如下:

image-20231207155418288

可以发现一共发送了 5 次 SYN 报文。

重传时间一开始是 \(1s\),后来每次都增长为上一次的两倍,依次为 \(2s, 4s, 8s\)

本次捕获的数据见 task2-1-a.pcapng

(b) 查看 Linux 主机的系统的 TCP 参数 SYN 重传设定:

image-20231207152542868

(c) 更改 SYN 重传次数为 3:

image-20231207155903112

必须要切换到 root 用户下才能修改,在非用户模式下即使加上 sudo 也不行。

(d) 再次 curl 访问,观察抓包内容。

image-20231207160221864

可以发现,发送 SYN 报文的次数比之前少了一次,变成了 \(4\) 次。

本次捕获的数据见 task2-1-d.pcapng

(e) 关闭服务器端的 SimpleHTTPServer(ctrl+C 中断,或关闭所在终端),客户端 curl 访问服务器 80 端口,观察应答报文。

image-20231207160353807

可以发现,在发送了连接请求报文后,收到了一个标志为 RST, ACK 的应答报文,而不是原本应该得到的 SYN, ACK 报文。这个报文的 winlen 都是 \(0\)。在收到这个报文后,客户端就知道这个端口没有开放,因此上报错误。

本次捕获的数据见 task2-1-e.pcapng

(f) 运行 nmap -sS <PC2 的 IP> 扫描服务器,并抓包。

image-20231207160806145

image-20231207160713229

虽然 nmap 发送 TCP 扫描报文的端口并不连续,但是我们可以猜测出它对每一个端口都发送了一个 TCP 链接请求报文进行测试。

对于每一个端口,客户端发送了一个 SYN 报文请求建立连接。显然,对于开放的端口,其应该报文应该是 SYN, ACK 接受连接,否则应该是 RST, ACK 报文建立表示无法建立连接。

本次捕获的数据见 task2-1-f.pcapng

(g) 在报告中总结以上观察结果,解释 SYN 扫描原理。

当在 Linux 系统下使用 nmap 命令进行 SYN 扫描时,实际上是在利用 TCP 协议的一种特性来探测目标主机的开放端口。这种扫描方式基于 SYN(同步)标志位的交互过程。

SYN 扫描工作原理如下:nmap 向目标主机发送 TCP 的 SYN 数据包,不完全建立连接,而是在三次握手的第一步发送 SYN 包。如果目标主机响应一个 SYN/ACK(同步/确认)数据包,说明该端口是开放的,然后 nmap 发送一个 RST(复位)数据包来终止这个尝试建立连接的过程。如果目标主机响应一个 RST(复位)数据包,说明该端口是关闭的。这种方式避免了完全建立连接,从而更加隐秘地进行端口扫描。

这种 SYN 扫描的策略在探测端口时非常高效,因为它不需要完全建立连接,只需进行部分握手,快速获取目标主机端口的开放状态信息,也不会在目标主机上留下太过明显的扫描记录。

2. 客户端发送了第一个 SYN 连接请求,服务器无响应的情景。

(a)

服务器开启 telnet 或 ssh 服务,客户端先尝试连接服务器,连接成功后,在双方键入 ss -tan 查看所有 TCP 连接状态。我们看到的 TCP 连接建立过程同 1 中的 HTTP 访问类似。在客户端,利用 iptables 拦截服务器回应的 SYN ACK 包,命令如下:
sudo iptables -I INPUT -s 192.168.100.144 -p tcp -m tcp -–tcp-flags ALL SYN,ACK -j DROP

连接建立

image-20231207161358341

可以发现,在连接建立阶段,依然是常规的三报文握手。第一次是一个 SYN 连接建立请求,然后服务端回应了一个 SYN, ACK 接受连接报文,随后客户端又发送了 ACK 报文第三次握手。

效果

image-20231207153725036

客户端

image-20231207161514477

服务端

image-20231207161527135

拦截:

sudo iptables -I INPUT -s 192.168.100.144 -p tcp -m tcp --tcp-flags ALL SYN,ACK -j DROP 

image-20231207164431227

本次捕获的数据见 task2-2-a.pcapng

(b) 再次尝试连接并启动 wireshark 抓包,并在双方多次用 ss -tan 观察 TCP 状态。

image-20231207164531497

image-20231207165248103

客户端

image-20231207165311409

服务端

image-20231207165327998

本次捕获的数据见 task2-2-b.pcapng

(c) 观察 TCP 的状态变化,分析 wireshark 捕获的 TCP 异常报文。

可以发现,尽管服务端发送了 SYN, ACK 连接接受报文,但是客户端一直认为自己没有收到,因此一直在重复发送 TCP 请求连接报文,一直到重传次数的限制为止。

观察 ss -tan 的 TCP 状态可以发现,客户端一直处于 SYN-SENT 状态,因为它发出连接建立报文后一直没有收到连接接受报文。而服务端一直处于 SYN-RECV 状态,因为服务端发送连接接受报文后一直没有收到第三次握手的确认。

image-20231207165621993

(d) 服务端的 SYN-RECV 状态何时释放?

服务端的 SYN-RECV 状态在一段时间内没有收到第三次握手报文的确认之后就会自动释放,大概两分钟左右。

(e) SYN ACK 重传了几次,时间间隔有何变化?

SYN,ACK 报文重传了 \(7\) 个包,但是只有 \(6\) 个时间点。

但是时间间隔比较随机,第一次重传距离原始数据包 1s,同时第二个重传包在同一时间也被发送。

在 2s 后,第三个重传包被发送。

再过 2s,第四个重传包被发送。

又过了 2s,第五个重传包被发送。

4s 后,第六个重传包发送。

8 s 后,第七个重传包发送。

(f) 参考 1 中的操作,在服务端修改 SYN ACK 重传次数 (tcp_synack_retries),再次观察,此任务结束后清空防火墙规则 (iptables -F)。

image-20231207170021805

image-20231207170009184

image-20231207170046364

仍然重传了 \(7\) 个包,但是时间点由原来的 \(6\) 个变为了 \(4\) 个。

在发送原始的 SYN,ACK 包后 1s,第一、第二个重传包被发送。

此后 2s,第三、第四个重传包被发送。

此后 4s,第五、第六个重传包被发送。

此后 8s,第七个重传包被发送。

此次捕获的数据位于 task2-2-f.pcapng

任务3:TCP 拥塞控制

实验步骤:

见实验书5.4.1,观察大文件TCP传输的过程。

见实验书5.4.2,分析此传输过程中的慢启动、拥塞避免、快速恢复等阶段。

观察大文件TCP传输的过程

任一端限制网卡传入/传出带宽为 10Mbps 以下:使用虚拟机作为实验机,可在 VMWare Player 中的虚拟机设置 网络适配器 高级中设置;物理机可使用 wondershaper 命令进行限速。再启动应用 (可以是 http wget,也可以 ftp 下载/上传) 传输大文件观察。

如图,由于限制在 10Bbps 以下,实际上的平均传输速度只有 1.17MB/s,完整的传输一共使用了 86s。

image-20231207163223498

分析此传输过程中的慢启动、拥塞避免、快速恢复等阶段

Wireshark 捕捉全部传输过程数据,找出该网络活动的拥塞点,并结合 AnalyzeExpert Information、StatisticIO Graphs、StatisticTCP Stream Graphs(如图1.3–5),分析此传输过程中的慢启动、拥塞避免、快速恢复等阶段。

因为 Wireshark 抓取到的 TCP 包很多,这里就不截图了。

下图是 Wireshark 的 AnalyzeExpert Information:

image-20231207163628651

下图是 Wireshark 的 StatisticIO Graphs:

image-20231207163652578

下图是 StatisticTCP Stream Graphs 中的窗口尺寸变化:

image-20231207163709981

由于「窗口尺寸」这一个字段并不能真实地反应拥塞窗口的大小,这里采用吞吐量来猜测拥塞窗口的表现。

从吞吐量关于时间的示意图中,可以发现「慢启动」阶段,吞吐量呈指数级增长。

image-20231218172539621

对于「拥塞避免」的阶段,我们可以从后面的的吞吐量增长缓慢的阶段看出。此时因为拥塞窗口不再指数级增长,每次只会增长 1,因此吞吐量增长幅度在这一阶段很小。

image-20231218211444810

对于「快恢复」和「快重传」,可以结合上下两图观察。可以发现,在下图的 I/O 图表中,每隔一段时间就会出现一次每秒发包数的下降后重新爬升,从上图的吞吐量也可以观察到,每隔一段时间就会出现吞吐量下降的后重新爬升(如上图 12.25s 处和 12.2s 处)。

image-20231218211405383

从之前的 Wireshark 专家信息中可以发现,整个传输过程中一共出现了 22 次快重传。

本次捕获的数据见 task3.pcapng

(警告:这一段的东西其实我都是瞎写的……这几个图里面,我根本看不出来任何东西,什么「拥塞避免」「快回复」「快重传」啥的,我都是随便附会的,仅供参考,大概率全是错的。)

任务 4 UDP 协议观察

实验步骤:

  • 利用 Python3 编写简单的 UDP 协议通信程序(也可直接运行示例程序)。
  • 在服务器和客户机先开启wireshark抓包,然后在服务器运行 udp_server.py,在客户机运行 udp_client.py,实现简单的 UDP通信(建议关闭防火墙测试)。
  • 观察 Wireshrak 抓包,输入“udp”进行过滤,观察相应的结果。

要求:

  • 利用Wireshark,抓包分析并截图,分析该报文UDP首部各字段的定义、值及其含义。
  • 根据结果对比TCP协议与UDP协议,分析它们的优缺点以及应用场景。
  • 将捕获结果保存为pcapng文件,随实验报告提交。

程序内容

没有看见 ppt 后面附的两个程序……所以自己写了两个。

客户端:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import socket
import time

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

addr = ("192.168.33.129", 9999)

while True:
    data = "hankeke303"
    s.sendto(data.encode('utf-8'), addr)
    response, addr = s.recvfrom(1024)
    print(response.decode('utf-8'))
    time.sleep(1)

s.close()

服务端:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import socket
import time

import socket

s = socket.socket(type=socket.SOCK_DGRAM)

s.bind(('0.0.0.0', 9999))

while True:
    msg, addr = s.recvfrom(1024)
    st = msg.decode('utf-8')
    print(st)

    st = 'Hello, ' + st

    s.sendto(st.encode('utf-8'), addr)

s.close()

捕获截图

image-20231218213312925

image-20231218213323467

分析 UDP 首部字段

这里以第二个报文(也就是服务端给客户端发回去的报文)为例,分析其首部字段的内容:

image-20231218213408053

  • 开头的两个字节代表源端口,也就是本地的端口号,这里是 9999,与我设置的一致。
  • 紧接着的两个字节代表目的端口,这里是 56482,与第一个报文中的源端口一致。
  • 接着的两个字节代表 UDP 用户数据报的长度,包括 UDP 首部和数据部分,这里的值是 26。从图中可知,数据部分长 16,而 UDP 首部固定为 6 字节,因此一致。
  • 最后的两个字节代表 UDP 校验和,用来检验 UDP 首部信息是否正确。

根据结果对比TCP协议与UDP协议,分析它们的优缺点以及应用场景

最显著的一个差异就是 UDP 用户数据报的首部长度比 TCP 简短了很多——除了必要的校验和以及长度外,UDP 用户数据报只需要指出源端口、目的端口即可,只需要 \(8\) 字节。而 TCP 首部至少 \(20\) 字节,比 UDP 首部臃肿了很多。因而在需要追求轻便、降低网络负载的情况下,往往使用 UDP 数据报,比如 DNS 或者 RIP 协议。

我这里并没有出现 UDP 数据报丢失或者失序的情况,因此无法通过对比体现 TCP 的可靠传输特性。但是我们知道可靠传输在网络中的重要性,因此重要的传输内容比如 HTTP、BGP 协议都使用 TCP 协议。

将捕获结果保存为pcapng文件,随实验报告提交

捕获数据见 task4.pcapng

任务 5 HTTP协议分析

实验步骤:

  • 利用 python 自带的 http.server 模块,搭建HTTP1.0,HTTP1.1和HTTP2的Web服务器,并用wireshark抓包观察协议的不同。

实验报告要求:

  • Wireshark抓包截图,并在截图中体现不同点

HTTP/1.0

搭建服务

这里直接使用实验一中指定的方法,使用 sudo python2 -m SimpleHTTPServer 80 来建立一个简单的 HTTP/1.0 服务器,然后在客户端使用 curl 获取 http 报文。

image-20231207175024095

报文分析

image-20231218214911411

需要注意的是,由于 curl 默认使用 HTTP/1.1,因此发出的 HTTP 请求都是 HTTP/1.1 的。但是这没有问题,因为应答报文会根据服务端的协议回答,因此我们收到的服务端的响应一定是我们指定的 HTTP 版本。比如此例中,响应报文的 HTTP 协议版本就是 HTTP/1.0

从报文中的 HTTP 字段可以发现,其 HTTP Header 和 HTTP Data 分别如下:

HTTP/1.0 200 OK
Server: SimpleHTTP/0.6 Python/2.7.18
Date: Thu, 07 Dec 2023 09:49:14 GMT
Content-type: text/html
Content-Length: 13
Last-Modified: Thu, 07 Dec 2023 09:46:22 GMT

TCP lab test

第一行指定了 HTTP 版本为 1.0,以及响应码 200。数据部分是明文传送。

本次捕获的数据全部位于 http1.0.pcapng

HTTP/1.1

搭建服务

通过查询 Python 文档发现,在 Python 3.11 中引入了 --protocol 参数,可以用来指定 HTTP 版本。

image-20231218215258402

所以,我下载了 Python 3.11,然后使用了下面的命令搭建 HTTP/1.1 的服务器:

sudo python3.11 -m http.server --protocol HTTP/1.1 80

image-20231207175814271

报文分析

image-20231207175805847

可以发现,在本次得到的 HTTP/1.1 的响应中,相应内容为:

HTTP/1.1 200 OK
Server: SimpleHTTP/0.6 Python/3.11.0rc1
Date: Thu, 07 Dec 2023 09:57:50 GMT
Content-type: text/html
Content-Length: 13
Last-Modified: Thu, 07 Dec 2023 09:46:22 GMT

TCP lab test

HTTP/1.0 时的相应对比,可以发现除了第一行的协议版本(HTTP/1.0 -> HTTP/1.1),以及因为使用的 Python 版本发生变化导致 Server 字段发生变化以外,内容几乎完全一样。

捕获数据的数据存储在 http1.1.pcapng

HTTP/2.0

服务搭建

HTTP/1.1 类似,使用以下命令搭建服务端:

sudo python3.11 -m http.server --protocol HTTP/2.0 80

image-20231207175903031

报文分析

image-20231207175915175

在本次得到的 HTTP/2.0 响应中,报文内容为:

HTTP/2.0 200 OK
Server: SimpleHTTP/0.6 Python/3.11.0rc1
Date: Thu, 07 Dec 2023 09:58:38 GMT
Content-type: text/html
Content-Length: 13
Last-Modified: Thu, 07 Dec 2023 09:46:22 GMT

TCP lab test

可以继续发现,除了第一行的协议版本(HTTP/1.1 -> HTTP/2.0)以外,基本上没有任何变化。

捕获数据的数据存储在 http2.0.pcapng

四、实验小结

在这次的网络协议实验中,我通过观察 TCP 和 UDP 这两种协议的行为,深入了解了它们在数据传输过程中的不同特点。

TCP 作为一种面向连接的协议,展现出了出色的可靠性。通过任务一的观察,我清晰地看到了它建立连接、传输数据、断开连接的过程。这种稳健的传输方式在保障数据完整性方面表现出色,但也因此会带来一些额外的开销。

任务二则让我了解了 TCP 异常传输时的表现。观察到当连接请求发生异常或是目标端口未开放时,TCP 会调整重传时间间隔以及处理不同的响应情况。这种对异常情况的处理方式让我更加理解了 TCP 协议的可靠性和稳定性。

在任务三中,我们探讨了 TCP 拥塞控制。我了解到 TCP 通过动态调整传输速率和拥塞窗口大小来适应网络状态,这种自适应性让它能够更好地处理网络拥塞的情况,保证了网络的稳定性和公平性。

UDP 则展现出了不同的特性。通过任务四的观察,我发现 UDP 更加简洁高效,适用于一些对实时性要求较高、允许数据包丢失的场景。它的轻量级特点使得它在音视频传输等领域有着广泛的应用。

最后,在任务五中,我对比了 HTTP/1.0、HTTP/1.1 和 HTTP/2.0 的协议差异。尽管它们在通信内容上基本一致,但协议版本的变化代表了网络通信规范的不同和性能的提升。这种协议版本的更新也是网络发展的体现。

这次实验不仅让我理论上更加了解了这些协议的特性,更重要的是让我意识到在实际应用中选择合适的协议非常重要。每种协议都有其适用的场景,了解其特点能够更好地应对实际的网络通信需求。这种对网络协议的深入了解,对于未来的学习和实践都具有重要的指导意义。

posted @ 2024-04-28 08:56  hankeke303  阅读(715)  评论(0编辑  收藏  举报