Write to boost :: asio socket from different threads

In our application, we use the Boost libraries (and ASIO for network communications).

Recently, we have found that if we send our data from different streams through the same socket, our client application receives garbage data.

A small test to highlight the problem:

#include <stdio.h> #include <boost/thread.hpp> #include <boost/asio.hpp> void send_routine(boost::shared_ptr<boost::asio::ip::tcp::socket> s, char c) { std::vector<char> data(15000, c); data.push_back('\n'); for (int i=0; i<1000; i++) boost::asio::write(*s, boost::asio::buffer(&data[0], data.size())); } int main() { using namespace boost::asio; using namespace boost::asio::ip; try { io_service io_service; io_service::work work(io_service); const char* host = "localhost"; const char* service_name = "18000"; tcp::resolver resolver(io_service); tcp::resolver::query query(tcp::v4(), host, service_name); tcp::resolver::iterator iterator = resolver.resolve(query); auto socket = boost::shared_ptr<tcp::socket>(new tcp::socket(io_service)); socket->connect(*iterator); boost::thread t1(send_routine, socket, 'A'); boost::thread t2(send_routine, socket, 'B'); boost::thread t3(send_routine, socket, 'C'); t1.join(); t2.join(); t3.join(); } catch (std::exception& e) { printf("FAIL: %s\n", e.what()); } return 0; } 

So, we create a socket here, connect to localhost:18000 and start 3 threads that will be written to the socket.

In another terminal window, I run nc -l -p 18000 | tee out.txt | sort | uniq | wc -l nc -l -p 18000 | tee out.txt | sort | uniq | wc -l nc -l -p 18000 | tee out.txt | sort | uniq | wc -l . I expect 3 as output, but it returns more than 100 "different lines" in the network stream (so the data is corrupted). But it works with small buffer sizes (if we change 15000 to 80 , for example).

So the question is, is this behavior of the ASIO library correct? And one more thing: how to fix it? Should I use mutex inside my send_routine function (or is there another solution)?

+4
source share
4 answers

Well, according to the documentation, tcp::socket not thread safe when sharing multiple threads.
This way, you either perform synchronization, as you suggested with boost::mutex , or use asynchronous recording. io_service work for you.

+2
source

write and async_write are not thread safe in the way you use them. The canonical way to approach this is to queue your messages and then write them one at a time.

+5
source

Yes, there is another solution! Lines: Use topics without explicit blocking . Be careful that the threads provide "atomic" socket access for the "event handlers", of course, you need to use asio "event handlers", which does not apply to your code. In other words, you need to use boost :: asio :: async_write instead of boost :: asio :: write.

+2
source

You can have two problems: the problem with the threads can be solved, for example, with one thread for recording, and with a queue where all threads send a response. You can also change your design to asynchronous and use the write_some () function and enable thread processing with io_service :: run (), which can be executed by multiple threads.

Secondly, you may have a problem with the protocol if the client expects responses to it in the same order.

Hth

Thorsten

0
source

All Articles