停止等待文件传输协议,什么时候停止监听?

3

我正在使用C++套接字(Socket)通过UDP协议(SOCK_DGRAM)开发一个客户端和服务器文件传输应用程序。对于PUT方法,我在握手后进行以下操作:

客户端

Send a packet;
if time_out
    send the packet again;
else  //ack received
    send a new packet;

服务器

wait for the first packet;
send acknowledge;
while(!EOF)
    Get a packet;
    Send an ack;

当EOF基本上检查我是否通过与握手期间发送的文件大小进行比较来接收整个文件时。

在此交换过程中,数据包可能会在任何时候丢失。客户端可以发送一个数据包,但该数据包会丢失。它会超时等待ACK并重新发送数据包。服务器接收到数据包并发送ACK。该ACK可能会丢失,在这种情况下,客户端会超时并重新发送其数据包。由于这些数据包具有编号序列,因此我们可以忽略已多次接收的数据包。

我的困惑出现在文件传输结束时。客户端刚刚发送了包含文件最后一位的数据包。服务器成功接收并发送了ACK。但是该数据包丢失了。服务器应用程序现在已经退出while循环,因为他已经将最后的数据写入文件,并且从他的角度来看,文件传输已成功。但是,客户端从未收到ACK,因此超时并重新发送数据包。在这里应该发生什么?服务器没有监听或正在监听新请求,而不是数据。客户端或服务器在哪个时点应该考虑传输完成并停止尝试通信?

2个回答

3
似乎你正在重新发明TFTP,但是客户端和服务器角色颠倒了。你确定需要这样做吗?由于缺乏滑动窗口或数据管道,这是一个非常低效的协议。
我看到你的难题有3种解决方案:
文件传输结束时进行三次握手。服务器在接收到最后一个数据块后,不仅会发送 ACK,而且还会等待客户端 ACK 该 ACK。当发生这种情况时,服务器知道客户端知道文件传输已完成。如果服务器从未收到 ACK 的 ACK,则它会定期重新发送其对最后一个数据块的 ACK。
服务器在文件传输完成后的几分钟内(或几个往返时间,以较大者为准)记住已完成的文件传输。如果在此期间,它从一个客户端接收到任何已经完成的文件传输的数据块,服务器会仅仅 ACK 它而不做任何其他操作,并重置该文件传输的结束计时器。这更加复杂,但优于先前的解决方案,因为服务器能够在文件传输完成后继续进行其他工作。
如果服务器收到任何不属于活动文件传输的数据块,它会响应一个错误(NAK)。无论是不存在、无效还是已完成的传输,都是如此。这是最简单的实现方法,但也是最丑陋的,因为客户端必须在收到 NAK 后以错误终止。但也许这没关系,因为从服务器的角度来看,文件传输仍然成功了。

1
我认为对于你的第一个解决方案,我会遇到同样的问题 将军问题。无论数据包是否丢失,客户端和服务器都会交替扮演角色,但永远不知道对方是否收到。对于你的第二个解决方案,那正是我想做的,但现在我必须记住序列号或某种会话 ID,以便知道数据包是否属于当前会话。即使在不合理的情况下,如超过 60% 的丢包率,这仍然无法阻止客户端继续发送。我认为第三个解决方案是最好的。 - Sotirios Delimanolis
对于第一个解决方案,你关于无限回归的观点是完全正确的。很好的观点。在实践中,我猜想如果客户端已经离开,服务器会在长时间超时后放弃。至于第二个解决方案,你说得对,需要更多的状态。我不确定,但我认为TFTP PUT操作的工作方式与那个解决方案有些相似。 - Celada

1

显而易见的解决方案是不要阻止服务器立即监听。我会让服务器不断发送一个特殊数据包,其值表示文件结束,直到从客户端收到确认为止。

您是否考虑过使用已经实现此类UDP文件传输的库,例如Raknet?这可能会让您的生活变得更轻松,因为您正在重新发明轮子。


这是为了练习,理解确认交互背后的原理。 - Sotirios Delimanolis
好的,我很欣赏这一点。了解高级系统在底层是如何工作的总是很有益的。 - Alex Z

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接