2011-06-10 109 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