基于TCP的大文件传输c语言项目
文章目錄
- 前言:功能實現
- tcp文件傳輸的基本過程:
- 1.用戶登錄
- 1.1創建數據庫
- 2.文件普通下載和上傳的實現:
- 2.1 普通下載
- 2.2 普通上傳
- 2.3 文件秒上傳的實現
- 2.斷點下載和斷點上傳的實現:
- 2.1 斷點下載
- 2.2 斷點上傳
- 2.3大文件傳輸
- 3.sendfile 零拷貝
- 4 演示圖:
- 4.1 啟動服務端:
- 4.2 啟動客戶端并注冊新用戶
- 4.3 上傳小文件:
- 4.4 上傳大文件并斷點續傳:
- 4.5 秒傳的實現:
- 4.6 不同用戶上傳同一文件,其中某一個用戶中途退出
- 5 待完善的地方
- 5.1 一點小bug
- 5.2 自己運行源碼時有問題的地方
前言:功能實現
1.用戶登錄
2.文件上傳,下載(包含大文件)
3.斷點續傳,秒傳
4.零拷貝
tcp文件傳輸的基本過程:
在傳輸文件數據之前,發送端會把文件名稱和文件長度等信息的數據包發送至接收端,接收端收到文件名稱和文件長度信息后會創建好空白文件。接著開始傳輸文件數據。
服務端有一個創建新線程的函數,一旦服務端監聽有連接請求,就創建新線程去連接該請求,即每個客戶端都是一個線程在服務器中運行,互不干擾,基本滿足局域網內的高并發訪問。
1.用戶登錄
1.1創建數據庫
采用mysql,新建三個數據庫
其中,**user **表如下:
創建代碼如下:
因為我們考慮到斷點續傳和秒傳,需要用md5值記錄該文件在服務端是否存在。
輸入任意長度的信息,經過處理,輸出為128位的信息(數字指紋);
不同的輸入得到的不同的結果(唯一性),采用是的散列函數,hash算法。
程序中計算md5值的方法,是利用LINUX支持OPENSSL并提供如下MD5函數。
MD5_Init初始化MD5_CTX結構。
MD5_Update計算摘要。
MD5_Final輸出摘要值。
其中,md5table表如下:
創建代碼如下:
其中,serverStartInfo表如下:
創建代碼如下:
如果出現啟動數據庫無法訪問,可能是要開啟權限,具體參照下文。
啟動Mysql數據庫報錯誤:-bash: ./start.sh: Permission denied
利用Shell編程實現多個接口用于用戶注冊,登錄,插入文件信息到數據庫,刪除某個文件記錄,md5值匹配后秒傳,判斷文件所屬是否是當前用戶等等。同時,系統也應該有一個root用戶,擁有最高權限,可用來刪除文件,關閉服務器等操作。設置root用戶的方法也很簡單,只需根據用戶名是否為root來設置一個標志位即可,用于標記該用于是否擁有管理員權限。
2.文件普通下載和上傳的實現:
(注:下載是指客戶端從服務器下載文件,上傳是指客戶端上傳文件到服務器)
考慮到下載或者上傳過程中有時會出現中斷,之后還要繼續下載,我們需要設置一個文件完整標志位來表明操作完成的文件是否是完整的。
2.1 普通下載
普通下載在這里指的是一次性完成文件下載工作,無中斷。分析后可知,對于普通下載,我們需要在服務器端判斷待下載的文件是否完整—即進入數據庫中查詢文件完整標志位,完整的話就開始下載。
2.2 普通上傳
??普通上傳在這里指的是一次性完成文件上傳工作,無中斷。由于考慮到多個用戶同時連接服務器的問題,服務器端的數據庫是要存儲文件名,文件所屬(用戶),文件完整標志位,以及md5值。這里md5值相當于每個文件的數字指紋,用來標識文件是否已經存在于服務器端,用于判斷還需不需要繼續上傳該文件(即秒傳功能的實現),后面會詳細提到。
注:Message-Digest Algorithm 5(信息-摘要算法5)
??接下來,普通上傳的實現就很簡單,當上傳文件成功后,文件完整標志位置1,使得用戶可以下載該文件。
2.3 文件秒上傳的實現
前面提過每個文件都會有一個md5值。在客戶端上傳文件的時候,會先計算該文件的md5值,并把文件信息和md5值發送給服務器,服務器會從數據庫中匹配該文件名和md5值以及文件是否完整。若服務器中存在滿足上述三個條件的文件,說明服務器中已經存在該文件,保留這一個即可。若不滿足,再上傳文件后根據文件完整標志位判斷使用普通上傳亦或是斷點上傳。
2.斷點下載和斷點上傳的實現:
??所謂斷點下載,類似于我們在app商城中下載應用,下載期間因某事中斷該下載后,繼續下載可以接著原來的進度進行下載,可提升傳輸效率。斷點上傳同理
2.1 斷點下載
??斷點下載,類似于接力賽,2道的選手要接力1道選手完成后序的任務。在這里,我們需要在客戶端計算出文件已經有多大了,然后把文件大小發給服務端,服務端再將文件指針偏移到收到的文件大小處后,開始發送給客戶端,即完成斷點下載。
2.2 斷點上傳
??斷點上傳的話相比于下載稍微復雜一些。由于是多用戶訪問服務器端,我們在上傳文件的時候,需要區分文件所屬(即是哪個用戶上傳的)。例如,用戶1上傳文件a,傳到一半暫停后(這時,服務器端的數據庫里已經有了用戶1和文件a的相關信息),有某一個用戶也上傳文件a。這時,服務器端需要判斷這“ 某一個用戶”是否為用戶1。若是的話,就可以進行斷點上傳。若不是,則說明是其他用戶在上傳該文件a,這時文件a是需要完整重傳的。
?? 同樣,斷點上傳完畢后文件完整標志位置1,方便后面用戶下載。
最后來說說下載上傳的實現細節
對于客戶端的文件下載方法:
對于服務端響應客戶端文件下載的方法:
- 判斷收到的文件名在服務端是否存在或是否能被打開
- 服務器端開辟多個線程,根據客戶端發送的ok或是over進行文件傳輸,傳輸過程中用poll監測異常斷開事件。
//
對于客戶端的文件上傳方法:
對于服務端響應客戶端文件上傳的方法:
2.3大文件傳輸
Linux下open函數只能打開2G以內的文件,若大于2G,open會執行失敗。
綜合了一些網上相關文章,原因在于:
32位Linux系統內部處理文件檔案是采用的指標定義為long,在32位系統上,long為4字節即32位,因此只能尋址2^(32-1)=2G的范圍。
更具體的,使用open,lseek函數操作文件時,lseek原型為
long lseek(int handle, long offset, int fromwhere);
lseek函數用于指定文件操作指針的偏移量,其中offset參數表示偏移量大小,可用來將文件指針偏移用于對文件進行寫入等操作。
那么要實現超過2G文件的傳輸,可采用如下方法:
open函數原型:
int open(const char *pathname, int flags, mode_t mode);
flags參數表示打開文件時所采用的操作,必選項包括只讀,只寫,可讀可寫三種操作之一;
另外用追加、截斷、創建等操作與三個必選項按位或。
第三個參數表示文件訪問的權限,只有在第二個參數為創建時才有效。
3.sendfile 零拷貝
在作者提供的源碼v9版本中,作者使用了sendfile技術;而v12版本中,我沒看到sendfile的使用,而是采用多線程實現分布式,采用IO多路復用中的poll實現對異常斷開事件的監測。
所謂的零拷貝技術,就是指在傳統的read/write中,會發生多次CPU拷貝操作。而采用零拷貝技術后,直接將磁盤里的數據讀取到操作系統的內核緩沖區,這樣子就減少了數據拷貝,和上下文切換的次數。
具體參見文章:Linux IO復用技術與零拷貝。
4 演示圖:
4.1 啟動服務端:
4.2 啟動客戶端并注冊新用戶
登錄成功:
4.3 上傳小文件:
客戶端
服務端:
4.4 上傳大文件并斷點續傳:
客戶端上傳過程中強制中止:
服務端顯示
客戶端重新登錄并繼續上傳該文件:(會先得到已經上傳的文件大小)
服務端顯示:
4.5 秒傳的實現:
客戶端重新上傳300M.mp4文件:
服務器端顯示,(因為之前已上傳過該文件)
4.6 不同用戶上傳同一文件,其中某一個用戶中途退出
刪除3_4G.mp4文件后,使用用戶1重新上傳3_4G.mp4,中途中止,
接著用戶2上傳3_4G.mp4,
服務器顯示:
由于屬于不同用戶,該文件必須重傳而不能續傳。
5 待完善的地方
5.1 一點小bug
通過rm-f命令在bash界面刪除文件,md5表中仍然保留了文件的md5值。這算是一個小bug。
5.2 自己運行源碼時有問題的地方
源代碼下載下來后,直接運行會出現各種各樣的問題。其中有一個關于mysql的問題,提示
錯誤如下: 2058: This handle is already connected. Use a separate handle for each connet該問題是由于源代碼中出現多次下列語句,比如client/user.c文件中27行,已經建立了一個mysql連接,后面if語句里面又來了一個判斷,所以會報錯,mysql的句柄重復使用。
該問題在源代碼中出現次數比較多,親測刪除后就可以運行了。
修改過后的代碼鏈接如下:
https://download.csdn.net/download/qq_35027690/21736932
總結
以上是生活随笔為你收集整理的基于TCP的大文件传输c语言项目的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C#源代码—姓名 请输入老师的编号、姓名
- 下一篇: 飞桨创意赛火热进行中,总有一款AI时代C