<基於Qt與POSIX線程>多線程下載器的簡易搭建

原創博客,轉載請聯繫博主!linux

本項目已託管到本人Git遠程庫:https://github.com/yue9944882/Snowgit

 

 

 


 

 

項目目標     github

Major Functionality編程

 

開發環境:  CentOS7-Qt4服務器

 

實現一個基於LINUX的多線程下載器,功能上仿造迅雷,主要有以下幾個功能:網絡

 

(僅限HTTP協議)多線程下載遠程資源數據結構

(暫停/繼續功能)斷點續傳多線程

 


 

 

項目主要技術   架構

Major Technique異步

 

 

POSIX線程及其協做

TCP協議套接字編程

Qt界面實現

Qt 信號槽機制SIGNAL/SLOT

// Linux信號處理

 


 

 

項目構思

Major Architecture

 

 

貫穿整個使用過程的Qt的主界面對全局的若干個隊列進行操做,從而實現系統協做,其過程當中使用若個同步鎖調配線程之間的協做與競爭。

 

爲何這麼設計?

 

這麼設計最大的緣由是爲了實現POSIX線程與Qt封裝類之間的互動,POSIX線程是基於LINUX下的C語言實現的,其調用建立的入口必須是C-style聲明的函數,若是直接將這些函數聲明爲Qt控件繼承類內靜態函數會破壞其封裝性(我的實踐證實,這麼作也是不可行的)。通過幾回完全失敗以後,這個方法也是目前很少可行解決辦法之一。 

 

爲何使用POSIX線程?

 

Qt是可移植的項目環境,若是使用linux下獨有的FORK/VFORK系列函數,會侷限程序運行環境,POSIX標準下的線程更通用,更普遍。至於爲何沒有使用QThread,我只能說不想用- -|||

 

 

下圖所示是總體模塊之間的聯繫:

 

 

網絡及動態顯示控件部分架構 以下圖所示:

 

 

 

邏輯任務線程不直接參與下載,附屬線程封裝進邏輯任務中,對其餘任務不可見,動態控件隊列隨着用戶的操做長度和順序會不斷髮生改變,而邏輯下載任務隊列只會不斷生長,而且經過索引與動態控件一對一對應。

 

Qt主界面應用類架構 分別在與之對應的 *.ui 文件中定義,這裏暫不贅述,各個類之間的溝通是經過QtSIGNAL/SLOT 信號槽機制完成的。

 

POSIX鎖類架構 以下圖所示:

 

 

--- Declaration ------  global.h     extern聲明外部全局變量

|            |

|             ---  global_t.h   C風格結構體定義,主要用於POSIX線程傳參

|            |

|             ---  global_f.h   extern聲明外部全局函數

|            |

|             ---  missionbar.h  missioncheck.h   C++風格聲明動態控件

|            |

|             ---  mainwindow.h  newdialog.h   C++風格聲明靜態控件

|

|

|

--- Defination  -----  global.cpp   :全局變量定義

|            |

|             ---  global_f.cpp  POSIX線程及日誌系統- C風格函數定義

|            |

|             ---  main.cpp    :程序入口,程序環境初始化..

|            |

|             ---  *.cpp       Qt庫繼承類定義

|

|

--- Makefile   ------  SnowLINUX.pro   QMake 腳本

             |

              ---  Makefile         :自動化編譯腳本

 

 


 

 

代碼實現

Implementation

 

網絡下載部分

 

1. TCP套接字:

 

  因爲多線程下載器須要保證文件的完整性,咱們選擇TCP協議下的套接字進行下載,每次下載首先發送一個HTTP-HEAD請求獲得文件的長度和完整名稱而且聲明不使用gzip格式壓縮,不然沒法多線程寫入!獲得了文件完整的長度以後,根據線程數量爲文件劃分段落,而後由若干個POSIX線程使用隨機上長的端口號和服務器80號端口進行TCP鏈接,再使用Linux內核提供的接口進行下載,每一個任務維護一個更新寫入進程鎖,以更新當前進度/速度的實時信息,並競爭全局鎖刷新全局統計變量。

 

 

2. 無鎖數據結構:

 

  使用Linux的原子文件讀/寫操做而不是標準文件操做,以保證多線程寫入的原子性和完整性,pwrite/pread函數是咱們的最終選擇,因爲讀寫原子性,文件描述符不須要鎖類保護同步性。

 

 

3. 日誌系統

 

  日誌系統本來的設計是經過Linux-signal庫進行定時的任務日誌更新,可是這樣給CPU帶來了太多額外的任務消耗,下載的速度也會形成不一樣程度減小,更重要的是這樣實現會破壞Qt繼承類的封裝性,由於signal_handler風格的函數必須是全局函數。咱們採用的是」單次」日誌,在用戶有須要的時候觸發日誌記錄系統,相關函數見 global_f.cpp中的:

 

  Init_log(),write_log(),read_log()系列函數,日誌格式暫不贅述,爲純ASCii編碼文件。

 

 

 

Qt類與全局鎖部分

 

1. 全局競爭鎖:

 

時間( timeMutex )是爲了主界面定時調用函數在獲取任務隊列中不斷異步更新進度的任務的執行時間,時間鎖是爲了保證主界面和動態控件之間數據的同步,其中動態控件的數據由動態控件封裝的鎖保證其內部POSIX線程刷新數據的同步性。

 

表鎖(tableMutex) 是爲了保證咱們在進行刪除/重啓任務而致使全局表修改的同時,程序不會由於定時刷新曲線而錯誤訪問任務表致使的程序崩潰。

狀態鎖( finishMutex ) 是爲了防止動態控件和任務表之間相互經過記錄對方索引而相互訪問時表內容修改致使的程序崩潰。

 

2. 全局協做鎖:

 

在啓動新任務的時候彈出的小QDialog窗口是個獨立的窗口,其內部空間的SIGNAL分別綁定到了主界面的槽(SLOT)和動態控件的槽中,然而兩邊的槽默認是同時進行調用的,而咱們必需要求其順序,否則程序會崩潰。例如,在咱們選擇文件路徑後開始下載任務,咱們須要先調用主界面的槽來更新表作預備工做,而後才能建立動態控件及下載任務.

 

 

 


 

 

 

程序操做

Demostration

 

操做方式很是簡明:

 

 

 

相關文章
相關標籤/搜索