当我的Boost::ASIO TCP服务器刚刚开始运行(AKA io_service.run())时,如何启动&Quot;事件&Quot;?

2022-02-21 00:00:00 c++ c++11 boost boost-asio

基于Boost::ASIO客户端/服务器关系,只有当服务器线程处于"正在等待连接"状态时,我才必须从服务器程序启动客户端程序。

我的问题是如何了解这种状态?

作为示例使用asio example/serialization link,并将server.cpp的main函数替换为该代码:

#include <conio.h>
#include <concrt.h> // wait function
#include <future>
#include <thread>

void server_thread( std::promise<bool>& run )
{ 
    boost::asio::io_service io_service;
    s11n_example::server server(io_service, 123);
    // too early to run.set_value( true );
    io_service.run();
    // too late to run.set_value( true );
}

int main(int argc, char* argv[])
{
    std::promise<bool> run;
    std::thread thrd( server_thread, boost::ref( run ) );
    thrd.detach(); 

    bool launched = run.get_future().get();
    // server is waiting for connection
    // launch the client
    if( launched )
    {
        int rc = system( "start client.exe localhost 123" );
        if( rc )
            std::cerr << "system failed returning " << rc << std::endl ;
    }
    else
        std::cerr << "server_thread failure" << std::endl ;

    std::cout << "hit a key to exit"  ;
    while( !_kbhit() )
        Concurrency::wait( 100 );

    return 0;
}

谢谢,


解决方案

简而言之,s11n_example::server处于一种状态,即传入连接将在构造函数调用完成后立即排队。


通过定义状态和操作之间的区别可能更容易理解这一点。状态确定OS可以对对象做什么;应用程序启动执行操作的操作,并且可能取决于状态。例如,当套接字处于打开状态时,操作系统将对数据进行排队;读取操作将检索排队的数据。这同样适用于接受者。当接受者处于侦听状态时,操作系统将对连接进行排队;接受操作将完成连接,并将其从队列中删除。

ANacceptor的[状态]和转换()如下:

     .----> [closed] ------.     [closed]:    socket not open
     |                     |     [opened]:    socket open but not listening for
     |                     V                  connections
  close() <------.      open()   [listening]: incoming connections will be
     ^           |         |                  queued until accepted(), causing
     |           |         V                  the connection to be established
[listening]      '---- [opened]
     ^                     |
     |                     |
     '------ listen() <----'
各种重载构造函数将导致acceptor以关闭、打开或侦听状态开始其生命周期。在s11n_example::server的情况下,接受器是用端点构造的,所以this过载会导致接受器在构造后处于侦听状态。相当于做:

using boost::asio::ip::tcp;
tcp::endpoint endpoint_(tcp::v4(), 123);
tcp::acceptor acceptor_(io_service); // closed state
acceptor.open(endpoint_.protocol()); // opened state
acceptor.bind(endpoint);
acceptor.listen();                   // listening state

因此,承诺可以在server构造之后、io_service.run()之前设置:

void server_thread(std::promise<bool>& run)
{ 
    boost::asio::io_service io_service;
    s11n_example::server server(io_service, 123);
    // The server's acceptor is in a listening state, so connection attempts
    // will be queued even without the io_service event loop running.  The
    // server also has an outstanding asynchronous accept operation.
    run.set_value(true);
    // Run the service, this will start an asynchronous loop that accepts 
    // connections.
    io_service.run();
}

需要注意的一个微妙之处是Boost.Asio的接受者不提供:

  • 基于反应堆的操作,用于接受连接。因此,无法检测连接何时准备好接受(即连接已排队等待接受)。
  • 检测acceptor是否处于侦听状态的更高级别方法。然而,这可以通过查询接受者的native_handle来实现。例如,使用getsockopt()获取SOL_SOCKET/SO_ACCEPTCONN的值。

相关文章