• Python实现基于UDP的可靠传输


    基于 UDP 的可靠传输

    实验环境:

    • 操作系统:Windows 10
    • 语言:Python 3.7
    • 编译器:Sublime、VS Code
    • LFTP 实现要求
    • 客户端可以通过 lsend myserver mylargefile 命令将本地文件上传到服务器
    • 客户端可以通过 lget myserver mylargefile 命令从服务器下载文件到本地
    • 基于 UDP 的传输方式实现可靠传输
    • 实现传输时的流控制和阻塞控制
    • 服务器允许多用户同时上传和下载文件

    发送模型

    在这里插入图片描述
    在这里插入图片描述

    下载模型

    发送方代码运行逻辑
    在这里插入图片描述

    当客户端或者服务器确认自己是发送方时,启动发送分组线程作为主线程,读取文件线 程和接受 ACK 线程为子线程,3 个线程对缓存、窗口大小等全局变量进行操作。在更改全局变量完成发送逻辑时需要利用线程锁的机制,避免读写冲突。

    读取文件线程检测到已经读取完全部文件分组时即可结束线程,即使有部分分组仍未确 认,但它们已经读取到发送缓存中,读取文件线程的结束并不会造成影响。

    接收 ACK 线程接收到所有分组确认后便可以结束自身线程,此时所有分组已经发送和确认,发送分组线程结束,完成整个文件发送的过程。

    在这里插入图片描述
    在这里插入图片描述

    读取文件线程

    在这里插入图片描述

    1. 接收 ACK 线程

    在这里插入图片描述

    接收方代码运行逻辑

    在这里插入图片描述

    为了实现流控制,文件接收方也需要设置一个缓存窗口,将接受分组和写文件的操作分 成 2 个独立线程。

    每当接收分组线程接收到 1 个正确的分组时,回馈的 ACK 的同时也会把接收缓存剩余的窗口大小(rwnd)返回,发送方根据这个返回的 rwnd 进行流控制的相关操作。当写文件线程把最后的分组写入文件,接收缓存被清空,宣布整个接收文件的过程结束,2 个线程不再接收分组和写文件。

    • 接受分组线程

    在这里插入图片描述
    在这里插入图片描述

    写文件线程

    在这里插入图片描述

    流控制

    想要实现类似 TCP 的流控制,需要在发送方和接收方都设置一个缓存进行分组的存放。接收方接收到分组时把分组放入缓存,回馈接收的分组号和 rwnd 给发送方,发送方根据 rwnd 更新自身的变量。满足以下条件时才能继续发送分组,以此达到流控制的目的:

    LastPacketSent - LastPacketAcked <= rwnd
    
    • 1

    其中,LastPacketSent 为最后发送的分组号,LastPacketAcked 为最后确认的分组号 。当引入阻塞控制时,条件变为:

    在这里插入图片描述

    LastPacketSent - LastPacketAcked <= min {rwnd, cwnd}
    
    • 1

    阻塞控制

    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 文件:

    在这里插入图片描述

  • 相关阅读:
    Java实现图片转文字!(OCR实现)
    shell基础(2):编程基础之变量:全局/局部/shell变量(作用域、定义、操作)、位置参数、数组
    Android解析自定义属性实现视差动画效果
    【MySQL】一文学会所有MySQL基础知识以及基本面试题
    分享Web端即时通讯的发展与WebSocket的实践
    Lambda函数
    css——半圆实心
    本博主二哈喇子!特此声明
    计算机体系结构的概念和学习目的
    流式数据湖平台Hudi核心概念三:索引
  • 原文地址:https://blog.csdn.net/sheziqiong/article/details/126050561