参照 XJTU计网实验 要求完成
基于 UDP 的 GBN + SR 可靠文件传输协议:
- 支持公网上双向传输(客户端可以上传文件到服务端,也可以从服务端下载文件);
- 支持所有文件类型任(文本,图片,pdf,视频等);
- 支持通过命令行参数来调整窗口长度N和超时时间TO;
- 支持丢包率,传输速率等参数统计。
本报文的协议头主要由六部分组成:
- 报文序号(seqnum):8位整型,代表该报文的序号
- 确认序号(acknum):8位整型,代表要确认的报文的序号。
- 窗口大小(win):3位整型,代表最多可以发送的未确认的报文的数量。
- 标志位:SR为1代表使用选择重传机制;GBN为1代表使用退回N帧协议;ACK为1代表该报文的应答报文序号(acknum)有效;FIN为1代表该报文为文件传输结束报文。
- 数据长度(Len): 4位整型,代表数据的长度。
- 重传时间(TO):4位整型,单位毫秒。
- 数据部分(data): 协议头之后位数据部分,即要传输的文件的内容(以比特形式存储)。
该协议是UDP的上层协议,因此外层用UDP报文封装。
当前ip均为localhost,需要自行更改来绑定自己的ip。 更改传输的文件时,需要相应的改变程序中的文件列表的初始值。
- 初始化发送窗口和确认窗口。发送窗口和确认窗口的大小为N,N为窗口的大小。
- 发送方等待上层应用程序传来待发送的数据,将待发送的数据放入发送窗口,并将发送窗口内的数据依次发送出去。
- 发送方同时开启一个定时器,等待确认。
- 接收到对应的确认消息后,发送方将对应的确认消息标记为已确认,将发送窗口向前移动一位,然后将新的一位添加到发送窗口的末尾,重复步骤2。
- 如果超时未收到对应的确认消息,则发送方重发发送窗口内的所有未确认的消息,重复步骤3。
- 初始化接收窗口和期望收到的序号。接收窗口和期望收到的序号的大小为N,N为窗口的大小。
- 接收方不断等待接收到数据。当接收到数据时,将其与期望收到的序号比较,如果相同则将数据发送到上层应用程序,并向发送方发送确认消息;如果不同则向发送方发送期望收到的序号,等待发送方重发。
- 接收方等待确认消息。如果接收到了对应的确认消息,则将期望收到的序号加1,重复步骤2;如果未接收到确认消息,则等待超时并重复步骤2。
- 初始化:窗口大小 N,初始为 1,初始序号 base = 0,下一待发送序号 next_seqnum = 0。对于每个分组,添加一个计时器,超时时间为定值,用于重传从上层获取数据,放入发送缓冲区,等待发送 将窗口内的第一个分组发送,计时器启动。
- 发送:发送窗口内的所有未确认分组,等待 ACK 应答,等待 ACK 应答的过程中,如果出现超时,则重传窗口内所有未确认分组,并重新计时
- 接收 ACK 应答:当收到一个 ACK 应答时,检查它的序号。如果序号小于 base,则丢弃 ACK 应答;如果序号在窗口内,则更新计时器,将分组标记为已确认;如果序号等于 base,则滑动窗口,更新 base 和下一待发送序号 next_seqnum,并启动新的计时器;如果序号大于 base,则说明窗口之外的分组已经被确认,直接更新缓存。
- 初始化:窗口大小 N,初始为 1,初始期望收到的序号为 0 初始化接收缓冲区和发送 ACK 序号。
- 接收数据:当收到一个数据包时,检查它的序号,如果序号小于期望收到的序号,则丢弃数据包;如果序号等于期望收到的序号,则将数据包保存到接收缓冲区,将期望收到的序号增加1;如果序号大于期望收到的序号,则保存到缓存,发送 ACK 应答。
- 发送 ACK 应答:发送缓存中未发送的 ACK 应答,如果缓存中的 ACK 应答数量达到窗口大小 N 或者达到序号上限,则发送一次 ACK 应答。
使用python中的open函数以比特的格式读取文件,再根据设置的数据长度将其封装成一个类的实例,该类的成员包括上述协议的协议头部分和data。再将这些实例依此加入packets列表。列表中的每一个元组即为要发送的报文的数据结构,在发送时再将它们转为字符后编码为比特。
用户输入所用协议类型(GBN或SR),窗口大小等参数,然后选择模式(上传文件或下载文件),再选择要上传或下载文件的文件名。上传和下载的过程类似,下面以客户端下载文件为例介绍。 服务器接收到用户的请求后,读取文件并将文件数据打包成多个packets。随后,服务端开始传输,同时接收客户端发送的ACK报文。根据不同的协议选择,服务端依此发送packets列表中的报文,并依据客户端的应答报文的ACK序号更新窗口。完成传输后,服务端会发送一个FIN=1的报文,客户端接收到后会结束文件写入。然后客户端可以继续选择模式(上传或下载)并重复上述的收发过程。