3

Is the order that data is sent to a socket with boost::asio guaranteed at all?

That is, I'm making multiple calls to

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

and I'm seeing some odd behaviour where the client isn't apparently receiving the data I think I'm sending, so I'm wanting to make sure that this is doing what I hope it's doing. Note that I'm not waiting for the completion handler to be called between each write, I'm just firing a bunch of async_write calls and naively expecting the data to be written to the socket in the same order.

The strand documentation in asio says:

Where there is a single chain of asynchronous operations associated with a connection (e.g. in a half duplex protocol implementation like HTTP) there is no possibility of concurrent execution of the handlers. This is an implicit strand.

I take this to mean that using a strand won't change anything, as the socket provides an implicit strand.

The strand documentation in general talks about strictly sequential invocation of event handlers. However it's not clear to me whether the data written to the connection will be written in the order in which I make calls to async_write.

Is the order that data is written to the socket guaranteed?

frlan
  • 6,950
  • 3
  • 31
  • 72
Tom Quarendon
  • 5,625
  • 5
  • 23
  • 30
  • It depends on the underlying protocol. Are you using TCP or UDP? If you're using TCP the order you call `async_write` will be the order in which the data is received on the other end. If the data is corrupted (using TCP sockets) then there is another problem somewhere else. What kind of data are you sending? How are you receiving it? How do you check for its validity? – Some programmer dude Aug 22 '17 at 09:33
  • Also, if you use TCP then remember that it's a *streaming* protocol, with no fixed-sized packets or message boundaries. A receive call may give you *less* than you expect, or more than one "message". You need to handle the splitting into separate messages or packets yourself. – Some programmer dude Aug 22 '17 at 09:34
  • On the other hand, if you use UDP, then it's possible that packets are lost, or are received in the wrong order. – Some programmer dude Aug 22 '17 at 09:37
  • @Someprogrammerdude Yes, sorry, should have clarified that. TCP, and I'm just dumping out the data to a file as I send it then dumping out the data as I receive it. I'm experienced with socket programming, but I'm just trying to find explanations for why I don't apparently receive what I send when using asio, which I'm less familiar with. I was wanting to (double) check that asio guarantees that data is sent to the connection in the order I call async_write – Tom Quarendon Aug 22 '17 at 09:59

2 Answers2

6

Making a call to async_write while another one is in progress is not allowed. This is true regardless of whether you are using one thread or multiple threads. From the asio docs:

This operation is implemented in terms of zero or more calls to the stream's async_write_some function, and is known as a composed operation. The program must ensure that the stream performs no other write operations (such as async_write, the stream's async_write_some function, or any other composed operations that perform writes) until this operation completes.

Edit: The workaround for this is to maintain a buffer of bytes to write, and add to that instead of calling async_write when a write is already in progress. When the current write completes, call async_write again with that buffer.

Edit 2: The answer to this question has an example of how do this. It uses strands so would work in a multi-threaded application (but is a bit more complex than is necessary if your application is single threaded).

Edit 3: One final clarification. Using a strand wouldn't help because a strand guarantees that your code (for that strand) is only running in one thread at once, but doesn't guarantee that only one operation is happening at once. In other words, if you call async_write in a handler, then as soon as that handler returns more stuff can be called in that strand, even if the async_write is still in progress. You need to manually store some state indicating that you've started an async_write and (here is the key part) update that state in the completion handler that you passed as a parameter to async_write.

Arthur Tacca
  • 8,833
  • 2
  • 31
  • 49
  • Yes, I'd come to the conclusion that calling async_write multiple times without waiting for a response was going to be a bad idea. If nothing else, I don't check that one write succeeds before doing the next one. In the scenario I'm not totally sure that an async write is an advantage over a blocking sync write – Tom Quarendon Aug 22 '17 at 14:58
  • In retrospect, I'm programming it rather like I'd program zeroMQ, where I know I can just fire the sends off and they occur asynchronously. I don't really want to have to maintain my own queue of things to send, so performing the writes synchronously is by far the *simplest* solution. – Tom Quarendon Aug 22 '17 at 15:08
  • I think if you can get away with synchronous calls, there's nothing wrong with that. – Arthur Tacca Aug 22 '17 at 15:13
0

you didn't say anything about your threading, how many threads concurrently send data? how many threads do io_service.run()? I don't know if Asio does any synchronisation on writing to a socket, I think it doesn't as socket writing is already thread-safe.

I'd recommend to check for data races on your data buffers (buffers that are sent to the socket) or your data receiving logic.

Also TCP streaming nature (as @Some_programmer_dude mentioned in comments) is a common source of confusion.

Andriy Tylychko
  • 15,967
  • 6
  • 64
  • 112