boost::asio:发送到套接字的数据顺序

3

boost::asio

库中发送数据到套接字的顺序是否有保障呢?

也就是说,我多次调用

boost::asio::async_write(socket, buffer, completionHandler)

我发现客户端似乎并没有接收到我发送的数据,出现了一些奇怪的行为,因此我希望确保这是我所期望的。请注意,我在每次写操作之间不等待完成处理程序,而是触发一堆异步写入调用,并天真地期望数据按照相同的顺序写入套接字。

Asio中的strand文档说:

当与连接关联的异步操作链条只有一个时(例如在半双工协议实现(如HTTP)中),处理程序不可能并发执行。这是一个隐式线程。

我理解这意味着使用strand不会改变任何内容,因为套接字提供了一个隐式线程。

总体上,strand文档讨论了事件处理程序的严格顺序调用。然而,我不清楚写入连接的数据是否按照我调用async_write的顺序写入。

写入套接字的数据顺序是否有保证?


这取决于底层协议。你是使用TCP还是UDP?如果您使用的是TCP,则调用async_write的顺序将是在另一端接收数据的顺序。如果数据(使用TCP套接字)被损坏,则在其他地方存在另外一个问题。您正在发送什么类型的数据?如何接收它?如何检查其有效性? - Some programmer dude
另外,如果您使用TCP,请记住它是一种流式协议,没有固定大小的数据包或消息边界。接收调用可能会给您比您期望的更少,或者多个“消息”。您需要自己处理将其拆分为单独的消息或数据包。 - Some programmer dude
另一方面,如果您使用UDP,则可能会丢失数据包或以错误的顺序接收。 - Some programmer dude
@Someprogrammerdude,是的,抱歉,我应该澄清一下。TCP,我只是在发送数据时将其转储到文件中,然后在接收数据时将其转储出来。我有套接字编程经验,但我正在尝试找出为什么使用asio时我似乎没有收到我发送的内容的解释,而我对此不太熟悉。我想要(双重)确认asio是否保证按照我调用async_write的顺序将数据发送到连接。 - Tom Quarendon
2个回答

6
在进行另一个异步写入操作时调用async_write是不允许的。这一点无论您是使用一个线程还是多个线程都是正确的。引用自asio文档

此操作以零个或多个对流的async_write_some函数的调用为基础实现,并称为组合操作。程序必须确保在此操作完成之前,流不执行其他写操作(例如async_write、流的async_write_some函数或执行写入的任何其他组合操作)。

编辑:解决方法是维护一个字节缓冲区以进行写入,并在已存在写入操作时添加到该缓冲区,而不是调用async_write。当当前写入完成后,请再次使用该缓冲区调用async_write
编辑2:答案此问题中有一个如何实现的示例。它使用strands,因此可在多线程应用程序中工作(但如果您的应用程序是单线程的,则会比必要复杂一些)。
编辑3:最后澄清一下。使用strands是没有帮助的,因为strands保证您的代码(对于该strands)只在一个线程中运行,但不保证同时只有一个操作发生。换句话说,如果您在处理程序中调用了async_write,那么在该处理程序返回时,即使async_write仍在进展中,也可以在该strands中调用更多的内容。您需要手动存储一些状态,指示您已经启动了async_write并在完成处理程序中更新该状态(这是关键部分)。

是的,我得出结论,多次调用async_write而不等待响应将是一个坏主意。如果什么都没有,我不会在执行下一个写操作之前检查一个写操作是否成功。在这种情况下,我不确定异步写入是否比阻塞同步写入更有优势。 - Tom Quarendon
回想起来,我编程时的思路有点像我编写 zeroMQ 时的方式,我知道我只需发送数据并让它们异步发生。我不想维护自己的发送队列,因此同步执行写操作是最简单的解决方案。 - Tom Quarendon
我认为如果你可以使用同步调用,那么这没有任何问题。 - Arthur Tacca

0

你没有提到你的线程情况,有多少个线程同时发送数据?有多少个线程运行 io_service.run()?我不知道 Asio 是否对写入套接字进行任何同步,我认为它不会,因为套接字写入已经是线程安全的。

我建议检查你的数据缓冲区(发送到套接字的缓冲区)或数据接收逻辑中是否存在数据竞争。

此外,TCP 流式传输的特性(正如 @Some_programmer_dude 在评论中提到的)是常见的混淆源。


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