Boost :: asio :: streambuf :: consume - enter garbage symbol

When I lose the connection, in my server code, I try to connect again in a loop. As soon as I reconnect, I will send a message to enter the component to which I am connected. This component then sends a response to the input, which looks like "MyResponse"

The original connection is working fine. However, after I reconnected, I get garbage before the expected message, which looks like this: "ýMyResponse"

Then go to Google. I see many questions about the stack overflow about boost :: asio :: streambuf, which is used for async sockets in boost :: asio. In particular, about the reuse of its buffer. I followed the advice there and called them consumption when disconnected. In other words, I call boost :: asio :: streambuf :: consume after I call shutdown and close my socket, after it was called with an error in recv in response to the recv_until call.

I also used wirehark to make sure the garbage symbol is not sent, and that is not the case.

After much debugging, it seems that calls to consume enter a character, rather than clearing all characters.

Here is a minimal example:

#include <boost/asio.hpp> #include <iostream> #include <sstream> #include <string> int main() { boost::asio::streambuf buffer; std::cout << "buffer size " << buffer.size() << std::endl; buffer.consume(std::numeric_limits<size_t>::max()); std::cout << "buffer size " << buffer.size() << std::endl; std::istream is(&buffer); std::string contents; is >> contents; std::cout << "Contents: " << contents << std::endl; std::cout << "buffer size " << buffer.size() << std::endl; return 0; } 

Conclusion:

 buffer size 0 buffer size 1 Contents: ² buffer size 0 

Expected Result:

 buffer size 0 buffer size 0 Contents: buffer size 0 

If I do not use consumption, I get some message, previously disconnected, before the first message after reconnecting, in my server code.

If I use consumption, I get a garbage character.

Cm:

Work with boost :: asio :: streambuf

Read to the line separator in boost :: asio :: streambuf

boost asio async_read: a read message adds to itself

+1
source share
2 answers

boost :: asio :: streambuf :: consumes overflow.

Use

 buffer.consume(buffer.consume(buffer.size()); 

Instead

 buffer.consume(std::numeric_limits<size_t>::max()); 

Report a bug to increase the mailing list.

+1
source

The ý symbol indicates a bunch of debug memory pickups :

enter image description here

This means that streambuf pointers are corrupted.

I do not have a complete picture for your program, but I notice that you are not correctly synchronizing the read / write on the socket.

Dispatch side

For example, looking at MyClass::Send , it joins the existing m_sendBuffer , but sends it again using async_write . it

  • redundant since everything was already in m_sendBuffer , which would mean that async_write already running on it
  • undefined because the docs say:

    This operation is performed in terms of zero or more calls to the async_write_some stream async_write_some and is called a compound operation. The program must ensure that the stream does not perform any other write operations (such as async_write , the stream function async_write_some or any other composed write operations) until this operation completes.

  • data race: m_sendBuffer changes when a previous write operation can be performed

The usual fix approach is to have a queue of outgoing buffers, not one, and send them sequentially, for example. boost asio async_write: how not to alternate async_write calls?

Reception Party

Here I do not see active problems. However, the same problems on the sending side still apply.

Once you have undefined behavior, you can expect any behavior as well as influence reception behavior.

Most importantly, the read loop leads to the existence of a data broadcast on m_socket : Send starts from ProcessingThreadProc , while the async_read loop runs in the service stream (s).

m_socket thread m_socket

The tcp::socket class is not thread safe. Since you have a worker thread, as well as a thread that runs io_service (and therefore completion handlers), you cannot access m_socket without synchronization.

You must use strand to serialize operations with the m_socket shared object. In this case, access to m_socket already secure inside completion handlers running on this chain. All other calls should be sent in a lock, for example:

 m_strand.post([this] { auto callback = boost::bind(&MyClass::OnSend, this, boost::asio::placeholders::error); async_write(m_socket, m_sendBuffer, m_strand.wrap(callback)); }); 

In fact, you can use the chain to synchronize access to the m_numPostedSocketIO variable, but this will remove the full duplex ability of read / write operations. In addition, you do not need the m_numPostedSocketIO counter:

Close socket

Note that closing a socket already cancels all pending asynchronous I / O. They will be populated with the error code boost::asio::error::operation_aborted .

This means that you can simplify the part in which you are waiting for the completion of pending I / O. You can just close the socket. Notes:

  • If you have multiple service flows, you have a line for serializing operations.
  • in this case, to prevent unsynchronized access to m_socket , the message in the line:

     m_strand.post([this] { m_socket.close(); }); // eg 

+1
source

Source: https://habr.com/ru/post/1213244/


All Articles