1. TFTP协议介绍
TFTP(Trivial File Transfer Protocol,简单文件传输协议)
是TCP/IP协议族中的一个用来在客户端与服务器之间进行简单文件传输的协议
特点:
- 简单
- 占用资源小
- 适合传递小文件
- 适合在局域网进行传递
- 端口号为69
- 基于UDP实现
TFTP数据包的格式如下:
操作码 | 功能 |
---|---|
1 | 读请求,即下载 |
2 | 写请求,即上传 |
3 | 表示数据包,即DATA |
4 | 确认码,即ACK |
5 | 错误 |
2. TFTP下载过程
TFTP服务器默认监听69号端口
当客户端发送“下载”请求(即读请求)时,需要向服务器的69端口发送服务器若批准此请求,则使用一个新的、临时的 端口进行数据传输
因为udp的数据包不安全,即发送方发送是否成功不能确定,所以TFTP协议中规定,为了让服务器知道客户端已经接收到了刚刚发送的那个数据包,所以当客户端接收到一个数据包的时候需要向服务器进行发送确认信息,即发送收到了,这样的包成为ACK(应答包)
为了标记数据已经发送完毕,所以规定,当客户端接收到的数据小516(2字节操作码+2个字节的序号+512字节数据)时,就意味着服务器发送完毕了
下载代码如下:
import socket
import struct
def main():
myFile=open('xx.avi','wb')
udpSocket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
destAddr = ('192.168.11.67',69)
date = struct.pack('!H7sb5sb',1,b'xxx.avi',0,b'octet',0)
udpSocket.sendto(date,destAddr) #发送请求
num = 0
while True:
recvDate,recvAddr =udpSocket.recvfrom(1024)
operNum = struct.unpack('!H',recvDate[:2])[0]
if operNum == 3:
myFile.write(recvDate[4:])
#发送ACK
blockNum = struct.unpack('!H',recvDate[2:4])[0]
print(blockNum)
num = num + 1
if num == 65536:
num = 0
if num == blockNum:
num = blockNum
ack = struct.pack('!HH',4,blockNum)
udpSocket.sendto(ack,recvAddr)
if len(recvDate)<516:
break
if operNum == 5 :
break
myFile.close()
if __name__ == '__main__':
main()
用num进行判断,避免因为文件过大导致发送ACK时,块编号超过两个字节,而出现异常
上传代码如下
import socket
import struct
def main():
#以读字节的方式打开文件
myFile=open('xx.avi','rb')
#创建socket UDP对象
udpSocket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
destAddr = ('192.168.11.67',69)
#组成上传文件请求包
date = struct.pack('!H5sb5sb',2,b'x.avi',0,b'octet',0)
#发送上传文件请求包
udpSocket.sendto(date,destAddr)
#数据包编号
blockNum = 0
while True:
recvDate, recvAddr = udpSocket.recvfrom(1024)
recvNum = struct.unpack('!H', recvDate[2:4])[0]
print('recvNum:%s' % recvNum)
date1 = myFile.read(512)
# 判断是否有发送失败的情况
if blockNum == recvNum:
#发送数据包
blockNum += 1
if blockNum == 65536:
blockNum = 0
blockDate1 = struct.pack('!HH%ss'%len(date1),3,blockNum,date1)
print('blockNum:%s' % blockNum)
udpSocket.sendto(blockDate1, recvAddr)
if len(date1)<512:
break
myFile.close()
if __name__ == '__main__':
main()