2011-06-10 52 views
9

我有一个创建一个线程阻塞UDP套接字读取一个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 ::原子) 对象的析构函数被调用从另一个线程,并做到这一点:

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)

回答

1

好吧recvfrom本身线程安全。 IIRC所有套接字函数都是。问题是:

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

这是一个好问题,但我怀疑的标准说这事(我也怀疑一个具体的实施手册说任何事情)。因此,任何应用程序可自由:

  • 完成操作(也许是因为它不需要描述符了,或者是因为它做一些很酷的引用计数或别的东西)
  • 充分利用recvfrom失败,并返回-1ENOTSOCK,EINVAL?)
  • 由于缓冲区和内部数据结构被close释放,导致崩溃。

显然,这只是猜测(我一直是错之前,多次),但除非你发现在标准的东西来支持,同时通过它接收,你可以关闭套接字的想法,你不安全。

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

就我个人而言,我会在recvfromclose之前的之后对信号量做一个UP

0

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

您的选择;

  1. 使用ASIO(这样做的)
  2. 超时阻断通话。尽管它可能工作,但这不是真正的便携式。
+2

他们不是增强套接字,他们是posix套接字。 Boost只是为了确保改变mRunning布尔是原子的。对不起,当我粘贴它,我没有注意到。 – 2011-06-10 12:55:37

5

可能有很多不同的问题。将我的应用程序从一个FreeBSD版本移动到另一个FreeBSD版本时,我发现close()在较早的内核上正常工作,只需挂上close(),直到从较新的recv()返回一些内容。而OSX是基于FreeBSD的:)

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

+1

我不认为这会实际上跨平台的工作,因为Windows上的select()似乎只支持套接字,而不是管道。 – 2011-06-10 19:15:39

+1

在Windows中,您可以有一个单独的代码,它将使用WSAEventSelect()和WaitForMultipleObjects()。 – blaze 2011-06-14 09:59:01