TFTP協議分析與實現

閒暇之餘做一個模塊化的TFTP DEMO程序,記錄一下。

1、TFTP協議與功能點介紹:

TFTP採用UDP協議進行TFTP協議的文件傳輸,其默認的協議socket爲:UDP + port 69;UDP不支持順序傳輸,但是TFTP有ACK的回覆,因此TFTP協議可以順序傳輸,但是可能會發生重傳過程中產生的已過時ACK/DATA傳輸到對端的等問題。TFTP適合在局域網傳輸文件大小不太大的文件,標準由RFC來定義,具體標準編號見末節,本demo分爲TFTP客戶端(client)和TFTP服務器(Server)兩部分;

基本功能描述:實現TFTP客戶端能夠往TFTP服務器上上傳一個幾十/幾百MB的文件,也能從TFTP服務器上下載一個幾十/幾百MB的文件;擴展(模塊化)功能:

①客戶端可以指定傳輸使用的Blksize(512/1024/1468/2048B/4096Byte等);

②客戶端下載有進度條顯示;

③客戶端可以在下載時重命名/選擇覆蓋;

④可支持超時重傳;

⑤可支持斷點續傳(自定義擴展選項爲bpid,暫未實現);

⑥可支持BlkSize重複利用(實現TFTP的超大文件傳輸);

⑦服務器支持多客戶端同時上傳/下載;

⑧服務器有客戶端的操作記錄日誌;

⑨服務器有debug開關進行有關數據、任務的查看;等。

2、基本功能描述:

2.1、協議格式流程介紹:

協議支持六種報文類型,分別爲:讀請求、寫請求、ACK回覆、DATA數據、ERROR報文、OPTIONAL選項請求、OACK選項回覆ACK報文。如下:

opcode1~5報文格式
 opcode operation
 1 Read request (RRQ)
 2 Write request (WRQ)
 3 Data (DATA)
 4 Acknowledgment (ACK)
 5 Error (ERROR)
 6 Acknowledge of Request and options (OACK)

對於OACK格式以及以上六種報文的格式詳情參考RFC標準文檔,一個讀寫請求的較爲完整流程分別如下所示:

Read/Write Request流程

:最後一個DATA報文的長度爲0~blksize-1,也就是說當文件還有blksize大小還未傳輸時,需要再發送兩個DATA報文,其中一個DATA長度爲blksize,最後一個長度爲0,表示文件傳輸完畢(當前未進行傳輸文件總長度,與請求報文中傳輸的tsize進行比較校驗的操作,但是計算了MD5值)。

2.2、基礎模塊實現描述:

2.2.1、shell功能模塊

以鏈表結構實現命令的註冊、命令管理、命令動態註冊、命令提示、命令格式化輸入、命令解析、命令匹配執行等功能,比如debug命令用來實現debug信息的輸出,以及debug信息內容的可選擇性輸出(debug信息所打印的當前taskName等)。

shell命令鏈表的節點結構爲:

typedef struct tftpShellList_s
{
	INT32 _cmdArgc;
	tftpShellCmd_t _cmdArgv;
	tftpCmdAbil_t _status;		/* 命令的能力、屬性狀態 */
	tftp_cmd_deal_fun _dealFun;	/* 命令的執行函數 */
	struct tftpShellList_s * _pre;
	struct tftpShellList_s * _next;
}tftpShellList_t;

:此處爲了方便對於命令argv緩衝區採用 "固定個數 + 固定長度(固定長度只是限制命令長度,其內存還是才用的動態申請的方式)" 的方式定義,argc描述該註冊命令命令參數個數;更好的方式爲動態申請空間,防止過多內存浪費:

#define __TFTP_SHELL_CMD_MAX_NUM_            (1U << 5))
#define __TFTP_SHELL_CMD_MAX_LEN_            (1U << 5))

對應的鏈表節點爲:

shell管理鏈表結構

命令註冊的接口爲:

tftp_shell_cmd_register
(
    tftp_cmd_deal_fun function,
    tftpCmdAbil_t ability,
    CHAR * cmd_str
);

function爲註冊命令的回調函數,ability爲註冊命令的特性與能力,如:是否隱藏、是否動態註冊等,cmd_str爲格式化的命令註冊字符串,爲命令的子命令與子命令描述信息的格式化字符串,由tftp_shell_cmd_register負責解析並掛載到命令註冊管理的鏈表中去。

以如下爲例:

tftp_shell_cmd_register((tftp_cmd_deal_fun)tftp_shell_cmd_display,
    __TFTP_CMD_NORMAL_ | __TFTP_CMD_DYN_,
        "shellcmd{Display command with format for special command}"
            "display{display command format}"
                "__STRING__{command string}");

註冊命令”shellcmd display __STRING__”,命令用於顯示某一條命令的詳細格式以及信息。__TFTP_CMD_NORMAL_ 和 __TFTP_CMD_DYN_表示該條命令可以正常顯示,並且默認註冊,__STRING__表示輸入的信息爲字符串(該字符串如果匹配到註冊的某條命令就會把該命令的命令格式以及命令描述輸出),實際的功能即:遍歷命令管理的鏈表,匹配字符串,並將匹配到的命令格式化輸出,如tftpserver命令爲啓動tftp服務器,命令格式如下:

tftp>shellcmd display tftpserver
    command list detail information:   
    -------------------------------------<<<-command:tftpserver->>>------------------------------------
        tftpserver: tftp server enable/disable
                __STRING__: enable or disable
                __IPADDR__: server ip address
    ---------------------------------------------------------------------------------------------------

此外有一條隱藏的命令:dynamic command __STRING__ enable/disable,用於使能/隱藏可以動態註冊的命令(默認都是可以動態註冊的,除了dynamic命令外,由於dynamic不對外呈現,且不能被刪除,因此除了調試外,基本別無他用):

tftp>dynamic command all enable
    sem is already dynamic register success!
    tftptask is already dynamic register success!
    taskpool is already dynamic register success!
tftp>shellcmd display tftptask
    command list detail information:
    --------------------------------------<<<-command:tftptask->>>-------------------------------------
        tftptask: all childs task information for tftp process display
        display: display some information
        taskId: display with tid
                __INT32__: task tid(-1 is all)
    ---------------------------------------------------------------------------------------------------
tftp>

2.2.2、log功能模塊

Log功能即日誌功能,用來實現一些正常/異常/debug的信息打印與記錄保存,分爲讀個級別:normal正常打印、note提示打印、debug調試打印、warn警告打印、error錯誤打印等,其中記錄日誌到日誌文件中(當前所有日誌同時都會默認打印到shell),日誌文件以級別命名:tftpDebug.log、tftpError.log、tftpNormal.log、tftpNote.log、tftpWarn.log等。debug日誌需要debug命令開啓對應debug開關纔可以打印出來,默認都是關閉debug信息的。

LOG模塊提供給各個模塊的不同級別打印的接口(宏)爲:

#define TFTP_LOGNOR(format, ...)
#define TFTP_LOGDBG(switch, format, ...)
#define TFTP_LOGNOTE(format, ...)
#define TFTP_LOGWARN(format, ...)
#define TFTP_LOGERR(format, ...)

TFTP_LOGDBG爲例,如下,當client模塊的debug開關打開時,執行到該行就會打印對應信息(打印指的是輸出到shell,此外日誌都默認輸出到對應的文件中去,如用TFTP_LOGDBG打印,在輸出到shell的同時輸出到debug的日誌文件中去),如果client模塊的debug開關關閉,則執行到該行時就不會打印相關的信息:

TFTP_LOGDBG(tftp_dbgSwitch_client, "operator:%s, ipaddr:%s, filename:%s, blksize:%d, timeout:%d", \
        pOperator, pIpaddr, pFilename, pBlksize, pTimeout);

Debug開關命令格式爲:

tftp>shellcmd display tftplogDbg
    command list detail information:
    -------------------------------------<<<-command:tftplogDbg->>>------------------------------------
        tftplogDbg: tftp log debug switch
        debug: debug switch open or close
                __UINT32__: switch choose[eg:
                                        ->(0)task
                                        ->(1)server
                                        ->(2)client
                                        ->(3)shell
                                        ->(4)sem
                                        ->(5)send
                                        ->(6)recv
                                        ->(7)pack
                                        ->(8)other]
                __STRING__: open/close
    ----------------------------------------------------------------------------------------------------

此外tftplogInfo用來讀取日誌文件並顯示,和tftplogTask用來打開/關閉日誌記錄中的taskName選項。如當taskName選項打開時,log信息打印如下:

tftp>tftplogTask taskname open
open task name logging switch
tftp>tftpserver enable 192.168.10.100
2019-10-02 21:33:41(UTC)[Wednesday] (tftpShellTask) %% TFTP-ERROR :(65)bind socket fd fail, sockfd = 8, port = 69, errno = 99

2.2.3、task功能模塊

 

2.2.4、sem功能模塊

 

2.2.5、server功能模塊

 

2.2.6、client功能模塊

 

2.2.7、cfg功能模塊

 

 

2.3、擴展功能模塊與創新點描述:

2.3.1、md5功能(已實現):

計算文件的MD5值,可以用Linux自帶的md5sum命令實現,system(“md5sum filePath/fileName”);而此處之前有自己實現過,因此拷貝過來即可,比起md5sum的效率慢多了(1G得算幾十秒),可以優化:一次性多讀取一些字節,如2048Byte而不是64Byte。此處不再優化,tftp一般傳輸的文件也就幾十兆,最大百十來兆,計算MD5的耗時也就幾秒,暫時不考慮優化。

2.3.2、超時重傳功能(已實現):

採用select函數實現timeout重傳的的功能,在網絡波動時既可以等待一定時間重發送,防止報文未發送成功或者對端未接收到,但是有重傳時間和次數限制,保證讓線程池中線程或者客戶端不會永久性阻塞下去,並提示信息,以達到故障排查的目的。

2.3.3、斷點續傳功能(未實現):

斷點續傳指的是,網絡中斷,長時間無法恢復,在此文件已經傳輸部分會被臨時保存,並且創建一個斷點信息的斷點文件(如客戶端/服務器地址、文件名、文件大小、已經傳輸大小、當時採用blksize、斷點blkid、斷點blkid被複用的輪數等信息),當數小時、數天、數月之後再次傳輸該文件時,會檢查斷點文件,如果確認傳輸的文件與之前的斷點文件爲同一個文件,則啓用斷點續傳機制:blkid從非0開始,該功能應該是私有協議,因爲標準字段並無bpid的定義。

3、TFTP實現架構:

3.1、客戶端流程:

 

3.2、服務器流程:

 

4、TFTP測試注意點:

 

5、參考資料:

RFC1350(RFC783已作廢):THE TFTP PROTOCOL (REVISION 2)

RFC2347(RFC1782已作廢): TFTP Option Extension

RFC2348(RFC1783已作廢):TFTP Blocksize Option

RFC2349(RFC1784已作廢):TFTP Timeout Interval and Transfer Size Options

RFC1785:TFTP Option Negotiation Analysis

6、GITHUB傳送門:

TFTP協議server、client一體化基本版

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章