代码之家  ›  专栏  ›  技术社区  ›  teeks99

boost::asio tcp async_read从不返回

  •  3
  • teeks99  · 技术社区  · 16 年前

    我试图将现有代码转换为使用Boost的ASIO TCP套接字,而不是使用当前的实现。我能得到一个非常相似的例子( of a chat client/server )从boost站点开始工作,但是当我试图将代码放入自己的程序时,它就停止工作了。

    我在做什么:

    1. 启动服务器进程
    2. 服务器进程生成一个空套接字,并使用它侦听(使用tcp::acceptor)端口上的tcp连接(例如10010)
    3. 启动客户端进程
    4. 让客户端进程创建一个连接到服务器端口的套接字
    5. 当客户端看到服务器已连接时,它会发送100字节的数据(使用异步写入),并等待套接字告诉它发送完成…当发生这种情况时,它会打印一条消息并关闭
    6. 当服务器收到已读取数据的通知时,它将打印一条消息并关闭

    很明显,我已经把代码从我要实现的代码中删减了很多,这是尽可能小的,我可以做一些重现问题的东西。我在windows上运行,有一个visual studio解决方案文件 get . 有一些内存泄漏,线程安全问题,等等,但这是因为我把东西从现有代码中取出,所以不用担心它们。

    不管怎样,这是一个文件头,里面有一些常见的东西,一个服务器和一个客户机。

    连接.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
    

    
    #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 回复  |  直到 16 年前
        1
  •  2
  •   villapx    10 年前

    在你的客户代码中 ConnectHandler() async_connect() 操作是与io U服务相关联的唯一工作;因此当 连接处理程序() io_service.run() 返回,线程退出。

    一个可能的选择是 conn->AsyncReceive() 从内部 连接处理程序() async_read() 在 连接处理程序() 返回,因此后台线程调用 不会再回来了。

    另一个选择,更简单的一个,是在创建调用io_service::run的线程之前实例化一个io_service::work实例(技术上,可以在 点之前 io U服务。运行()

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

    这一点记录在io U服务文档中:

    阻止io U服务耗尽工作

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

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

    推荐文章