[转载] Python中TFTP的理解
參考鏈接: Python中的打包pack和拆包unpack參數(shù)
Num01–>TFTP協(xié)議介紹?
?
?TFTP(Trivial File Transfer Protocol,簡單文件傳輸協(xié)議)?
?是TCP/IP協(xié)議族中的一個用來在客戶端與服務(wù)器之間進(jìn)行簡單文件傳輸?shù)膮f(xié)議?
?特點(diǎn):?
?1,簡單? 2,占用資源小? 3,適合傳遞小文件? 4,適合在局域網(wǎng)進(jìn)行傳遞? 5,端口號為69? 6,基于UDP實(shí)現(xiàn)?
?
Num02–>TFTP下載過程?
?
?TFTP服務(wù)器默認(rèn)監(jiān)聽69號端口?
?當(dāng)客戶端發(fā)送“下載”請求(即讀請求)時,需要向服務(wù)器的69端口發(fā)送?
?服務(wù)器若批準(zhǔn)此請求,則使用一個新的、臨時的 端口進(jìn)行數(shù)據(jù)傳輸?
?
?
?
?當(dāng)服務(wù)器找到需要現(xiàn)在的文件后,會立刻打開文件,把文件中的數(shù)據(jù)通過TFTP協(xié)議發(fā)送給客戶端?
?如果文件的總大小較大(比如3M),那么服務(wù)器分多次發(fā)送,每次會從文件中讀取512個字節(jié)的數(shù)據(jù)發(fā)送過來?
?因?yàn)榘l(fā)送的次數(shù)有可能會很多,所以為了讓客戶端對接收到的數(shù)據(jù)進(jìn)行排序,所以在服務(wù)器發(fā)送那512個字節(jié)數(shù)據(jù)的時候,會多發(fā)2個字節(jié)的數(shù)據(jù),用來存放序號,并且放在512個字節(jié)數(shù)據(jù)的前面,序號是從1開始的?
?因?yàn)樾枰獜姆?wù)器上下載文件時,文件可能不存在,那么此時服務(wù)器就會發(fā)送一個錯誤的信息過來,為了區(qū)分服務(wù)發(fā)送的是文件內(nèi)容還是錯誤的提示信息,所以又用了2個字節(jié) 來表示這個數(shù)據(jù)包的功能(稱為操作碼),并且在序號的前面?
?
操作碼? ? ?功能
?1? ? ? 讀請求,即下載
?2? ? ? 寫請求,即上傳
?3? ? ? 表示數(shù)據(jù)包,即DATA
?4? ? ? 確認(rèn)碼,即ACK
?5? ? ? 錯誤?
?
?因?yàn)閡dp的數(shù)據(jù)包不安全,即發(fā)送方發(fā)送是否成功不能確定,所以TFTP協(xié)議中規(guī)定,為了讓服務(wù)器知道客戶端已經(jīng)接收到了剛剛發(fā)送的那個數(shù)據(jù)包,所以當(dāng)客戶端接收到一個數(shù)據(jù)包的時候需要向服務(wù)器進(jìn)行發(fā)送確認(rèn)信息,即發(fā)送收到了,這樣的包成為ACK(應(yīng)答包)?
?為了標(biāo)記數(shù)據(jù)已經(jīng)發(fā)送完畢,所以規(guī)定,當(dāng)客戶端接收到的數(shù)據(jù)小于516(2字節(jié)操作碼+2個字節(jié)的序號+512字節(jié)數(shù)據(jù))時,就意味著服務(wù)器發(fā)送完畢了?
?
Num03–>TFTP數(shù)據(jù)包的格式?
?
Num04–>TFTP客戶端案例編寫?
#! /usr/bin/env python3
# -*- coding:utf-8 -*-?
?
from socket import *
import struct #為了實(shí)現(xiàn)打包struct.pack()和拆包struct.unpack()數(shù)據(jù)
import sys
?
# python3 05-xx.py 192.168.105.125 bb.jpg
def main():
?
? ? if len(sys.argv) < 3:
? ? ? ? sys.exit('usage : python3? %s ip filename' % sys.argv[0])
?
? ? #server_ip = '192.168.105.125'
? ? #file_name = 'bb.jpg'
?
? ? server_ip = sys.argv[1]
? ? file_name = sys.argv[2]
?
? ? udp_socket = socket(AF_INET, SOCK_DGRAM)
? ? server_addr = (server_ip,69)
?
? ? #? 打包數(shù)據(jù)
? ? # !表示網(wǎng)絡(luò)字節(jié)序,H表示2bytes無符號整數(shù),
? ? #? 5s表示長度為5字符串
? ? #? B表示1byte的無符號整數(shù)
? ? fmt = '!H%dsB5sB' % len(file_name)
? ? send_data = struct.pack(fmt,1,file_name.encode() ,0,b'octet',0)
? ? #send_data = struct.pack(fmt,1,file_name ,0,b'octet',0)
?
? ? udp_socket.sendto(send_data,server_addr)
?
? ? f =? None # 文件對象
? ? #上一次blockNum
? ? lastBlockNum = 0
?
? ? # 循環(huán)接收和應(yīng)答
? ? while True:
? ? ? ? recv_data,peer_addr = udp_socket.recvfrom(1024)
? ? ? ? # 拆包數(shù)據(jù)
? ? ? ? opcode,blockNum = struct.unpack('!HH',recv_data[:4])
?
? ? ? ? if opcode == 3: # 表示數(shù)據(jù)包
? ? ? ? ? ? # 寫入文件
? ? ? ? ? ? # 1打開文件
? ? ? ? ? ? # 第一次收到服務(wù)器發(fā)送數(shù)據(jù)包
? ? ? ? ? ? if blockNum == 1:?
? ? ? ? ? ? ? ? f = open(file_name,'wb')
?
? ? ? ? ? ? # 拆出數(shù)據(jù)
? ? ? ? ? ? data_fmt = '!%ds' % (len(recv_data) - 4)
? ? ? ? ? ? data_content = struct.unpack(data_fmt, recv_data[4:])
?
? ? ? ? ? ? # 寫入文件之前判斷寫過沒有
? ? ? ? ? ? # if 這一次blockNum == 上一次blockNum + 1
? ? ? ? ? ? if lastBlockNum + 1 == blockNum:
? ? ? ? ? ? ? ? #print(data_content[0])
? ? ? ? ? ? ? ? f.write(data_content[0]) # 拆出來是元組,bytes對象,write時候需要str字符串
?
? ? ? ? ? ? # 打包應(yīng)答數(shù)據(jù)
? ? ? ? ? ? ack_data = struct.pack('!HH',4,blockNum)
? ? ? ? ? ? udp_socket.sendto(ack_data,peer_addr) # 不能再給server_addr,因?yàn)槎丝谔栕兞?/p>
?
? ? ? ? ? ? # 當(dāng)應(yīng)答完畢,更新lastBlockNum
? ? ? ? ? ? lastBlockNum = blockNum
?
? ? ? ? ? ? # 如果數(shù)據(jù)長度小于 2 + 2 + 512 傳輸結(jié)束
? ? ? ? ? ? if len(recv_data) < 516:
? ? ? ? ? ? ? ? print('over')
? ? ? ? ? ? ? ? f.close()
? ? ? ? ? ? ? ? break
? ? ? ? elif opcode == 5:# 出錯
? ? ? ? ? ? err_num = blockNum
? ? ? ? ? ? # 拆出錯誤信息
? ? ? ? ? ? fmt = "!%ds" % (len(recv_data) - 5)
? ? ? ? ? ? err_msg = struct.unpack(fmt,recv_data[4:-1])
? ? ? ? ? ? print('出錯信息:%s' % err_msg)
? ? ? ? ? ? break
?
if __name__ == "__main__":
? ? main()
總結(jié)
以上是生活随笔為你收集整理的[转载] Python中TFTP的理解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: pipedreader_Java Pip
- 下一篇: Java中的main()方法是强制性的吗