boost::asio tcp async_read 永远不会返回

标签 boost tcp boost-asio

我正在尝试将一些现有代码转换为使用 boost 的 asio tcp 套接字而不是我们当前的实现。我能够从 boost 站点获得一个非常相似的示例 ( of a chat client/server ),但当我尝试将代码放入我自己的程序时,它停止工作。

我在做什么:

  1. 启动服务器进程
  2. 服务器进程创建一个空套接字并使用它来监听(使用 tcp::acceptor)端口(例如 10010)上的 TCP 连接
  3. 启动客户端进程
  4. 让客户端进程创建一个连接到服务器端口的套接字
  5. 当服务器看到客户端正在连接时,它开始在套接字上监听数据(使用 async_read)并创建另一个空套接字以监听端口上的另一个 TCP 连接
  6. 当客户端看到服务器已连接时,它会发送 100 字节的数据(使用 async_write)并等待套接字告诉它发送已完成...当发生这种情况时,它会打印一条消息并关闭<
  7. 当服务器收到已读取数据的通知时,它会打印一条消息并关闭

很明显,我已经从我试图实现的代码中大大缩减了这段代码,这是我可以制作的重现问题的尽可能小的代码。我在 Windows 上运行并且有一个 visual studio 解决方案文件,您可以 get .存在一些内存泄漏、线程安全问题等,但那是因为我正在从现有代码中取出一些东西,所以不要担心它们。

无论如何,这是一个带有一些常用内容的 header 、一个服务器和一个客户端的文件。

连接.hpp:


#ifndef CONNECTION_HPP
#define CONNECTION_HPP
#include 
#include 
#include 

class ConnectionTransfer
{
public:
   ConnectionTransfer(char* buffer, unsigned int size) :
      buffer_(buffer), size_(size)   {
   }
   virtual ~ConnectionTransfer(void){}

   char* GetBuffer(){return buffer_;}
   unsigned int GetSize(){return size_;}

   virtual void CallbackForFinished() = 0;

protected:
   char* buffer_;
   unsigned int size_;
};

class ConnectionTransferInProgress
{
public:
   ConnectionTransferInProgress(ConnectionTransfer* ct):
      ct_(ct)
   {}
   ~ConnectionTransferInProgress(void){}

   void operator()(const boost::system::error_code& error){Other(error);}
   void Other(const boost::system::error_code& error){
      if(!error)
         ct_->CallbackForFinished();
   }
private:
   ConnectionTransfer* ct_;
};

class Connection 
{
public:
   Connection(boost::asio::io_service& io_service):
   sock_(io_service)
   {}
   ~Connection(void){}
   void AsyncSend(ConnectionTransfer* ct){
      ConnectionTransferInProgress tip(ct);
      //sock_->async_send(boost::asio::buffer(ct->GetBuffer(), 
      //   static_cast(ct->GetSize())), tip);
      boost::asio::async_write(sock_, boost::asio::buffer(ct->GetBuffer(), 
         static_cast(ct->GetSize())), boost::bind(
         &ConnectionTransferInProgress::Other, tip, boost::asio::placeholders::error));
   }
   void AsyncReceive(ConnectionTransfer* ct){
      ConnectionTransferInProgress tip(ct);
      //sock_->async_receive(boost::asio::buffer(ct->GetBuffer(), 
      //   static_cast(ct->GetSize())), tip);
      boost::asio::async_read(sock_, boost::asio::buffer(ct->GetBuffer(), 
         static_cast(ct->GetSize())), boost::bind(
         &ConnectionTransferInProgress::Other, tip, boost::asio::placeholders::error));
   }

   boost::asio::ip::tcp::socket& GetSocket(){return sock_;}
private:
   boost::asio::ip::tcp::socket sock_;
};
#endif //CONNECTION_HPP

BoostConnectionClient.cpp:


#include "Connection.hpp"

#include 
#include 
#include 
#include 

using namespace boost::asio::ip;

bool connected;
bool gotTransfer; 

class FakeTransfer : public ConnectionTransfer
{
public:
   FakeTransfer(char* buffer, unsigned int size) : ConnectionTransfer(buffer, size)
   {
   }
   void CallbackForFinished()
   {
      gotTransfer = true;
   }
};

void ConnectHandler(const boost::system::error_code& error)
{
   if(!error)
      connected = true;
}

int main(int argc, char* argv[])
{
   connected = false;
   gotTransfer = false;
   
   boost::asio::io_service io_service;

   Connection* conn = new Connection(io_service);

   tcp::endpoint ep(address::from_string("127.0.0.1"), 10011);
   conn->GetSocket().async_connect(ep, ConnectHandler);
   
   boost::thread t(boost::bind(&boost::asio::io_service::run, &io_service));

   while(!connected)
   {
      boost::this_thread::sleep(boost::posix_time::millisec(1));
   }
   std::cout (angle brackets here) "Connected\n";

   char data[100];
   FakeTransfer* ft = new FakeTransfer(data, 100);
   conn->AsyncReceive(ft);

   while(!gotTransfer)
   {
      boost::this_thread::sleep(boost::posix_time::millisec(1));
   }
   
   std::cout (angle brackets here) "Done\n";

   return 0;
}

