可靠UDP-停等机制
声明:以下内容包括代码均为个人记录分享学习历程
PS:此专栏评论区用不了
利用数据报套接字在用户空间实现面向连接的可靠数据传输,功能包括:建立连接、差错检测、确认重传等。流量控制采用停等机制,完成给定测试文件的传输。
给出协议设计:


给出状态转移图:


停等机制简述:
函数声明:封装了两个函数stopAndWaitSend和stopAndWaitRecv,分别用于确认重传和停等机制。
确认重传:在stopAndWaitSend中维护了计时器和重发计数器,自原始包发出后开始尝试捕获ACK并开启计时,此步骤需要接受有效ACK,会检查ACK包的有效位以及是否是期望的ack序号,每超时一次重发一个包并重置计时器,若超过最大重发次数则发送失败。
停等机制:stopAndWaitRecv主要进行包体的校验和校验(这要使用差错检验的内容)和以及有效位检验,未能通过检查的包将不予回复ACK,因为此函数是被动回复,因此必须等对方发送一条消息我方才能发一条消息,这就实现了停等机制。
三次握手 四次挥手:
此次实现仅需要单向传输文件,因此无需传统的三次握手四次挥手,仅需两次握手实现建立连接两次挥手实现断开连接。
文件发送:
通过维护的全局数组(发送缓存区)储存要发送的文件,全局数组的第二维大小必须等于包体数据段最大长度。根据数组第一维非空的大小确定报文头部counts字段大小,根据每一个包数据段不为空的部分大小确定报文头部totalLen字段大小。文件传输正式开始前,要先发送一个STATE_STR包,告知server即将开始传输的文件的大小、名称、总分包数目等信息,待收到ACK后开始发包。当文件仅剩最后一个包时,这个包要将STATE_END置位,告知server文件传输完毕,此时需等待server保存文件内容完毕才能继续操作
文件接收:
通过维护的全局数组(接收缓存区)储存接收到的文件,server的缓存区应当和client缓存区大小一致以避免不必要的麻烦。自收到带有STATE_STR的包起,server将连续接收count+1个报文,并读取其数据段,同时每接收到一个报文就必须回传一个ACK报文。当最后一个报文接收完毕后,检查最后一个报文是否是STATE_END报文,如果是则代表文件无误(校验和和有效性在停等函数中实现),将进行文件储存。文件储存即从第一个报文开始将其数据段读取出来并填充在目标路径处,每次读取的字节数为该包头部totalLen的值,共计读取count+1个包。值得注意的是,文件储存会阻塞代码,需手动输入有效路径(文件完整名)后才会继续执行,否则server将不会响应报文缓冲队列中的报文。
套接字初始化:
① 协商socket库版本
② 创建一个数据报套接字
③ 初始化两个结构体SOCKADDR_IN ,储存源主机和目标主机地址信息
④ 更改套接字配置为非阻塞,设置timeout时间
⑤ 为套接字bind一个SOCKADDR(client不需要 会自动分配port)
运行细节补充:
根据主机当前状态以及收到的报文的flags段情况来决定下一步操作。
客户端:
① OFF状态下输入路径后将主动发起SYN请求
② 第一次进入ON状态将自动发送STATE_STR包,开始传输文件
③ 再次进入ON状态将等待用户输入新路径
④ 理论上而言,client全程只需要接收ACK报文即可,并在超时时重传
⑤ 传输过一次文件后才能选择发送FIN请求
服务器:
① OFF状态下被动等待SYN请求,并回复ACK
② ON状态下等待STATE_STR包,保存文件信息,调用停等接收函数
③ SENDING状态下持续接收并检查数据包,对合法包进行ACK回复
④ 传输完毕后阻塞,等待用户输入保存路径
⑤ 再次回到ON状态,等待下一个STATE_STR包,
⑥ 只有在第二次及以后进入ON状态才能响应FIN包(因为client只有在传输过一次文件后才会发出FIN包),此时可以选择退出程序
有效位检查:
在报文的头部有flags字段,此字段宏定义了FLAG_EXIST,我们每次收到报文都要检查此标记,以免收到一些干扰报文,可能会导致接收文件时纳入一些无关报文数据,同时还能排除一些不满足协议的外界报文干扰主机状态。
其余前置知识:差错检验略
代码:见 https://github.com/BluesPizza/ComputerNetworks