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

时间:2010-03-09 20:59:31

标签: boost tcp boost-asio

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

我在做什么:

  1. 启动服务器进程
  2. 服务器进程创建一个空套接字并使用它来侦听(使用tcp :: acceptor)端口上的TCP连接(例如10010)
  3. 启动客户端流程
  4. 让客户端进程创建一个连接到服务器端口的套接字
  5. 当服务器看到客户端正在连接时,它开始在套接字上侦听数据(使用async_read)并创建另一个空套接字以侦听端口上的另一个TCP连接
  6. 当客户端看到服务器已连接时,它会发送100个字节的数据(使用async_write)并等待套接字告诉它发送已完成...当发生这种情况时,它会打印一条消息并关闭
  7. 当服务器收到通知,其中包含已读取的数据时,会打印一条消息并关闭
  8. 显然,我已经从我正在尝试实现的内容中大大减少了这些代码,这是我可以创建一些可以重现问题的东西。我正在Windows上运行并拥有一个可视化工作室解决方案文件,您可以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;
    }
    
    我已经搜了一下,但运气不好。据我所知,我几乎完全匹配样本,所以它必须是我忽略的小东西。

    谢谢!

1 个答案:

答案 0 :(得分:2)

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

一个可能的选择是从conn->AsyncReceive()内拨打ConnectHandler(),以便在async_read()返回之前调用ConnectHandler(),从而后台线程调用{ {1}}不会返回。

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

io_service.run()

这在io_service文档中有记录:

  

停止io_service停止工作

     

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

类型的对象,run()调用可以保持运行

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