1. 程式人生 > >應用:TFTP客戶端

應用:TFTP客戶端

1. TFTP協議介紹

TFTP(Trivial File Transfer Protocol,簡單檔案傳輸協議),是TCP/IP協議族中的一個用來在客戶端與伺服器之間進行簡單檔案傳輸的協議

特點:

1)簡單
2)佔用資源小
3)適合傳遞小檔案
4)適合在區域網進行傳遞
5)埠號為69
6)基於UDP實現

2. TFTP下載過程

TFTP伺服器預設監聽69號埠;當客戶端傳送“下載”請求(即讀請求)時,需要向伺服器的69埠傳送;伺服器若批准此請求,則使用一個新的、臨時的 埠進行資料傳輸。

當伺服器找到需要現在的檔案後,會立刻開啟檔案,把檔案中的資料通過TFTP協議傳送給客戶端;

如果檔案的總大小較大(比如3M),那麼伺服器分多次傳送,每次會從檔案中讀取512個位元組的資料傳送過來;

因為傳送的次數有可能會很多,所以為了讓客戶端對接收到的資料進行排序,所以在伺服器傳送那512個位元組資料的時候,會多發2個位元組的資料,用來存放序號,並且放在512個位元組資料的前面,序號是從1開始的

因為需要從伺服器上下載檔案時,檔案可能不存在,那麼此時伺服器就會發送一個錯誤的資訊過來,為了區分服務傳送的是檔案內容還是錯誤的提示資訊,所以又用了2個位元組 來表示這個資料包的功能(稱為操作碼),並且在序號的前面

因為udp的資料包不安全,即傳送方傳送是否成功不能確定,所以TFTP協議中規定,為了讓伺服器知道客戶端已經接收到了剛剛傳送的那個資料包,所以當客戶端接收到一個數據包的時候需要向伺服器進行傳送確認資訊,即傳送收到了,這樣的包成ACK(應答包)

為了標記資料已經發送完畢,所以規定,當客戶端接收到的資料小於516(2位元組操作碼+2個位元組的序號+512位元組資料)時,就意味著伺服器傳送完畢了

TFTP資料包的格式如下:

2. 參考程式碼如下:

#coding=utf-8
from socket import *
import struct
import sys

if len(sys.argv) != 2:
    print('-'*30)
    print("tips:")
    print("python xxxx.py 192.168.1.1")
    print('-'*30)
    exit()
else:
    ip = sys.argv[1]

# 建立udp套接字
udpSocket = socket(AF_INET, SOCK_DGRAM)

#構造下載請求資料
cmd_buf = struct.pack("!H8sb5sb",1,"test.jpg",0,"octet",0)

#傳送下載檔案請求資料到指定伺服器
sendAddr = (ip, 69)
udpSocket.sendto(cmd_buf, sendAddr)

p_num = 0
recvFile = ''

while True:
    recvData,recvAddr = udpSocket.recvfrom(1024)
    recvDataLen = len(recvData)
    # print recvAddr # for test
    # print len(recvData) # for test
    cmdTuple = struct.unpack("!HH", recvData[:4])
    # print cmdTuple # for test
    cmd = cmdTuple[0]
    currentPackNum = cmdTuple[1]
if cmd == 3: #是否為資料包
    # 如果是第一次接收到資料,那麼就建立檔案
    if currentPackNum == 1:
        recvFile = open("test.jpg", "a")
    # 包編號是否和上次相等
    if p_num+1 == currentPackNum:
        recvFile.write(recvData[4:]);
        p_num +=1
        print '(%d)次接收到的資料'%(p_num)
        ackBuf = struct.pack("!HH",4,p_num)
        udpSocket.sendto(ackBuf, recvAddr)
    # 如果收到的資料小於516則認為出錯
    if recvDataLen<516:
        recvFile.close()
        print '已經成功下載!!!'
        break
elif cmd == 5: #是否為錯誤應答
    print "error num:%d"%currentPackNum
    break

udpSocket.close()