C++ 从不同线程写入boost::asio套接字

C++ 从不同线程写入boost::asio套接字,c++,multithreading,sockets,boost,boost-asio,C++,Multithreading,Sockets,Boost,Boost Asio,在我们的应用程序中,我们使用Boost库(以及用于网络通信的ASIO) 最近,我们发现,如果我们通过同一个套接字从不同线程发送数据,我们的客户机应用程序就会收到垃圾数据 突出问题的小测试: #include <stdio.h> #include <boost/thread.hpp> #include <boost/asio.hpp> void send_routine(boost::shared_ptr<boost::asio::ip::tcp::so

在我们的应用程序中,我们使用Boost库(以及用于网络通信的ASIO)

最近,我们发现,如果我们通过同一个套接字从不同线程发送数据,我们的客户机应用程序就会收到垃圾数据

突出问题的小测试:

#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;
}
#包括
#包括
#包括
void send_例程(boost::shared_ptr s,char c)
{
标准::矢量数据(15000,c);
数据。推回('\n');
for(int i=0;i连接(*迭代器);
boost::线程t1(发送_例程,套接字'A');
boost::线程t2(发送_例程,套接字'B');
boost::线程t3(send_例程,socket,'C');
t1.join();
t2.连接();
t3.join();
}
捕获(标准::异常&e){
printf(“失败:%s\n”,例如what());
}
返回0;
}
因此,我们在这里创建套接字,连接到
localhost:18000
,并启动3个线程来写入套接字

在不同的终端窗口中,我运行
nc-l-p 18000 | tee out.txt | sort | uniq | wc-l
。我希望
3
作为输出,但它在网络流中返回100多个“不同的字符串”(因此,数据已损坏)。但它的缓冲区大小很小(例如,如果我们将
15000
更改为
80


因此,问题是:这是ASIO库的正确行为吗?另一个问题是:如何修复它?我应该在我的
send_例程
函数中使用
mutex
(或者有其他解决方案)?

您可能有两个问题,线程问题可以通过一个专用于写入的线程和一个所有线程都在那里发布响应的队列来解决。您还可以将设计更改为异步设计,并使用write_some()函数,让io_服务::run()完成线程处理,可以由多个线程运行

第二,如果客户希望以相同的顺序回答其问题,那么您可能会遇到协议问题

Torsten根据tcp::socket在多个线程之间共享时不是线程安全的。

因此,您要么像您建议的那样使用
boost::mutex
进行同步,要么使用异步写入。
io\u服务
为您提供工作。

write
async\u write
在您使用它们的方式中不是线程安全的。实现这一点的标准方法是将消息排队,然后将它们写出来每次。

是的,还有另一种解决方案!
。请注意,绞线仅为“事件处理程序”提供对套接字的“原子”访问,当然您需要使用asio“事件处理程序”这与您的代码不同。换句话说,您需要使用boost::asio::async_write而不是boost::asio::write。

async_write
不会神奇地解决此问题。即使使用异步IO完成,对不同缓冲区的写入仍然需要连续进行。
IO服务应为此进行序列化you.async\u write由多个async\u send组成,没有进一步的线程安全性(我研究了实现)。因此,当从不同线程使用同一套接字时,它不是线程安全的。@Databyte使用一个链。它确保由同一个链发送的所有处理程序都将被序列化发送。请注意,不鼓励使用仅链接的答案,因此答案应该是搜索解决方案的终点(相对于另一个引用的停顿,它会随着时间的推移而变得陈旧)。请考虑在这里添加一个独立的概要,保持链接作为引用。