【问题标题】:Proper way to close a blocking UDP socket关闭阻塞 UDP 套接字的正确方法
【发布时间】:2011-06-10 11:03:41
【问题描述】:

我有一个 C++ 对象,它创建一个线程以从阻塞的 UDP 套接字中读取:

mRunning.store(true);
while (mRunning.load(boost::memory_order_consume)) {
    ...
    int size = recvfrom(mSocket, buf, kTextBufSize , 0,
                        (struct sockaddr *) &packet->mReplyAddr.mSockAddr, (socklen_t*)&packet->mReplyAddr.mSockAddrLen);

    if (size > 0) {
        //do stuff
    }
}
return 0;

(mRunning 是一个 boost::atomic) 从另一个线程调用对象的析构函数并执行以下操作:

mRunning.store(false);  
#ifdef WIN32
    if (mSocket != -1) closesocket(mSocket);
#else
    if (mSocket != -1) close(mSocket);
#endif
pthread_join(mThread, NULL);

这似乎可行,但我的一位同事建议如果 recv 在读取某些内容的过程中被打断,可能会出现问题。这个线程安全吗?关闭阻塞 UDP 套接字的正确方法是什么? (需要跨平台OSX/Linux/Windows)

【问题讨论】:

    标签: c++ sockets


    【解决方案1】:

    可能会有很多不同的问题。将我的应用程序从一个 FreeBSD 版本移动到另一个版本,我发现这样的 close() 在旧内核上正常工作,并且只是挂起 close() 直到从新版本的 recv() 返回一些东西。 OSX 是基于 FreeBSD 的 :)

    从不同线程关闭套接字的可移植方法是创建管道和块,而不是在recv()中,而是在select()中。当你需要关闭套接字时,向管道写入一些东西,select() 将解除阻塞,你可以安全地执行 close()。

    【讨论】:

    • 我认为这实际上不会跨平台工作,因为 Windows 上的 select() 似乎只支持套接字,而不支持管道。
    • 在windows中你可以有一个单独的代码来使用WSAEventSelect()和WaitForMultipleObjects()。
    【解决方案2】:

    recvfrom 本身线程安全的。 IIRC所有的socket函数都是。问题是:

    • 如果在将数据复制到缓冲区时从recvfrom 下提取描述符会发生什么?

    这是一个好问题,但我怀疑标准对此是否有任何说明(我也怀疑实施的具体手册对此有何说明)。所以任何实现都是免费的:

    • 完成操作(可能是因为它不再需要描述符,或者因为它正在执行一些很酷的引用计数或其他操作)
    • 使recvfrom 失败并返回-1ENOTSOCKEINVAL?)
    • 由于缓冲区和内部数据结构被close 释放,导致严重崩溃。

    显然这只是推测(我以前错了很多次),但除非您在标准中找到支持您可以在通过套接字接收时关闭套接字的想法,否则您'不安全。

    那么,你能做什么?最安全的:使用同步机制来确保在recvfrom 完成后您只使用close 套接字(信号量、互斥锁等)。

    我个人会在recvfrom 之后的信号量上执行UP,在close 之前执行DOWN

    【讨论】:

      【解决方案3】:

      您的同事是对的,boost 套接字不是线程安全的。

      您的选择;

      1. 使用 ASIO(做这个)
      2. 阻塞调用超时。尽管它可能有效,但它并不是真正可移植的。

      【讨论】:

      • 它们不是 boost 套接字,它们是 posix 套接字。 Boost 只是为了确保更改 mRunning bool 是原子的。抱歉,当我把它粘贴进去的时候,我没有注意到它被破坏了。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-02-27
      • 1970-01-01
      • 2011-05-19
      • 1970-01-01
      • 1970-01-01
      • 2010-12-30
      • 2015-07-30
      相关资源
      最近更新 更多