Tftp文件傳輸服務器(基於UDP協議)


一個簡單的UDP服務端與客戶端

服務端: 

 1 from socket import *
 2 #創建套接字
 3 udp_server = socket(AF_INET,SOCK_DGRAM)
 4 
 5 msg_server = ("",8877)
 6 #綁定ip地址和端口
 7 udp_server.bind(msg_server)
 8 
 9 while True:
10     #接受消息,注意此處用的是   recvfrom()
11     msg_client = udp_server.recvfrom(1024)
12 
13     print("新客戶端已連接--->>>")
14 
15     if len(msg_client) != 0:
16         #msg_client中有兩個值,第一個表示收到的消息的內容,第二個表示客戶端的IP地址和端口的信息
17         print("%s:%s" % (msg_client[1], msg_client[0].decode("utf-8")))
18     else:
19         break
20 
21 udp_server.close()

 

客戶端:

from socket import *

sock = socket(AF_INET,SOCK_DGRAM)

addr_msg = ("192.168.1.104",8877)
#連接服務端
sock.connect(addr_msg)

while True:
    msg_send = input("請輸入要發送的信息:")
    if msg_send =='q':
        break
    #注意此處用的是sendto()     其中第一個參數表示要發送的消息的內容,第二個參數表示服務端的IP地址和端口的元組
    sock.sendto(msg_send.encode("utf-8"),addr_msg)

sock.close()

 

 

基於UDP的tftp文件傳輸

客戶端:

 1 from socket import *
 2 import struct
 3 import os
 4 
 5 def main():
 6     file_name = input("請輸入要下載的文件名:")
 7     #建立一個UDP的套接字
 8     udpsocket = socket(AF_INET,SOCK_DGRAM)
 9     #創建一個tftp的下載請求  H表示第一個參數占用兩個字節,d表示對應的參數為bite類型
10     #  %ds 表示文件名占用的字符個數,如后面的octet占用了5個字符,所以寫成5s
11     #  octet是tftp傳輸的一種模式,它決定了傳輸數據的格式,還有其他的幾種tftp傳輸的模式
12     #構造包使用的是struck中的pack   ,其中的 H 表示使參數占用兩個字節
13     request_header = struct.pack("!H%dsb5sb"%len(file_name),1,file_name.encode('utf-8'),0,b"octet",0)
14     server_msg = ("192.168.0.100",69)
15     #發送一個下載文件的請求
16     udpsocket.sendto(request_header,server_msg)
17 
18     # 這里必須以wb的格式打開文件,否則文件不能正常顯示
19     f = open(file_name,"wb")
20 
21     #  num表示的是數據塊的編號,這里使得其初始值為0
22     num = 0
23     #此處建立一個標記,以確認傳輸過程中沒發生錯誤
24     mask = True
25 
26     while True:
27         # 接收服務端發來的數據包
28         response_data = udpsocket.recvfrom(1024)
29         recv_data, server_info  = response_data
30 
31         # 此處用struct中的unpack來解包  H 表示數據占用兩個字節長度
32         # 這里的前兩個字節中存儲的是操作碼
33         operation_num = struct.unpack("!H",recv_data[:2])
34         # 這里的第三個和第四個字節中存儲的是數據塊的編號
35         package_num = struct.unpack("!H",recv_data[2:4])
36         print(package_num[0])
37 
38         # 操作碼為3時表示接收到的信息是服務端收到響應而發給客戶端的數據包
39         if operation_num[0] == 3:
40             #這一次收到的值應該是在上一次收到的值的基礎上加上一后的結果
41             num =num + 1
42             #此處表示一旦num的值超過了它2個字節所表示的值的范圍,則讓它從0開始再來重復一遍(主要是因為操作碼只能占2個字節)
43             if num == 65535:
44                 num = 0
45             # 進行塊編號的確認
46             if num == package_num[0]:  #此處取下標是因為此時的package_num表示的是一個元組,而塊編號是元組中的第一個數據
47                 # 將收到的具體數據寫入到文件中
48                 f.write(recv_data[4:])
49                 #塊編號的確認每次都要在之前的值上加1
50                 num = package_num[0]
51             #構造一個響應包  操作碼為4   發送的數據就是加上1后的塊編號
52             ack_data = struct.pack("!HH",4,package_num[0])
53             #發送響應包
54             udpsocket.sendto(ack_data, server_info)
55 
56         elif operation_num[0] == 5:
57             print("Error!")
58             #若發生錯誤信息,則將標記的值改成False , 以便后續操作刪除這個文件
59             mask = False
60             f.close()
61 
62         # 一個數據包的最大長度為 4+512 = 516 位,如果數據包小於這個數值,則表明這已經是最后一次的數據傳輸了
63         if len(recv_data) < 516:
64             print("Finish!")
65             break
66     # mask標記的值為True則表示文件傳輸的過程中沒有發生錯誤,就將本地接收到的文件關閉保存
67     if mask == True:
68         f.close()
69 
70     else:
71         # mask標記的值為False則表示文件傳輸的過程中出現了錯誤信息,則將已經接收到的文件刪除
72         os.remove(file_name)
73 
74 if __name__ == "__main__":
75     main()

至於服務端可以用 tftpd32 這個軟件來模擬實現

 

關於TFTP協議

 

TFTP協議是一種基於UDP的小型文件傳輸協議,它不具備FTP的許多功能

TFTP的端口號為69

 

一個數據包在接受的過程中最大長度為512+2+2=516字節,所以當它的長度小於516字節時便可以判斷出這是最后一次的數據傳輸

上面構造數據包時使用的H的意思是,讓它所代表的參數占用兩個字節的長度,從而使數據包符合TFTP協議的要求

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM