正确的方法来关闭阻塞的UDP套接字

Mül*_*sik 9 c++ sockets

我有一个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;
Run Code Online (Sandbox Code Playgroud)

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

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

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

bla*_*aze 5

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

从不同线程关闭套接字的便携方式是不在recv()中创建管道和块,而是在select()中.当你需要关闭套接字时,写一些东西到管道,select()将解除阻塞,你可以安全地关闭().


cni*_*tar 1

那么recvfrom本身就是线程安全的。IIRC 所有套接字功能都是。问题是:

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

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

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

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

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

就我个人而言,我会UP在 之后对信号量执行 an 操作,在 之前执行recvfroma操作。DOWNclose