C++ Linux Google Protobuf + boost::asio Cannot Parse - c++

I am trying to send a Google Protobuf message over a boost::asio socket via TCP. I recognize that TCP is a streaming protocol and thus I am performing length-prefixing on the messages before they go through the socket. I have the code working, but it only appears to work some of the time, even though I'm repeating the same calls and not changing the environment. On occasion I will receive the following error:
[libprotobuf ERROR google/protobuf/message_lite.cc:123] Can't parse message of type "xxx" because it is missing required fields: Name, ApplicationType, MessageType
The reason is easy to understand, but I cannot single out why this only occurs sometimes and parses just fine the majority of the time. It is very easy to duplicate the error by just having a single client talking to the server and simply restarting the processes.
Below are the socket code snippets.
const int TCP_HEADER_SIZE = 8;
Sender:
bool Write(const google::protobuf::MessageLite& proto) {
char header[TCP_HEADER_SIZE];
int size = proto.ByteSize();
char data[TCP_HEADER_SIZE + size];
sprintf(data, "%i", size);
proto.SerializeToArray(data+TCP_HEADER_SIZE, size);
boost::asio::async_write(Socket,
boost::asio::buffer(data, TCP_HEADER_SIZE + size),
boost::bind(&TCPSender::WriteHandler,
this, _1, _2));
}
Receiver:
std::array<char, TCP_HEADER_SIZE> Header;
std::array<char, 8192> Bytes;
void ReadHandler(const boost::system::error_code &ec,
std::size_t bytes_transferred) {
if(!ec) {
int msgsize = atoi(Header.data());
if(msgsize > 0) {
boost::asio::read(Socket, boost::asio::buffer(Bytes,static_cast<std::size_t>(msgsize)));
ReadFunc(Bytes.data(), msgsize);
}
boost::asio::async_read(Socket, boost::asio::buffer(Header, TCP_HEADER_SIZE),
boost::bind(&TCPReceiver::ReadHandler, this, _1, _2));
}
else {
std::cerr << "Server::ReadHandler::" << ec.message() << '\n';
}
}
ReadFunc:
void HandleIncomingData(const char *data, const std::size_t size) {
xxx::messaging::CMSMessage proto;
proto.ParseFromArray(data, static_cast<int>(size));
}
I should mention that I need this to be as fast as possible, so any optimizations would be very much appreciated as well.

The program invokes undefined behavior as it fails to meet a lifetime requirement for boost::asio::async_write()'s buffers parameter:
[...] ownership of the underlying memory blocks is retained by the caller, which must guarantee that they remain valid until the handler is called.
Within the Write() function, boost::asio::async_write() will return immediately, and potentially cause data to go out of scope before the asynchronous write operation has completed. To resolve this, consider expanding the life of the underlying buffer, such as by associating the buffer with the operation and performing cleanup in the handler, or making the buffer a data member on TCPSender.

Related

How to access the contents of the boost::asio::streambuf in the async_write() handler

I need to print the contents of the boost::asio::streambuf in the async_write() handler into the log after it was sent with the same async_write(). But although streambuf::size() returns 95 before async_write(), it will return 0 in the async_write() handler, while containing the exact sent data. That makes logging impossible, as we don't know the buffer size (how many symbols to log).
As far as I understand, the problem is that after async_write() has been executed, the internal pointers of the streambuf are changed because of the "write" operation, and the data in the buffer is "invalidated" after been sent. That's why, despite the fact that streambuf::size() returns 95 before async_write(), it will return 0 in the async_write() handler.
I also noticed that the buffer still contains the needed content in the async_write() handler. One could suggest saving the size of the buffer before sending and reuse it in the handler. Still, I assume I cannot rely on the fact that it will always be available in the handler, as far as the streambuf implementation may delete the content if it thinks it would be necessary. Intuitively such approach feels unreliable.
Are there any workarounds to safely print the buffer content into the log in async_write() handler?
// Message class.
struct MyTcpMsg {
...
public:
// Buffer "getter".
boost::asio::streambuf& buffer();
private:
// Buffer that keeps the data that is going to be sent over the network.
boost::asio::streambuf m_buffer;
};
// Message queue.
std::deque<std::unique_ptr<MyTcpMsg>> messagesOut;
//.. fill messagesOut queue with messages... (code omitted)
// Code that executes sending the message.
// Attempting to log the sent data in the handler.
auto& msgStream = m_messagesOut.front()->buffer();
// msgStream.size() is 95 here.
boost::asio::async_write(
getSocket(), msgStream,
[this](boost::system::error_code ec, std::size_t length) {
if (ec == 0) {
auto& msgStreamOut = m_messagesOut.front()->buffer();;
// Log important info. But the buffer size
// in (msgStreamOut.size()) is 0 now, which makes logging impossible,
// although the data is still present in the buffer.
printBufferToLog(msgStreamOut, msgStreamOut.size());
}
});
Thanks in advance
Yeah. You correctly understood the way DynamicBuffer operates. If you don't want that, use a non-dynamic buffer or sequence of buffers.
The good news is that you can get a buffer sequence instance from the streambuf in no effort at all:
auto& sb = m_messagesOut.front()->buffer();
asio::const_buffers_1 buf = sb.data();
So you can update your code:
void stub_send_loop() {
auto& sb = m_messagesOut.front()->buffer();
asio::const_buffers_1 buf = sb.data();
async_write(getSocket(), buf, [=, &sb](error_code ec, size_t length) {
if (!ec) {
// Log important info
(std::cout << "Wrote : ").write(buffer_cast<char const*>(buf), length) << std::endl;
// update stream
sb.consume(length);
}
});
}
Side-note: The exact type of buf is a bit of an implementation detail. I recommend depending on it indirectly to make sure that the implementation of the streambufs buffer sequence is guaranteed to be a single buffer. async_write doesn't care, but your logging code might (as shown). See also is it safe to use boost::asio::streambuf as both an istream and an array as string_view?
Live On Coliru
#include <boost/asio.hpp>
#include <deque>
#include <iostream>
namespace asio = boost::asio;
using asio::ip::tcp;
using boost::system::error_code;
struct MyTcpMsg {
asio::streambuf& buffer() { return m_buffer; }
template <typename... T> MyTcpMsg(T const&... args) {
(std::ostream(&m_buffer) << ... << args);
}
private:
asio::streambuf m_buffer;
};
struct X {
asio::io_context io;
tcp::socket sock_{io};
std::deque<std::unique_ptr<MyTcpMsg>> m_messagesOut;
X() {
m_messagesOut.push_back(std::make_unique<MyTcpMsg>("Hello world: ", 21 * 2, "."));
m_messagesOut.push_back(std::make_unique<MyTcpMsg>("Bye"));
};
tcp::socket& getSocket() {
if (!sock_.is_open())
sock_.connect({{}, 7878});
return sock_;
}
void stub_send_loop() {
auto& sb = m_messagesOut.front()->buffer();
asio::const_buffers_1 buf = sb.data();
async_write(getSocket(), buf, [=, &sb](error_code ec, size_t length) {
if (!ec) {
// Log important info
(std::cout << "Wrote : ").write(buffer_cast<char const*>(buf), length) << std::endl;
// update stream
sb.consume(length);
}
});
}
};
int main() {
X x;
x.stub_send_loop();
}
Local demo:
Side Note
I think you might want to rethink your design a little. Likely, the use of streambuf is a pessimization. You could "just" return a buffer sequence, which may allow you to avoid allocation. Also, the fact that you expose it by mutable reference (via a quasi-class "getter") breaks encapsulation.

Sending a large text via Boost ASIO

I am trying to send a very large string to one of my clients. I am mostly following code in HTTP server example: https://www.boost.org/doc/libs/1_78_0/doc/html/boost_asio/examples/cpp11_examples.html
Write callbacks return with error code 14, that probably means EFAULT, "bad address" according to this link:
https://mariadb.com/kb/en/operating-system-error-codes/
Note that I could not use message() member function of error_code to read error message, that was causing segmentation fault. (I am using Boost 1.53, and the error might be due to this: https://github.com/boostorg/system/issues/50)
When I try to send small strings, let's say of size 10 for example, write callback does not return with an error.
Here is how I am using async_write:
void Connection::do_write(const std::string& write_buffer)
{
auto self(shared_from_this());
boost::asio::async_write(socket_, boost::asio::buffer(write_buffer, write_buffer.size()),
[this, self, write_buffer](boost::system::error_code ec, std::size_t transfer_size)
{
if (!ec)
{
} else {
// code enters here **when** I am sending a large text.
// transfer_size always prints 65535
}
});
}
Here is how I am using async_read_some:
void Connection::do_read()
{
auto self(shared_from_this());
socket_.async_read_some(boost::asio::buffer(buffer_),
[this, self](boost::system::error_code ec, std::size_t bytes_transferred)
{
if (!ec)
{
do_write(VERY_LARGE_STRING);
do_read();
} else if (ec != boost::asio::error::operation_aborted) {
connection_manager_.stop(shared_from_this());
}
});
}
What could be causing write callback to return with error with large string?
The segfault indicates likely Undefined Behaviour to me.
Of course there's to little code to tell, but one strong smell is from you using a reference to a non-member as the buffer:
boost::asio::buffer(write_buffer, write_buffer.size())
Besides that could simply be spelled boost::asio::buffer(writer_buffer), there's not much hope that write_buffer stays around for the duration of the asynchronous operation that depends on it.
As the documentation states:
Although the buffers object may be copied as necessary, ownership of the underlying memory blocks is retained by the caller, which must guarantee that they remain valid until the handler is called.
I would check that you're doing that correctly.
Another potential cause for UB is when you cause overlapping writes on the same socket/stream object:
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.
If you checked both these causes of concern and find that something must be wrong, please post a new question including a fully selfcontained example (SSCCE or MCVE)

Boost Asio async_send produces beep sound

Trying to write a client for a type of server which behaves like an echo server. When I try to send something, it produces a beep sound. I've found that the problem arise at line 356 (on my editor) of win_iocp_socket_service_base.ipp file. The code is:
int result = ::WSASend(impl.socket_, buffers,
static_cast<DWORD>(buffer_count), &bytes_transferred, flags, op, 0);
Then I made some research.
Continuous boost::asio reads here in the comment of the answer, someone said when a binary data is being written to std::cout.
Why does the following code make my computer beep? here, it seems like the problem is same. They concluded that a '\a' is what produced the sound.
Debugging the application, the buffer of the API function I mentioned earlier, contains the following:
"asd#ıııı2•Z\x1aP"
here "asd#" is my string, the rest changes every time when I debug and I don't know what are they. Now, this is probably the part which the sound is produced, but my question how do I prevent it?
I have the following implementation of a one-round "Correspond" function. Note that I also have a isolated Send() function implementation, which can be paired with ReadUntil() explicitly to achieve same result. Using Send() and ReadUntil() together, the beep sound is still produced. Normally, this shouldn't happen as I am not dealing with low-level API functions, but Boost.Asio. Am I doing something wrong?
CODES
void Correspond(const std::string &data,
const std::string &delim,
std::vector<char> &response)
{
std::shared_ptr<std::vector<char>> localbuffer = std::make_shared<std::vector<char>>(data.begin(), data.end());
// pass shared_ptr to callback function to prolong the buffer's lifetime
// !!! perhaps a better buffer management is needed
socket.async_send(boost::asio::buffer(*localbuffer),
std::bind(&TCPClient::on_correspond,
this,
std::placeholders::_1,
std::placeholders::_2,
delim,
std::ref(response),
localbuffer));
}
and following callback implementation
void on_correspond(const boost::system::error_code& errorcode,
std::size_t sent,
const std::string &delim,
std::vector<char> &response,
const std::shared_ptr<std::vector<char>> &buffer)
{
if(errorcode) {
SLOGERROR(mutex, errorcode.message(), "on_correspond()");
}
if(sent == 0) {
SLOG(mutex, "0 bytes sent w/o errors", "on_correspond()");
}
ReadUntil(delim, response);
}
After debugging deep into API, I've found this issue is not related with read function, but I will post it here just to be sure.
void ReadUntil(const std::string &delim, std::vector<char> &response)
{
boost::asio::async_read_until(socket,
buffer,
delim,
std::bind(&TCPClient::on_readuntil,
this,
std::placeholders::_1,
std::placeholders::_2,
std::ref(response)));
}
void on_readuntil(const boost::system::error_code& errorcode,
std::size_t received,
std::vector<char> &response)
{
SLOG(mutex, "on_readuntil invoked", "on_readuntil()");
SLOG(mutex, received, "on_readuntil");
// !!! needs error handling, short-read handling and whatnot
if(errorcode) {
SLOGERROR(mutex, errorcode.message(), "on_readuntil()");
return;
}
if(received == 0) {
SLOG(mutex, "0 bytes received w/o errors", "on_readuntil()");
}
response.reserve(received);
std::copy(boost::asio::buffers_begin(buffer.data()),
boost::asio::buffers_begin(buffer.data()) + received,
std::back_inserter(response));
buffer.consume(received);
}
here "asd#" is my string, the rest changes every time when I debug
The buffer is read without caring about the size of it, welcome to C++. Give the boost::asio::buffer the buffers size.

C++ Boost ASIO async_send_to memory leak

I am currently working on a UDP socket client. I am currently noticing a memory leak and I've tried several things in hopes to squash it, but it still prevails. In my main, I have a char* that has been malloc'd. I then call the below function to send the data:
void Send(const char* data, const int size) {
Socket.async_send_to(boost::asio::buffer(data, size), Endpoint, boost::bind(&MulticastSender::HandleSendTo, this, boost::asio::placeholders::error));
}
If I run this code, it will always leak memory. However, if I comment out the async_send_to call, the memory stays consistent.
I have tried several variations(see below) on this, but they all only appear to speed up the memory leak.
A couple notes, there is a chance that the char* that gets passed to Send may get free'd before the call completes. However, in my variations, I have taken precaution to do handle that.
Variation 1:
void Send(const char* data, const int size) {
char* buf = (char*)malloc(size);
memcpy(buf, data, size);
Socket.async_send_to(boost::asio::buffer(buf, size), Endpoint, boost::bind(&MulticastSender::HandleSendTo, this, boost::asio::placeholders::error, buf));
}
void HandleSendTo(const boost::system::error_code& ec, const char* buf) {
free(buf);
}
Variation 2:
class MulticastSender {
char* Buffer;
public:
void Send(const char* data, const int size) {
Buffer = (char*)malloc(size);
memcpy(Buffer, data, size);
Socket.async_send_to(boost::asio::buffer(Buffer, size), Endpoint, boost::bind(&MulticastSender::HandleSendTo, this, boost::asio::placeholders::error));
}
void HandleSendTo(const boost::system::error_code& ec) {
free(Buffer);
}
}
However, both variations seem to only speed up the memory leak. I have also tried removing the async_send_to and just calling boost::asio::buffer(data, size), but as has been explained in other questions, the buffer does not own the memory and thus it is up to the user to safely manage it. Any thoughts on what could be causing this issue and how to resolve it?
EDIT 1:
As suggested in the comments, I have preallocated a single buffer (for test purposes) and I am never deallocating it, however, the memory leak still persists.
class MulticastSender {
char* Buffer;
const int MaxSize = 16384;
public:
MulticastSender() {
Buffer = (char*)malloc(MaxSize);
}
void Send(const char* data, const int size) {
memcpy(Buffer, data, size);
Socket.async_send_to(boost::asio::buffer(Buffer, size), Endpoint, boost::bind(&MulticastSender::HandleSendTo, this, boost::asio::placeholders::error));
}
void HandleSendTo(const boost::system::error_code& ec) {
}
}
EDIT 2:
As requested here is an MCVE of the problem. In making this I have also observed an interesting behavior that I will explain below.
#include <string>
#include <iostream>
#include <functional>
#include <thread>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
class MulticastSender {
private:
boost::asio::io_service IOService;
const unsigned short Port;
const boost::asio::ip::address Address;
boost::asio::ip::udp::endpoint Endpoint;
boost::asio::ip::udp::socket Socket;
boost::asio::streambuf Buffer;
void HandleSendTo(const boost::system::error_code& ec) {
if(ec) {
std::cerr << "Error writing data to socket: " << ec.message() << '\n';
}
}
void Run() {
IOService.run();
}
public:
MulticastSender(const std::string& address,
const std::string& multicastaddress,
const unsigned short port) : Address(boost::asio::ip::address::from_string(address)),
Port(port),
Endpoint(Address, port),
Socket(IOService, Endpoint.protocol()) {
std::thread runthread(&MulticastSender::Run, this);
runthread.detach();
}
void Send(const char* data, const int size) {
std::ostreambuf_iterator<char> out(&Buffer);
std::copy(data, data + size, out);
Socket.async_send_to(Buffer.data(), Endpoint, boost::bind(&MulticastSender::HandleSendTo, this, boost::asio::placeholders::error));
}
};
const int SIZE = 8192;
int main() {
MulticastSender sender("127.0.0.1", "239.255.0.0", 30000);
while(true) {
char* data = (char*)malloc(SIZE);
std::memset(data, 0, SIZE);
sender.Send(data, SIZE);
usleep(250);
free(data);
}
}
The above code still produces a memory leak. I should mention that I am running this on CentOS 6.6 with kernel Linux dev 2.6.32-504.el6.x86_64 and running Boost 1.55.0. I am observing this simply by watching the process in top.
However, if I simply move the creation of the MulticastSender into the while loop, I no longer observe the memory leak. I am concerned about the speed of the application though, so this is not a valid option.
Memory is not leaking, as there is still a handle to the allocated memory. However, there will be continual growth because:
The io_service is not running because run() is returning as there is no work. This results in completion handlers being allocated, queued into the io_service, but neither executed nor freed. Additionally, any cleanup that is expected to occur within the completion handler is not occurring. It is worth noting that during the destruction of the io_service, completion handlers will be destroyed and not invoked; hence, one cannot depend on only performing cleanup within the execution of the completion handler. For more details as to when io_service::run() blocks or unblocks, consider reading this question.
The streambuf's input sequence is never being consumed. Each iteration in the main loop will append to the streambuf, which will then send the prior message content and the newly appended data. See this answer for more details on the overall usage of streambuf.
A few other points:
The program fails to meet a requirement of async_send_to(), where ownership of the underlying buffer memory is retained by the caller, who must guarantee that it remains valid until the handler is called. In this case, when copying into the streambuf via the ostreambuf_iterator, the streambuf's input sequence is modified and invalidates the buffer returned from streambuf.data().
During shutdown, some form of synchronization will need to occur against threads that are running the io_service. Otherwise, undefined behavior may be invoked.
To resolve these issues, consider:
Using boost::asio::io_service::work to ensure that the io_service object's run() does not exit when there is no work remaining.
Passing ownership of the memory to the completion handler via std::shared_ptr or another class that will manage the memory via resource acquisition is initialization (RAII) idiom. This will allow for proper cleanup and meet the requirement's of the buffer validity for async_send_to().
Not detaching and joining upon the worker thread.
Here is a complete example based on the original that demonstrates these changes:
#include <string>
#include <iostream>
#include <thread>
#include <boost/asio.hpp>
class multicast_sender
{
public:
multicast_sender(
const std::string& address,
const std::string& multicast_address,
const unsigned short multicast_port)
: work_(io_service_),
multicast_endpoint_(
boost::asio::ip::address::from_string(multicast_address),
multicast_port),
socket_(io_service_, boost::asio::ip::udp::endpoint(
boost::asio::ip::address::from_string(address),
0 /* any port */))
{
// Start running the io_service. The work_ object will keep
// io_service::run() from returning even if there is no real work
// queued into the io_service.
auto self = this;
work_thread_ = std::thread([self]()
{
self->io_service_.run();
});
}
~multicast_sender()
{
// Explicitly stop the io_service. Queued handlers will not be ran.
io_service_.stop();
// Synchronize with the work thread.
work_thread_.join();
}
void send(const char* data, const int size)
{
// Caller may delete before the async operation finishes, so copy the
// buffer and associate it to the completion handler's lifetime. Note
// that the completion may not run in the event the io_servie is
// destroyed, but the the completion handler will be, so managing via
// a RAII object (std::shared_ptr) is ideal.
auto buffer = std::make_shared<std::string>(data, size);
socket_.async_send_to(boost::asio::buffer(*buffer), multicast_endpoint_,
[buffer](
const boost::system::error_code& error,
std::size_t bytes_transferred)
{
std::cout << "Wrote " << bytes_transferred << " bytes with " <<
error.message() << std::endl;
});
}
private:
boost::asio::io_service io_service_;
boost::asio::io_service::work work_;
boost::asio::ip::udp::endpoint multicast_endpoint_;
boost::asio::ip::udp::socket socket_;
std::thread work_thread_;
};
const int SIZE = 8192;
int main()
{
multicast_sender sender("127.0.0.1", "239.255.0.0", 30000);
char* data = (char*) malloc(SIZE);
std::memset(data, 0, SIZE);
sender.send(data, SIZE);
free(data);
// Give some time to allow for the async operation to complete
// before shutting down the io_service.
std::this_thread::sleep_for(std::chrono::seconds(2));
}
Output:
Wrote 8192 bytes with Success
The class variation looks better, and you can use boost::asio::streambuf as a buffer for network io (it doesn't leak and doesn't need much maintenance).
// The send function
void
send(char const* data, size_t size)
{
std::ostreambuf_iterator<char> out(&buffer_);
std::copy(data, data + size, out);
socket.async_send_to(buffer_, endpoint,
std::bind( &multicast_sender,
this, std::placeholders::_1 ));
}
Moving the socket and endpoint inside the class would be a good idea. Also you should bear in mind that the async operation can finish when your object goes out of scope. I would recommend using enable_shared_from_this (boost or std flavours) and pass shared_from_this() instead of this to the bind function.
The whole solution would look like this:
#include <boost/asio.hpp>
class multicast_sender :
public std::enable_shared_from_this<multicast_sender> {
using boost::asio::ip::udp;
udp::socket socket_;
udp::endpoint endpoint_;
boost::asio::streambuf buffer_;
public:
multicast_sender(boost::asio::io_service& io_service, short port,
udp::endpoint const& remote) :
socket_(io_service, udp::endpoint(udp::v4(), port)),
endpoint_(remote)
{
}
void
send(char const* data, size_t size)
{
std::ostreambuf_iterator<char> out(&buffer_);
std::copy(data, data + size, out);
socket_.async_send_to(buffer_, endpoint_,
std::bind( &multicast_sender,
shared_from_this(), std::placeholders::_1 ));
}
void
handle_send(boost::system::error_code const& ec)
{
}
};
EDIT
And as far as you don't have to do anything in the write handler, you can use a lambda (requires C++11) as a completion callback
// The send function
void
send(char const* data, size_t size)
{
std::ostreambuf_iterator<char> out(&buffer_);
std::copy(data, data + size, out);
socket.async_send_to(buffer_, endpoint,
[](boost::system::error_code const& ec){
std::cerr << "Error sending :" << ec.message() << "\n";
});
}

boost read_some function lost data

I'm implementing a tcp server with boost asio library.
In the server, I use asio::async_read_some to get data, and use asio::write to write data. The server code is something like that.
std::array<char, kBufferSize> buffer_;
std::string ProcessMessage(const std::string& s) {
if (s == "msg1") return "resp1";
if (s == "msg2") return "resp2";
return "";
}
void HandleRead(const boost::system::error_code& ec, size_t size) {
std::string message(buffer_.data(), size);
std::string resp = ProcessMessage(message);
if (!resp.empty()) {
asio::write(socket, boost::asio::buffer(message), WriteCallback);
}
socket.async_read_some(boost::asio::buffer(buffer_));
}
Then I write a client to test the server, the code is something like
void MessageCallback(const boost::system::error_code& ec, size_t size) {
std::cout << string(buffer_.data(), size) << std::endl;
}
//Init socket
asio::write(socket, boost::asio::buffer("msg1"));
socket.read_some(boost::asio::buffer(buffer_), MessageCallback);
// Or async_read
//socket.async_read_some(boost::asio::buffer(buffer_), MessageCallback);
asio::write(socket, boost::asio::buffer("msg1"));
socket.read_some(boost::asio::buffer(buffer_), MessageCallback);
// Or async_read
//socket.async_read_some(boost::asio::buffer(buffer_), MessageCallback);
If I run the client, the code will be waiting at second read_some, and output is:resp1.
If I remove the first read_some, the ouput is resp1resp2, that means the server done the right thing.
It seems the first read_some EAT the second response but don't give the response to MessageCallback function.
I've read the quesion at What is a message boundary?, I think if this problem is a "Message Boundary" problem, the second read_some should print something as the first read_some only get part of stream from the tcp socket.
How can I solve this problem?
UPDATE:
I've try to change the size of client buffer to 4, that output will be:
resp
resp
It seems the read_some function will do a little more than read from the socket, I'll read the boost code to find out is that true.
The async_read_some() member function is very likely not doing what you intend, pay special attention to the Remarks section of the documentation
The read operation may not read all of the requested number of bytes.
Consider using the async_read function if you need to ensure that the
requested amount of data is read before the asynchronous operation
completes.
Note that async_read() free function does offer the guarantee that you are looking for
This operation is implemented in terms of zero or more calls to the
stream's async_read_some function, and is known as a composed
operation. The program must ensure that the stream performs no other
read operations (such as async_read, the stream's async_read_some
function, or any other composed operations that perform reads) until
this operation completes.