TFTP 的基本理论
目录
TFTP(Trivial File Transfer Protocol,简单文件传输协议)是UDP协议族中的一个用来在客户机与服务器之间进行简单文件传输的协议,提供不复杂、开销不大的文件传输服务。端口号为69,基于RFC1350协议。
1 通信流程 / Communication Flow
TFTP协议主要基于UDP进行,传输过程主要如下:
客户端从服务器下载: 客户端向服务器上传:
Client----RRQ---->Server Client----WRQ---->Server
<----DATA---- block = 1 <----ACK---- block = 0
----ACK----> ----DATA----> block = 1
…… <----ACK----
……
其中有以下几点注意事项:
- Client向Server的69端口发送RRQ或者WRQ;
- 每个数据报文都是一个完整的block size,512bytes,发送不完整的数据包代表传输结束,若恰好为整数倍,则还需发送一个0bytes的数据报文
- 发送方在一定时间内没收到ACK,则需重新发送数据。
2 数据报文格式 / Data Message Format
主要包括5种数据报文格式,序号即是其opcode:
- RRQ 2. WRQ 3. DATA 4. ACK 5. ERROR
2.1 RRQ / WRQ packet
2 bytes string 1 byte string 1 byte
----------------------------------------------------------------
| Opcode | Filename | 0 | Mode | 0 |
----------------------------------------------------------------
其中filename为长度不定的字符串,mode的前后均有一个比特的0作为分隔符,mode为netascii、octet或者mail之一,大小写不敏感。
netascii – ascii码传输,需要发送方和接收方自己解码ascii码;
octet – 文件传输,字节模式,较为常用
mail – 邮件传输,基本不用
2.2 DATA packet
2 bytes 2 bytes n bytes
--------------------------------------------
| Opcode | Block | Data |
--------------------------------------------
数据报文格式中,Block为数据报文的序号,因为只有2个字节,所以是1-65535。当 data block是512 byte时,能接受的文件大小是512*65536=32MB,不过后面的tftp修订允许data block达到1468 byte(以太网MTU(1500)-TFTP header(4)-UDP header(8)-IP header(20)),那么能够传输的大小是1468*65535=93M。不过如果tftp实现循环利用序列号的话(65535的下一个序号是0),理论上可以传输的文件大小没有限制。
2.3 ACK packet
2 bytes 2 bytes
------------------------------
| Opcode | Block |
------------------------------
ACK报文中,Block是应答的数据报序列号,WQR的ACK报文Block序列号为0。所有的报文都有ACK报文作为应答,除了,
- 重复的ACK
- 结束报文
- 报文传送超时
2.4 ERROR packet
2 bytes 2bytes string 1 byte
------------------------------------------------------------
| Opcode | ErrorCode | ErrMsg | 0 |
------------------------------------------------------------
错误报文的格式如上所示,Opcode是5,ErrorCode是错误代码,ErrMsg是错误信息,结尾以一个比特的0作为分隔符。
2.5 加解码packet
加解码的方式可按照下面的示例代码完成,其中用到了 struct 模块
import struct
RRQ = 1
WRQ = 2
DATA = 3
ACK = 4
ERROR = 5 class TFTPReadWriteRequest():
@staticmethod
def encode(op, file_name, mode='octet'):
opcode_select = {'r': RRQ, 'w': WRQ}
opcode = opcode_select[op]
packet = struct.pack('!H%dsb%dsb' % (len(file_name), len(mode)), opcode, file_name.encode(), 0, mode.encode(), 0)
return packet @staticmethod
def decode(msg_bytes):
opcode = struct.unpack('!H', msg_bytes[:2])[0]
fileName = msg_bytes[2:].decode('utf-8').split('\x00')[0]
mode = msg_bytes[2:].decode('utf-8').split('\x00')[1]
print('=== Transfer mode is %s' % mode)
return opcode, fileName class TFTPData():
@staticmethod
def encode(block_number, data):
packet = b'\x00\x03'
packet += struct.pack('!H', block_number)
packet += data
return packet @staticmethod
def decode(data_msg):
opcode = struct.unpack('!HH', data_msg[:4])[0]
block_num = struct.unpack('!HH', data_msg[:4])[1]
data = data_msg[4:]
return opcode, block_num, data class TFTPAck():
@staticmethod
def encode(block_num):
packet = b''
packet += b'\x00\x04'
packet += struct.pack('!H', block_num)
return packet @staticmethod
def decode(msg_bytes):
opcode = struct.unpack('!HH', msg_bytes)[0]
ack_block = struct.unpack('!HH', msg_bytes)[1]
return opcode,ack_block class ErrorOpcode(Exception):
pass
3 传输终结 / Transmission End
传输终结的标志是传输数据小于512bytes,当接收方收到最后一个数据报文时,仍然需要发送ACK应答(对于接收端意义不大,但发送端需进行确认),在发送完ACK应答后,还需要等待一段时间(默认的超时时间),如果还是收到了发送端发送的最后一个数据报文,说明发送端没收到ACK报文,需要再次发送,直到在等待时间里没有收到数据报文,或者超过了默认的最大重发次数。
4 异常处理 / Error Handle
当发送过程中出现错误或者请求无法满足,就会发送ERROR报文,ERROR报文是发完就不管的,没有重发机制,需要接收方借助超时机制来检测错误。错误代码如下:
0 – Not defined, see error message (if any)
1 – File not found
2 – Access violation
3 – Disk full or allocation exceeded
4 – Illegal TFTP operation
5 – Unknown transfer ID
6 – File already exists
7 – No such user
5 数据丢失和超时 / Data Loss and Time out
TFTP没有提供报文的错误检查机制,即认为所有接收的报文都是正确的,数据丢失和对端主机服务无法访问等错误需要借助超时机制来检测,在每次发送一个报文的时候,需要设置一个计时器,如果在计时器超时的时候还没有收到下一个数据,则重发上次的报文。如果计时器超时次数超过了最大值,则终止程序。当接收到新的报文时,不管报文正确还是重复,都要重置计时器。
相关阅读
1. struct 模块
参考链接
http://cizixs.com/2015/04/12/write-tftp-with-python-1