我正在使用C ++开发自己的FTP客户端,但我遇到了函数 recv()。当我使用recv()获取数据时,它们可能不完整,因为我使用 TCP 协议,因此我必须在循环中使用recv。问题是,当我收到应该收到的所有内容后收到服务器阻止,然后我的程序卡住了。 我不知道我要接收多少字节,所以我无法控制它并在完成后停止它。我现在发现了两个不太优雅的解决方案:
问题是,对此有什么干净而优雅的解决方案吗?
答案 0 :(得分:3)
显而易见的事情是提前传输待接收消息的长度(许多协议,包括例如HTTP这样做,以解决完全相同的问题)。这样,你知道当你收到金额X时,就不会再有了。
这将在99.9%的情况下正常工作,并且在0.1%的服务器对你撒谎或服务器意外崩溃或有人偶然发现网络电缆(或类似情况发生)的情况下会发生灾难性故障。可悲的是,"连接"由TCP建立是一种幻觉,你没有多少手段来检测连接何时死亡。另一端可以关闭,你不会注意到任何事情,除非你试图发送并收到错误(或者直到几个小时之后)。
因此,您还需要一种备用策略,以确定事情不会像预期的那样好。您可以使用select
或poll
来了解数据何时可用,因此您不会永远阻止永远不会发出的消息。
使用线程解决块到端问题(如其他答案中所提出的)不是一个很好的选择,因为阻塞不是实际的问题。实际问题是你不知道什么时候到达传输结束。在传输结束时有一个工作线程阻塞将工作"但是会使工作线程无限期地被阻塞,消耗资源并且具有不确定的,依赖于系统的命运。
在退出之前你不能join
线程,因为它被阻止了(因此尝试join
它会使你的主线程死锁)。当您的进程退出并且套接字关闭时,该线程将解除阻塞,但是(至少在某些操作系统,例如Windows上)将立即终止。这可能不会造成太大的危害,但是以不受控制的方式终止线程总是比让它正确退出更不可取。在其他操作系统上,您可能还有剩余的线程。
答案 1 :(得分:2)
由于您使用的是C ++,因此与库存C相比,有一些替代库可以极大地简化网络编程。我个人最喜欢的是Boost::Asio,但其他人可用。这些库不仅可以节省您在C中编码的麻烦,还可以提供异步功能来解决阻塞问题。
答案 2 :(得分:1)
典型方法是使用select()
/ pselect()
或poll()
/ ppoll()
。两者都允许指定超时,以便在没有传入数据时退出。
然而,我不知道你应该如何“应该收到所有应该收到的recv”。当没有网络问题时,依赖超时也是非常低效的......
或者您发送数据的大小,数据之前,您读取的数据,或数据连接是否以EOF终止。在这种情况下,read()将返回-1并退出。
答案 3 :(得分:0)
我可以想到两个选项,不需要对现有代码进行重大改写,第三个选项更为激进:
答案 4 :(得分:0)
你可以将recv函数放在它自己的线程中,并在另一个线程中进行处理。