基于 UDP 的可靠传输
实验环境:
操作系统:Windows 10语言:Python 3.7编译器:Sublime、VS CodeLFTP 实现要求客户端可以通过 lsend myserver mylargefile 命令将本地文件上传到服务器客户端可以通过 lget myserver mylargefile 命令从服务器下载文件到本地基于 UDP 的传输方式实现可靠传输实现传输时的流控制和阻塞控制服务器允许多用户同时上传和下载文件发送模型
下载模型
发送方代码运行逻辑
当客户端或者服务器确认自己是发送方时,启动发送分组线程作为主线程,读取文件线 程和接受 ACK 线程为子线程,3 个线程对缓存、窗口大小等全局变量进行操作。在更改全局变量完成发送逻辑时需要利用线程锁的机制,避免读写冲突。
读取文件线程检测到已经读取完全部文件分组时即可结束线程,即使有部分分组仍未确 认,但它们已经读取到发送缓存中,读取文件线程的结束并不会造成影响。
接收 ACK 线程接收到所有分组确认后便可以结束自身线程,此时所有分组已经发送和确认,发送分组线程结束,完成整个文件发送的过程。
读取文件线程
接收 ACK 线程接收方代码运行逻辑
为了实现流控制,文件接收方也需要设置一个缓存窗口,将接受分组和写文件的操作分 成 2 个独立线程。
每当接收分组线程接收到 1 个正确的分组时,回馈的 ACK 的同时也会把接收缓存剩余的窗口大小(rwnd)返回,发送方根据这个返回的 rwnd 进行流控制的相关操作。当写文件线程把最后的分组写入文件,接收缓存被清空,宣布整个接收文件的过程结束,2 个线程不再接收分组和写文件。
接受分组线程写文件线程
流控制
想要实现类似 TCP 的流控制,需要在发送方和接收方都设置一个缓存进行分组的存放。接收方接收到分组时把分组放入缓存,回馈接收的分组号和 rwnd 给发送方,发送方根据 rwnd 更新自身的变量。满足以下条件时才能继续发送分组,以此达到流控制的目的:
LastPacketSent - LastPacketAcked <= rwnd
其中,LastPacketSent 为最后发送的分组号,LastPacketAcked 为最后确认的分组号 。当引入阻塞控制时,条件变为:
LastPacketSent - LastPacketAcked <= min {rwnd, cwnd}
阻塞控制
LFTP 的阻塞控制仿照 TCP 实现,发送方一开始处于慢启动状态,根据 cwnd、ssthresh
和冗余 ACK 计数三个变量进行状态变换,最终实现发送窗口的大小控制。
多用户
服务器用 23333 端口接收客户端请求,每当接收到一个请求,服务器则会分配一个端口给一个新的子线程,这个子线程负责与客户端完成文件传输或者下载的交互,主线程继续监 听 23333 端口,等待另一个新请求的到来。
服务器客户端
传输测试
作业要求文件传输不仅仅在校园网内进行,所以我们租用了一个服务器,在上面运行服 务端代码,个人电脑运行客户端代码。为了方便用户使用 LFTP,我们还实现了一个简单的 UI 窗口,客户端启动 window.py 即可输入指令,UI 窗口有基本的指令错误提示。
UI 窗口 lsend 指令测试租用服务器运行服务端代码,等待客户端请求。 客户端发送 lsend 指令。客户端:
文件发送结束,可以在服务器看到接收到的 lena.jpg 图片,并且可以正常打开。
lget 指令测试
服务器存在目标文件 C:\cat.gif
客户端通过 lget 指令请求下载 C:\cat.gif,此时下载文件目录中并没有 cat.gif 文件发送中。服务器:客户端:
文件接收完成,可以在下载目录看到接收的文件,并且能正常打开。
流控制测试
流控制机制的作用是匹配发送方和接收方的缓存读写速度,接收方每次返回确认分组都携带接收缓存窗口长度,发送方根据这个接收缓存窗口长度调整自己的发送缓存窗口长度。 在截图中可以明显地看到发送缓存窗口长度不断地在调整,流控制机制在正确运作。
阻塞控制测试为了测试阻塞控制,在接收方添加随机数代码,当随机的数字小于 0.6 时不接收分组(即使它是期望收到的分组),即每个期望分组只有 40% 的概率被接收方接收,以此造成阻塞假象,达到测试目的。
由于连续 3 次冗余 ACK,触发了阻塞控制,状态由慢启动变为快速恢复,ssthresh 变为 cwnd/2,cwnd 变为 ssthresh+3。
接收方尝试接收分组 40,触发了随机丢包,但是因为这是最后一个分组,所以没有后续报文造成的冗余回送 ACK,于是触发了超时,发送方没接收到确认 ACK 认定超时,不管此时阻塞控制处于哪个状态,都转变为慢启动状态,cwnd 重置为 1。
经过冗余 ACK 和超时事件的测试,阻塞控制机制都如预想中的那样运作,测试成功!
多用户测试
为了验证服务器支持多用户同时下载或者传输文件,我们在一台主机上运行两个客户 端,同时下载不同的文件,最后在下载目录中查看文件是否成功下载并且能够正常查看。
下载前
运行下载文件的指令,可以看到 2 个命令行窗口同时在下载文件并打印信息。此时下载目录已经创建了 2 个正在下载的文件,但因为下载未完成,大小显示为 0Kb。
下载完成,2 个命令行窗口显示下载完成,并且可以在下载目录看到 2 个正常大小下载文件,测试成功!
大文件传输测试由于网速的限制,即使是同一台主机传输文件,预估计的最快速度也只有 50Kb/s,传输 1G 大文件的耗时大概需要 8 小时。因此我们选择了一个大小约为 200M 的 mp4 文件作为测试文件。
为了提高传输速度,大文件传输测试没有选择租用的外部服务器,而是选择了校园网内的 2 台主机作为服务器跟客户端。通过 lsend 指令向服务器请求传递 mp4 文件,可以看到文件的大小大约为 2 亿 Bytes,每个分组的大小为 800KB,计算出总共需要发送 257500 个分组。文件传输结束,服务器成功接收了 257500 个分组。查看本地文件,可以看到 mp4 文件从创建到修改总共历时 1 小时 23 分钟,也就是整个文件传输过程的耗时,平均传输速度只有 41Kb/s。打开 mp4 文件视频能正常播放,大文件传输测试成功!
mp4 创建、修改日期:
平均传输速度:
播放 mp4 文件: