使用相同的udp套接字进行异步接收/发送

Ghi*_*ita 5 c++ networking boost boost-asio

我在我的udp服务器中使用相同的套接字以便从某些端口上的客户端接收数据,然后在处理请求后使用ip :: ud :: socket :: async_send_to响应客户端

接收也与async_receive_from异步完成.套接字使用相同的ioService(毕竟它是相同的套接字)文档没有明确说明是否可以在同一个udp套接字从客户端A接收数据报(以异步方式)并可能将另一个数据报发送到客户端B(异步)我怀疑这可能导致问题.我最终使用相同的套接字进行回复,因为在回复另一个客户端时,我无法将另一个套接字绑定到同一个服务器端口.

如何将另一个套接字绑定到同一个服务器端口?

编辑.我尝试将第二个udp套接字绑定到同一个UDP端口:

socket(ioService, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), port))
Run Code Online (Sandbox Code Playgroud)

当我第一次这样做(绑定服务器"接收"套接字)时没关系,但是第二次尝试创建另一个套接字,就像它在bind上报告错误一样(asio抛出异常)

Tan*_*ury 14

可以从一个远程端点同时接收UDP套接字并发送到不同的远程端点.但是,根据Boost.Asio Threads和Boost.Asio文档,对单个对象进行并发调用通常是不安全的.

因此,这是安全的:

 thread_1                             | thread_2
--------------------------------------+---------------------------------------
socket.async_receive_from( ... );     |
socket.async_send_to( ... );          |

这是安全的:

 thread_1                             | thread_2
--------------------------------------+---------------------------------------
socket.async_receive_from( ... );     |
                                      | socket.async_send_to( ... );

但这被指定为不安全:

 thread_1                             | thread_2
--------------------------------------+---------------------------------------
socket.async_receive_from( ... );     | socket.async_send_to( ... );
                                      |

请注意,某些功能(例如boost::asio::async_read,组合操作)具有其他线程安全限制.


如果满足以下任一条件,则不需要进行其他同步,因为流将隐式同步:

  • 所有套接字调用都在处理程序中进行,并且io_service::run()仅从单个线程调用.
  • async_receive_from并且async_send_to仅在同一个异步操作链中调用.例如,ReadHandler传递给async_receive_from调用async_send_to,WriteHandler传递给async_send_to调用async_receive_from.

    void read()
    {
      socket.async_receive_from( ..., handle_read );  --.
    }                                                   |
        .-----------------------------------------------'
        |      .----------------------------------------.
        V      V                                        |
    void handle_read( ... )                             |
    {                                                   |
      socket.async_send_to( ..., handle_write );  --.   |
    }                                               |   |
        .-------------------------------------------'   |
        |                                               |
        V                                               |
    void handle_write( ... )                            |
    {                                                   |
      socket.async_receive_from( ..., handle_read );  --'
    }
    
    Run Code Online (Sandbox Code Playgroud)

另一方面,如果有多个线程可能对套接字进行并发调用,则需要进行同步.考虑通过boost :: asio :: io_service :: strand调用函数和处理程序,或使用其他同步机制(如Boost.Thread的互斥锁)来执行同步.


除线程安全外,还必须考虑对象生命周期的管理.如果服务器需要同时处理多个请求,然后小心的所有权bufferendpoint每个请求- >流程- >响应链.根据Per async_receive_from的文档,调用者保留缓冲区端点的所有权.因此,通过boost :: shared_ptr管理对象的生命周期可能更容易.否则,如果链足够快以至于不需要并发链,那么它简化了管理,允许每个请求使用相同的缓冲区端点.


最后,socket_base::reuse_address该类允许将套接字绑定到已在使用的地址.但是,我不认为这是一个适用的解决方案,因为它通常用于:

  • 对于TCP,允许进程重新启动并侦听同一端口,即使端口处于某种TIME_WAIT状态.
  • 对于UDP,允许多个进程绑定到同一端口,允许每个进程通过多播接收和广播.

  • 在对象上挂起多个异步操作是可以的; 它只是指定同时调用该对象是不安全的.如果从处理程序外部发生套接字调用,并且只有一个线程正在调用`io_service :: run()`,那么将套接字调用发布到`io_service`将同步.Boost.Asio要求调用者保证某些参数在调用处理程序之前保持有效; 它永远不会强迫用户以特定方式管理它们.在某些情况下,通过智能指针将对象生命周期与异步链相关联可能更容易. (2认同)