<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                #### 一、TFTP協議簡單介紹 ##### 1.定義 TFTP(Trivial File Transfer Protocol):簡單文件傳輸協議)。 TFTP是TCP/IP協議族中的一個用來在客戶端與服務器之間進行簡單文件傳輸的協議,傳輸不復雜、開銷不大的文件。端口號固定為69。 TFTP是一個傳輸文件的簡單協議,它基于UDP協議而實現。 ##### 2.特點 簡單、占用資源少、基于UDP實現、端口號為69、適合在局域網內傳輸小文件。 ##### 3.TFTP支持五種類型的包 opcode operation * Read request (RRQ) * Write request (WRQ) * Data (DATA) * Acknowledgment (ACK) * Error (ERROR) #### 二、TFTP數據包格式 ![](https://box.kancloud.cn/e2fcc247596b082393b9fab9fb33c14e_819x451.png) ~~~ 1、讀寫請求 操作碼 + 文件名 + 0 + 模式 + 0 2Bytes String 1Byte String 1Byte 當操作碼的取值為1時,表示RD 讀請求;當操作碼的取值為2時,表示WE 寫請求。 2、數據包 操作碼 + 塊編碼 + 數據 2Bytes 2Bytes 512Bytes 數據包操作碼值為3。 3、ACK 操作碼 + 塊編碼 2Bytes 2Bytes ACK 操作碼值為4。 4、ERROR 操作碼 + 差錯碼 + 差錯信息 + 0 2Bytes 2Bytes String 1Byte ERROR 操作碼值為5。 ~~~ >[danger] 注意 >**** >1、當客戶端接收到的數據小于516字節時,表示服務器發送數據完成! 2、塊編碼從0開始,每次加1,它的范圍是[0, 65535]。 #### 三、TFTP協議過程分析 ##### 1、下載過程 第一步:客戶端給服務器發送下載請求,數據格式為(操作碼1+文件名+0+模式+0)。 第二步:服務器接收到請求之后,回復客戶端消息,數據格式為元組類型。如下所示:(操作碼3+塊編碼0+數據, (IP號, 端口號))。 第三步:客戶端每接受一次數據,都要回復服務器一次ACK信號。 第四步:直到客戶端接收到的數據小于516個字節,才說明服務器發送完畢! ##### 2、上傳過程 第一步:客戶端給服務器發送上傳請求,數據格式為(操作碼2+文件名+0+模式+0)。 第二步:服務器接收到請求之后,回復客戶端ACK消息,數據格式為元組類型。如下所示:(操作碼4+塊編碼0, (IP號, 端口號))。 第三步:客戶端每發送一次數據,服務器都要回復一次ACK信號。 第四步:直到客戶端發送完數據才結束。 #### 四、TFTP具體傳輸數據 ##### 1、服務器回復客戶端下載請求 ~~~ (b'\x00\x03\x00\x01\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x02\x01\x00H\x00H\x00\x00\xff\xe1\x08\xbbExif\x00\x00MM\x00*\x00 \x00\x00\x08\x00\x07\x01\x12\x00\x03\x00\x00\x00\x01\x00\x01\x00\x00\x01\x1a\x00\x05\x00\x00\x00\x01\x00\x00\x00b\x01\x1b \x00\x05\x00\x00\x00\x01\x00\x00\x00j\x01(\x00\x03\x00\x00\x00\x01\x00\x02\x00\x00\x011\x00\x02\x00\x00\x00\x14\x00\x00\x00r \x012\x00\x02\x00\x00\x00\x14\x00\x00\x00\x86\x87i\x00\x04\x00\x00\x00\x01\x00\x00\x00\x9c\x00\x00\x00\xc8\x00\x00\x00H\x00 \x00\x00\x01\x00\x00\x00H\x00\x00\x00\x01Adobe Photoshop 7.0\x002004:06:15 16:14:56\x00\x00\x00\x00\x03\xa0\x01\x00\x03\x00 \x00\x00\x01\xff\xff\x00\x00\xa0\x02\x00\x04\x00\x00\x00\x01\x00\x00\x04\x00\xa0\x03\x00\x04\x00\x00\x00\x01\x00\x00\x03\x00 \x00\x00\x00\x00\x00\x00\x00\x06\x01\x03\x00\x03\x00\x00\x00\x01\x00\x06\x00\x00\x01\x1a\x00\x05\x00\x00\x00\x01\x00\x00\x01 \x16\x01\x1b\x00\x05\x00\x00\x00\x01\x00\x00\x01\x1e\x01(\x00\x03\x00\x00\x00\x01\x00\x02\x00\x00\x02\x01\x00\x04\x00\x00\x00 \x01\x00\x00\x01&\x02\x02\x00\x04\x00\x00\x00\x01\x00\x00\x07\x8d\x00\x00\x00\x00\x00\x00\x00H\x00\x00\x00\x01\x00\x00\x00H\x00 \x00\x00\x01\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x02\x01\x00H\x00H\x00\x00\xff\xed\x00\x0cAdobe_CM\x00\x02\xff\xee\x00\x0eAdobe \x00d\x80\x00\x00\x00\x01\xff\xdb\x00\x84\x00\x0c\x08\x08\x08\t\x08\x0c\t\t\x0c\x11\x0b\n\x0b\x11\x15\x0f\x0c\x0c\x0f\x15\x18\x13 \x13\x15\x13\x13\x18\x11\x0c\x0c\x0c\x0c\x0c\x0c\x11\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c \x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x01\r\x0b\x0b\r\x0e\r\x10\x0e\x0e\x10\x14\x0e\x0e\x0e\x14\x14\x0e\x0e\x0e\x0e\x14\x11\x0c\x0c \x0c\x0c\x0c\x11\x11\x0c\x0c\x0c\x0c\x0c\x0c\x11\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c \x0c\x0c\x0c\x0c\x0c\x0c\x0c\xff\xc0\x00\x11', ('192.168.43.119', 54835)) ~~~ 由于數據太長,一行不方便顯示,這里多行展示了TFTP服務器回復客戶端的下載請求數據。 ##### 2、服務器回復客戶端上傳請求 `(b'\x00\x04\x00\x00', ('192.168.43.119', 62768)) ` #### 五、TFTP傳輸過程 ![](https://box.kancloud.cn/ec8f4661731fec3fa98e30de3fca24e3_539x610.png) #### 六、Python實現TFTP協議 ##### 1、客戶端下載文件參考程序 ~~~ #coding=utf-8 #導包 import sys import struct from socket import * #全局變量 g_server_ip = '' g_downloadFileName = '' #運行程序格式不正確 def run_test(): "判斷運行程序傳入參數是否有錯" global g_server_ip global g_downloadFileName if len(sys.argv) != 3: print("運行程序格式不正確") print('-'*30) print("tips:") print("python3 tftp_download.py 192.168.1.1 test.jpg") print('-'*30) exit() else: g_server_ip = sys.argv[1] g_downloadFileName = sys.argv[2] #print(g_server_ip, g_downloadFileName) #主程序 def main(): run_test() # 打包 sendDataFirst = struct.pack('!H%dsb5sb'%len(g_downloadFileName), 1, g_downloadFileName.encode('gb2312'), 0, 'octet'.encode('gb2312'), 0) # 創建UDP套接字 s = socket(AF_INET, SOCK_DGRAM) # 發送下載文件請求數據到指定服務器 s.sendto(sendDataFirst, (g_server_ip, 69)) #第一次發送, 連接tftp服務器 downloadFlag = True #表示能夠下載數據,即不擅長,如果是false那么就刪除 fileNum = 0 #表示接收文件的序號 # 以二進制格式創建新文件 f = open(g_downloadFileName, 'wb') while True: #3. 接收服務發送回來的應答數據 responseData = s.recvfrom(1024) #print(responseData) recvData, serverInfo = responseData # 解包 packetOpt = struct.unpack("!H", recvData[:2]) #操作碼 packetNum = struct.unpack("!H", recvData[2:4]) #塊編號 #print(packetOpt, packetNum) # 接收到數據包 if packetOpt[0] == 3: #optNum是一個元組(3,) # 計算出這次文件的序號,是上一次接收到的+1。 fileNum += 1 # 文件超過了65535 那么就又從0開始計數。 if fileNum == 65536: fileNum = 0 # 包編號是否和上次相等 if fileNum == packetNum[0]: f.write(recvData[4:]) #寫入文件 fileNum = packetNum[0] # 整理ACK的數據包 ackData = struct.pack("!HH", 4, packetNum[0]) s.sendto(ackData, serverInfo) # 錯誤應答 elif packetOpt[0] == 5: print("sorry,沒有這個文件!") downloadFlag = False break else: print(packetOpt[0]) break # 接收完成,退出程序。 if len(recvData) < 516: downloadFlag = True print("%s文件下載完畢!"%g_downloadFileName) break if downloadFlag == True: f.close() else: os.unlink(g_downloadFileName) #沒有下載的文件,就刪除剛創建的文件。 #調用main函數 if __name__ == '__main__': main() ~~~ ##### 2、客戶端上傳文件程序 ~~~ #coding=utf-8 # 導包 import sys import struct from socket import * # 全局變量 g_server_ip = '' g_uploadFileName = '' #運行程序格式不正確 def run_test(): "判斷運行程序傳入參數是否有錯" global g_server_ip global g_uploadFileName if len(sys.argv) != 3: print("運行程序格式不正確") print('-'*30) print("tips:") print("python3 tftp_upload.py 192.168.1.1 test.jpg") print('-'*30) exit() else: g_server_ip = sys.argv[1] g_uploadFileName = sys.argv[2] #print(g_server_ip, g_uploadFileName) #主程序 def main(): run_test() # 打包 sendDataFirst = struct.pack('!H%dsb5sb'%len(g_uploadFileName), 2, g_uploadFileName.encode('gb2312'), 0, 'octet'.encode('gb2312'), 0) # 創建UDP套接字 s = socket(AF_INET, SOCK_DGRAM) # 發送上傳文件請求到指定服務器 s.sendto(sendDataFirst, (g_server_ip, 69)) #第一次發送, 連接tftp服務器 fileNum = 0 #表示接收文件的序號 # 以二進制格式打開文件 f = open(g_uploadFileName, 'rb') # 第一次接收數據 responseData = s.recvfrom(1024) # print(responseData) recvData, serverInfo = responseData #print(recvData) #print(serverInfo) # 解包 packetOpt = struct.unpack("!H", recvData[:2]) #操作碼 packetNum = struct.unpack("!H", recvData[2:4]) #塊編號 #print(packetOpt, packetNum) if packetOpt[0] == 5: print("tftp服務器發生錯誤!") exit() while True: # 從文件中讀取512字節數據 readFileData = f.read(512) # 打包 sendData = struct.pack('!HH', 3, fileNum) + readFileData # 發送數據到tftp服務器 s.sendto(sendData, serverInfo) #第二次發給服務器的隨機端口 # 接受服務器回傳數據 recvData, serverInfo = s.recvfrom(1024) #print(recvData) # 解包 packetOpt = struct.unpack("!H", recvData[:2]) #操作碼 packetNum = struct.unpack("!H", recvData[2:4]) #塊編號 if packetOpt[0] == 5: print("tftp服務器發生錯誤!") exit() if len(sendData) < 516 or packetNum[0] != fileNum: print("%s文件上傳成功!"%g_uploadFileName) break fileNum += 1 # 關閉文件 f.close() # 關閉套接字 s.close() #調用main函數 if __name__ == '__main__': main() ~~~ ##### 3、服務器參考程序 ~~~ #coding=utf-8 # 導包 import sys import struct from socket import * from threading import Thread ''''' 利用多線程的機制,來實現tftp服務器同時進行上傳和下載功能。 ''' # 客戶端上傳線程 def upload_thread(fileName, clientInfo): "負責處理客戶端上傳文件" fileNum = 0 #表示接收文件的序號 # 以二進制方式打開文件 f = open(fileName, 'wb') # 創建UDP套接字 s = socket(AF_INET, SOCK_DGRAM) # 打包 sendDataFirst = struct.pack("!HH", 4, fileNum) # 回復客戶端上傳請求 s.sendto(sendDataFirst, clientInfo) #第一次用隨機端口發送 while True: # 接收客戶端發送的數據 responseData = s.recvfrom(1024) #第二次客戶連接我隨機端口 # print(responseData) recvData, clientInfo = responseData #print(recvData, clientInfo) # 解包 packetOpt = struct.unpack("!H", recvData[:2]) #操作碼 packetNum = struct.unpack("!H", recvData[2:4]) #塊編號 #print(packetOpt, packetNum) # 客戶端上傳數據 if packetOpt[0] == 3 and packetNum[0] == fileNum: # 保存數據到文件中 f.write(recvData[4:]) # 打包 sendData = struct.pack("!HH", 4, fileNum) # 回復客戶端ACK信號 s.sendto(sendData, clientInfo) #第二次用隨機端口發 fileNum += 1 if len(recvData) < 516: print("用戶"+str(clientInfo), end='') print(':上傳'+fileName+'文件完成!') break # 關閉文件 f.close() # 關閉UDP套接字 s.close() # 退出上傳線程 exit() # 客戶端下載線程 def download_thread(fileName, clientInfo): "負責處理客戶端下載文件" # 創建UDP套接字 s = socket(AF_INET, SOCK_DGRAM) fileNum = 0 #表示接收文件的序號 try: f = open(fileName,'rb') except: # 打包 errorData = struct.pack('!HHHb', 5, 5, 5, fileNum) # 發送錯誤信息 s.sendto(errorData, clientInfo) #文件不存在時發送 exit() #退出下載線程 while True: # 從本地服務器中讀取文件內容512字節 readFileData = f.read(512) fileNum += 1 # 打包 sendData = struct.pack('!HH', 3, fileNum) + readFileData # 向客戶端發送文件數據 s.sendto(sendData, clientInfo) #數據第一次發送 if len(sendData) < 516: print("用戶"+str(clientInfo), end='') print(':下載'+fileName+'文件完成!') break # 第二次接收數據 responseData = s.recvfrom(1024) # print(responseData) recvData, clientInfo = responseData #print(recvData, clientInfo) #解包 packetOpt = struct.unpack("!H", recvData[:2]) #操作碼 packetNum = struct.unpack("!H", recvData[2:4]) #塊編號 #print(packetOpt, packetNum) if packetOpt[0] != 4 or packetNum[0] != fileNum: print("文件傳輸錯誤!") break # 關閉文件 f.close() # 關閉UDP套接字 s.close() # 退出下載線程 exit() # main函數 def main(): # 創建UDP套接字 s = socket(AF_INET, SOCK_DGRAM) # 解決重復綁定端口 s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) # 綁定任意IP,端口號69 s.bind(('', 69)) print("tftp服務器成功啟動!") print("正在運行中...") while True: # 接收客戶端發送的消息 recvData, clientInfo = s.recvfrom(1024) # 第一次客戶連接69端口 #print(clientInfo) # 解包 if struct.unpack('!b5sb', recvData[-7:]) == (0, b'octet', 0): opcode = struct.unpack('!H',recvData[:2]) # 操作碼 fileName = recvData[2:-7].decode('gb2312') # 文件名 # 請求下載 if opcode[0] == 1: t = Thread(target=download_thread, args=(fileName, clientInfo)) t.start() # 啟動下載線程 # 請求上傳 elif opcode[0] == 2: t = Thread(target=upload_thread, args=(fileName, clientInfo)) t.start() # 啟動上傳線程 # 關閉UDP套接字 s.close() # 調用main函數 if __name__ == '__main__': main() ~~~ >[warning] 用到的軟件:Wireshark,Tftpd32 >**** >Wireshark軟件進行調試,也可以打印傳輸數據信息進行調試! >Tftpd32 一個可以當做服務器軟件 > >[success] 小結------tftp可以理解為: >*** >* 1.tftp是基于udp的協議 >* 2.實現簡單的tftp,首先要有tftp的協議圖 >* 3.tftp默認接收端口為69,但每次有連接過來后,tftp會隨機分配一個端口來專門為這個連接來服務。 >* 4.操作碼:1.上傳 2.下載 3.傳數據 4.接收確認 5.錯誤碼
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看