【问题标题】:boost::asio tcp async_read never returnsboost::asio tcp async_read 永远不会返回
【发布时间】:2010-03-09 20:59:31
【问题描述】:

我正在尝试将一些现有代码转换为使用 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。有一些内存泄漏、线程安全问题等等,但那是因为我从现有代码中取出了一些东西,所以不用担心。

不管怎样,这里有一个文件头,其中包含一些常见的东西、一个服务器和一个客户端。

Connection.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;
}

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

谢谢!

【问题讨论】:

  • 异步读取永远不会返回到哪一侧?

标签: boost tcp boost-asio


【解决方案1】:

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

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

另一个更简单的选择是在创建线程以调用 io_service::run 之前实例化一个 io_service::work 实例(从技术上讲,您可以在 any 点之前执行此操作到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 文档中有记录:

阻止 io_service 停止工作

当没有更多工作要做时,某些应用程序可能需要阻止 io_service 对象的 run() 调用返回。例如,io_service 可能在应用程序异步操作之前启动的后台线程中运行。 run() 调用可以通过创建 io_service::work: 类型的对象来保持运行:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-10
    • 2013-12-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多