BoostConnectionServer.cpp:


#include "Connection.hpp"

#include 
#include 
#include 
#include 

using namespace boost::asio::ip;

Connection* conn1;
bool conn1Done;
bool gotTransfer;
Connection* conn2;

class FakeAcceptor
{
public:
   FakeAcceptor(boost::asio::io_service& io_service, const tcp::endpoint& endpoint)
      : 
      io_service_(io_service),
      acceptor_(io_service, endpoint)
  {
      conn1 = new Connection(io_service_);
      acceptor_.async_accept(conn1->GetSocket(),  
         boost::bind(&FakeAcceptor::HandleAccept, this, conn1, 
         boost::asio::placeholders::error));
   }
   void HandleAccept(Connection* conn, const boost::system::error_code& error)
   {
      if(conn == conn1)
         conn1Done = true;
      conn2 = new Connection(io_service_);
      acceptor_.async_accept(conn2->GetSocket(),  
         boost::bind(&FakeAcceptor::HandleAccept, this, conn2, 
         boost::asio::placeholders::error));
   }
   boost::asio::io_service& io_service_;
   tcp::acceptor acceptor_;
};

class FakeTransfer : public ConnectionTransfer
{
public:
   FakeTransfer(char* buffer, unsigned int size) : ConnectionTransfer(buffer, size)
   {
   }
   void CallbackForFinished()
   {
      gotTransfer = true;
   }
};

int main(int argc, char* argv[])
{
   boost::asio::io_service io_service;
   conn1Done = false;
   gotTransfer = false;
   tcp::endpoint endpoint(tcp::v4(), 10011);
   FakeAcceptor fa(io_service, endpoint);
   boost::thread t(boost::bind(&boost::asio::io_service::run, &io_service));

   while(!conn1Done)
   {
      boost::this_thread::sleep(boost::posix_time::millisec(1));
   }
   std::cout (angle brackets here) "Accepted incoming connection\n";

   char data[100];
   FakeTransfer* ft = new FakeTransfer(data, 100);
   conn1->AsyncReceive(ft);

   while(!gotTransfer)
   {
      boost::this_thread::sleep(boost::posix_time::millisec(1));
   }
   std::cout (angle brackets here) "Success!\n";
   return 0;
}

我搜索了一下,但运气不佳。据我所知,我几乎完全匹配样本,所以它一定是我忽略的小东西。

谢谢!

最佳答案

在您的客户端代码中,您的 ConnectHandler() 回调函数只是设置一个值然后返回,而不会向 io_service 发送任何更多工作。那时,async_connect() 操作是与 io_service 关联的唯一工作;所以当 ConnectHandler() 返回时,不再有与​​ io_service 相关的工作。因此,后台线程对 io_service.run() 的调用返回,线程退出。

一个可能的选择是从 ConnectHandler() 中调用 conn->AsyncReceive(),以便 async_read() 获取在 ConnectHandler() 返回之前调用,因此后台线程对 io_service.run() 的调用不会返回。

另一种更简单的选择是在创建线程之前实例化一个 io_service::work 实例以调用 io_service::run(从技术上讲,您可以在任何点之前执行此操作到 io_service.run() 调用的返回):

...
// some point in the main() method, prior to creating the background thread
boost::asio::io_service::work work(io_service)
...

这在 io_service 文档中有记录:

Stopping the io_service from running out of work

Some applications may need to prevent an io_service object's run() call from returning when there is no more work to do. For example, the io_service may be being run in a background thread that is launched prior to the application's asynchronous operations. The run() call may be kept running by creating an object of type io_service::work:

http://www.boost.org/doc/libs/1_43_0/doc/html/boost_asio/reference/io_service.html

关于boost::asio tcp async_read 永远不会返回,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2412520/

相关文章:

c++ - 链接器错误 - 带有 libboost_thread 的 macOS 上 undefined symbol std::string::c_str() const?

C++多写点流

c++ - C++ 网络库

c++ - 不能在 Boost.Asio 中使用可移动物体

sockets - 使用 TCP/IP LabVIEW 在 PC 和 UR5 Universal Robotics 机器人 ARM 之间进行通信

c++ - boost::asio::io_service.post() 后台线程内存使用

c++ - 将 boost 与 Visual Studio 和 vcpkg 链接时出错

c++ - 在同一线程上 boost 互斥锁

c# - 解析来自 TCP/IP 连接的 XML 字符串

windows - netsh winsock reset - 它的作用是什么?为什么我的无线网卡在使用该命令之前没有建立良好的连接?