【问题标题】:Use same udp socket for async receive/send使用相同的 udp 套接字进行异步接收/发送
【发布时间】:2012-09-03 18:56:49
【问题描述】:

我在我的 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))

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

【问题讨论】:

  • 请说明您的实际问题。您的设置是否正常工作,并且您正在寻找设计是否正常的确认?还是它不起作用,如果不是,您观察到什么错误?或者您是在问如何将另一个套接字绑定到服务器端口?
  • @mtrw 是否可以将另一个套接字绑定到同一个服务器端口?这样我会使用不同的套接字来发送重播。发生的情况是,我认为我的设置会导致服务器故障。
  • 请编辑您的问题以澄清而不是添加 cmets
  • 我想这里的关键问题是试图用同一个端口绑定多个套接字,这根本不可能而且毫无意义。
  • @E_net4 确实可以通过SO_REUSEADDR 使用UDP:但是我同意这是没有意义的。 OP:套接字一直是全双工的:您可以同时读取和写入它们。

标签: c++ networking boost boost-asio


【解决方案1】:

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

因此,这是安全的:

 thread_1 |线程_2
--------------------------------------------------+------------ ----------------------------
socket.async_receive_from( ... ); |
socket.async_send_to( ... ); |

这是安全的:

 thread_1 |线程_2
--------------------------------------------------+------------ ----------------------------
socket.async_receive_from( ... ); |
                                      | socket.async_send_to( ... );

但这被指定为不安全:

 thread_1 |线程_2
--------------------------------------------------+------------ ----------------------------
socket.async_receive_from( ... ); | socket.async_send_to( ... );
                                      |

请注意,某些函数,例如boost::asio::async_read,是一个组合操作,并且有额外的线程安全限制。


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

  • 所有套接字调用都发生在处理程序中,io_service::run() 仅从单个线程调用。
  • async_receive_fromasync_send_to 仅在同一异步操作链中调用。例如,传递给async_receive_fromReadHandler 调用async_send_to,传递给async_send_toWriteHandler 调用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 );  --'
    }
    

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


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


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

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

【讨论】:

  • 相当令人印象深刻的答案。我的情况是,我说只有一个 ioService 线程(主线程),但即使 async_receive_from/async_send_to 不能被交错调用,其中一个也可以在另一个完成之前被调用(另一个调用的完整处理程序)。这是因为我在另一个与 ioService 线程无关的线程中进行了处理。
  • 另一件事。它在哪里指定必须以特定方式处理端点类。一旦我在 async_receive_from 中接收到一个端点作为输出参数,我就会对其进行复制(使用复制构造函数)将它作为“状态”传递(以便我知道稍后在哪里响应),直到我需要发回响应。
  • 一个对象可以有多个异步操作挂起;只是指定对对象的并发调用是不安全的。如果从处理程序外部发生套接字调用并且只有一个线程正在调用io_service::run(),则将套接字调用发布到io_service 将同步。 Boost.Asio 要求调用者保证某些参数在调用处理程序之前保持有效;它从不强迫用户以特定方式管理它们。在某些情况下,通过智能指针将对象生命周期与异步链相关联会更容易。
  • asio-users 邮件列表中的帖子似乎证实了您所说的。我会将您的答案标记为已接受。
  • 虽然我在这里只是想问一下,如果您没有从完成句柄进行相应的异步调用,您是否可以通过这种方法使用 shared 以防出错留下你被删除的句柄?
猜你喜欢
  • 1970-01-01
  • 2017-07-13
  • 2015-02-21
  • 1970-01-01
  • 2013-11-19
  • 2021-09-05
  • 2014-11-25
  • 1970-01-01
  • 2021-06-13
相关资源
最近更新 更多