程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

Python實現基於UDP的可靠傳輸

編輯:Python

基於 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

其中,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 文件:


  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved