rdt2.2协议UDP socket传输Cat.bmp时,发送端recv()不触发的故障排查求助
异常定位排查步骤
- 首先核查接收数据的变量作用域:这是匹配你描述现象的最高发问题。如果把
socket.recv()的调用写在了主循环外侧,第一次读取拿到第一块数据后,后续循环根本不会触发新的socket读操作,只会反复解析内存中存的第一次数据块,完全符合“持续重复读取同一块已接收数据”的表现。 - 其次核查服务端的状态跳转逻辑:RDT2.2是停等协议,必须维护当前等待的交替SEQ(0/1)值。如果收到SEQ匹配、MD5校验通过的合法数据包后,没有把等待的SEQ值翻转、没有跳出当前包的处理分支,就会一直停留在解析第一块数据的逻辑里,不会拉取后续新到的网络数据。
- 再核查ACK的发送逻辑:如果服务端处理完第一块合法数据后,没有正确发送对应SEQ的ACK给客户端,客户端就会一直停在等待ACK的状态,既不会读取下一块Cat.bmp的数据发送,也不会因为超时重传触发收包逻辑,表现出来就是客户端的
socket.recv()一直没有返回。 - 最后核查接收缓存的复用逻辑:如果在循环中没有每次接收前重置缓存内容,反复在同一个固定长度的字节缓存上做解析,会导致socket内核缓冲区的新数据根本没被读到应用层,每次解析的都是上一次残留的第一块数据。
对应修复方案
- 调整
socket.recv()的位置:必须把接收新数据的逻辑放在循环内的待收新包分支里,不能放在循环外侧。错误写法是先在循环外调用一次recv拿到数据,再进入循环反复解析;正确逻辑是每次进入“等待新数据包”的状态时,才调用recv从socket拉取新的网络数据。 - 完善SEQ状态维护:服务端必须初始化当前期望接收的SEQ为0,每收到一个合法的、SEQ匹配的数据包,完成文件写入、ACK发送后,立刻把期望SEQ翻转成
1 - expected_seq,作为下一次的匹配值;如果数据包校验失败、或者SEQ不匹配,直接重发上一次已经确认过的合法数据包对应的ACK,不更新期望SEQ值。 - 修正客户端的停等逻辑:客户端每发送一块带SEQ的文件数据后,必须在超时窗口内等待服务端返回的对应SEQ的ACK,收到正确ACK后,再把文件读取指针向后偏移对应长度,读取下一块Cat.bmp的数据封装成新的交替SEQ的数据包发送,不能把第一块文件数据存在缓存里反复重发。
- 清理接收缓存的残留:每次准备接收新数据包前,把应用层的接收缓存重置为b'',不要用同一个固定缓存反复拼接解析,避免读到上一次的残留数据。
常见错误代码片段参考
# 错误写法:recv在循环外,反复解析第一次拿到的数据 recv_data = conn.recv(BUFFER_SIZE) while True: seq, data, md5 = unpack(recv_data) if verify_md5(seq, data, md5): f.write(data) conn.send(make_ack(seq)) # 正确写法:recv放在循环内的状态分支 expected_seq = 0 while True: recv_data = conn.recv(BUFFER_SIZE) seq, data, md5 = unpack(recv_data) if verify_md5(seq, data, md5) and seq == expected_seq: f.write(data) conn.send(make_ack(seq)) expected_seq = 1 - expected_seq else: conn.send(make_ack(1 - expected_seq))
内容的提问来源于stack exchange,提问作者KPurcell10
相关产品推荐
相关产品推荐

