2009-01-19 24 views
8

我正在使用Linux系統(使用2.6.20內核的Ubuntu 7.04服務器)。選擇UDP套接字在套接字關閉時不會結束 - 我做錯了什麼?

我有一個程序,有一個線程(thread1)等待UDP套接字的選擇變得可讀。 我使用select(與我的套接字作爲單個readfd和單個exceptfd),而不是僅僅調用recvfrom,因爲我想要超時。

從另一個線程中,我關閉並關閉了套接字。 如果我在recvfrom中阻止thread1時執行此操作,recvfrom將立即終止。 如果我這樣做,而thread1被阻塞在一個超時選擇,那麼選擇不會立即終止,但最終會正常超時。

任何人都可以告訴我爲什麼選擇不會在套接字關閉時退出?這不是例外嗎?我可以看到它不可讀(顯然),但是它是封閉的,這似乎是可以忽略的。

這裏的插座的開口(處理去除所有的錯誤讓事情變得簡單):

m_sockfd = socket(PF_INET, SOCK_DGRAM, 0); 
struct sockaddr_in si_me; 
memset((char *) &si_me, 0, sizeof(si_me)); 
si_me.sin_family = AF_INET; 
si_me.sin_port = htons(port); 
si_me.sin_addr.s_addr = htonl(INADDR_ANY); 
if (bind(m_sockfd, (struct sockaddr *)(&si_me), sizeof(si_me)) < 0) 
{ 
// deal with error 
} 

這裏的SELECT語句線程1執行:

struct timeval to; 
to.tv_sec = timeout_ms/1000;// just the seconds portion 
to.tv_usec = (timeout_ms%1000)*1000;// just the milliseconds 
            // converted to microseconds 

// watch our one fd for readability or 
// exceptions. 
fd_set readfds, exceptfds; 
FD_ZERO(&readfds); 
FD_SET(m_sockfd, &readfds); 
FD_ZERO(&exceptfds); 
FD_SET(m_sockfd, &exceptfds); 

int nsel = select(m_sockfd+1, &readfds, NULL, &exceptfds, &to); 

更新:顯然,(如下所述),關閉套接字並不是一個例外情況(從select的角度來看)。我想我需要知道的是:爲什麼?而且,這是故意的嗎?

我真的很想理解這個選擇行爲背後的想法,因爲它似乎與我的期望相反。因此,我顯然需要調整我對TCP堆棧如何工作的思考。請給我解釋一下。

回答

4

也許你應該使用別的東西來喚醒選擇。也許是管道或類似的東西。

+0

這將是一個很好的解決方案。在套接字和管道上都選擇等待,另一個線程將寫入管道以使選擇返回。 – 2009-01-26 19:57:49

4

UDP是無連接協議。由於沒有聯繫,所以沒有任何聯繫可以被破壞,所以消費者不知道生產者永遠不會再發送。

您可以讓生產者發送「流結束」消息,並讓消費者在收到消息後終止。

+1

正確的,真實的和可行的解決辦法,但:當我關閉套接字recvfrom的DOES終止。爲什麼不選擇?我真的很困惑,爲什麼關閉套接字不會產生終止select的異常。 – 2009-01-19 18:29:50

+0

我相信你誤解了這個問題:兩個線程並沒有操縱兩個UDP套接字。有**只有一個UDP套接字**。 – curiousguy 2011-10-09 01:24:33

2

我認爲最明顯的解決方案是關閉不被視爲特殊情況。我認爲問題的根源在於,你並不真正擁抱select的理念。你爲什麼在另一個線程中擺弄插座,這聽起來像是一場災難。

+0

我從另一個線程處理套接字的唯一方法就是關閉它,用於停止正在進行的等待。這樣我可以讓我的程序在選擇超時到期之前退出。 – 2009-01-26 18:56:20

2

我會說不同的是,recvfrom的積極嘗試從單一的插座,其中選擇等待消息的到達,可能在多個句柄讀郵件,而不必套接字句柄。

3

你能不發送信號(例如USR2),以這將導致選擇()與EINTR返回線程? 然後在信號處理程序中設置一個標誌,告訴它不要重新啓動select()?

這將消除需要等待多個文件描述符,而且似乎比使用管道將其殺死清潔了許多。

0

您的代碼基本上已損壞。這種錯誤的變化是常見的,過去曾造成嚴重的錯誤,並帶來巨大的安全隱患。這裏是你缺少的:

當你關閉套接字時,根本沒有辦法知道另一個線程是被阻塞在select還是要阻塞在select。例如,請考慮以下幾點:

  1. 線程去調用select,但沒有得到調度。
  2. 您關閉套接字。
  3. 在一個線程代碼是不知道的(也許是該平臺的內部內存管理或記錄內部的一部分)庫打開插座,並得到相同的標識符,你關閉了套接字。
  4. 線程現在進入select,但它是荷蘭國際集團select由庫開在插座上。
  5. 災難降臨。

當另一個線程正在或可能正在使用它時,您不能嘗試釋放資源。