如何喚醒socket被阻塞的函式

2021-09-03 10:23:10 字數 2389 閱讀 7543

最近專案遇到乙個問題,程式退出的時候資源沒有正常釋放。經過除錯發現,原來是網路執行緒一直阻塞,導致一些必要的資源沒有被釋放,寫了幾個簡單的測試程式除錯了一下才明白,原來在linux下直接close socket的檔案描述符,並不會使程式中呼叫的一些阻塞式的socket函式(比如 read、recvfrom 等)退出阻塞,從而導致無法正常釋放資源。簡化示例如下。

下面是乙個簡化的udp服務程式,首先建立socket物件,然後開啟服務執行緒,將客戶端傳送過來的資料報回發給客戶端。當使用者在shell中敲入兩次回車後,程式退出。我們來觀察一下程式退出後,socket服務執行緒在怎樣的情況下可以正常退出。

#include 

#include 

#include 

#include 

#include 

#include 

#define server_port 8888 

#define buffer_len  256 

int g_exit = 0;  

void *service( void* arg )  

printf("ok, service thread exit!\n");  

pthread_exit(null);;  

}  int main( int argc,char * argv )  

// free attribute 

pthread_attr_destroy(&attr);  

// wait user control exit

getchar();  

getchar();  

g_exit = 1;      

printf("ok, waiting for thread exit...!\n");  

close(fd);

// wait for thread exit

pthread_join(thr, &status);

printf("ok, exit main process !\n");

return 0;  

}  

上述程式,當使用者敲兩次回車後,顯示結果如下:

可以看到,沒有打出主程序和服務執行緒的退出資訊,無論是主程序還是服務執行緒都沒有正常退出,由此可見,直接close socket控制代碼,並不能使 recvfrom 函式退出阻塞。

那麼,如果把 pthread_join 換成 pthread_cancel 呢?結果是一樣的,雖然主程序退出了,但依然無法讓 service 執行緒正常退出。那麼,該如何才能正常退出 recvfrom 的阻塞呢?

網上搜了一下,可以考慮使用 shutdown 函式。

//shutdown函式原型為: 

#include 

int shutdown(int s, int how);  

//shutdown() 可以對套接字的關閉進行更細緻的控制,它允許對套接字進行單向關閉或全部禁止。 

//引數 s 為待關閉的套接字描述符。 

//引數 how 指定了關閉方式,具體取值如下: 

//shut_rd : 將連線上的讀通道關閉,此後程序將不能再接收到任何資料,接收緩衝區中還未被讀取的資料也將被丟棄,但仍然可以在該套接字上傳送資料。 

//shut_wr : 將連線上的寫信道關閉,此後程序將不能再傳送任何資料,傳送緩衝區中還未被傳送的資料也將被丟棄,但仍然可以在該套接字上接收資料。 

//shut_rdwr : 讀、寫信道都將被關閉。 

//執行成功返回 0,出錯則返回 -1,錯誤**存入 errno 中。 

可以看到,service服務執行緒已經正常退出了。進一步測試,如果只是shutdown寫信道或者只shutdown讀通道呢?

經過測試可以發現,如果只關閉寫信道 shutdown(fd,shut_wr); 服務執行緒依然無法正常退出,而如果只關閉讀通道 shutdown(fd,shut_rd),則服務執行緒正常退出了。分析如下:因為recvfrom在fd的讀通道等待列表中,因此必須關閉讀通道時才能將recvfrom阻塞喚醒。

那麼,為啥shutdown就可以使得recvfrom退出阻塞,而close卻不能呢?

我的理解如下:shutdown破壞了socket連線的讀寫通道,導致讀寫阻塞的socket函式被喚醒,而close函式只是做了關閉連線釋放socket資源的操作,卻並沒有進行讀寫通道的清理工作,從而無法成功喚醒讀寫函式的阻塞。(期待高手給出更深層次的解釋)

進一步,那麼,解決這一問題,還有其他的什麼辦法沒有?

下面我簡單地羅列一下網上搜到的可行的一些方法,以後有時間再深入研究:

1.  設定socket傳送/接收超時

2.  使用非阻塞方式,非同步socket模型

如何停止被BlockingQueue阻塞的執行緒

如下阻塞佇列 執行緒的 很常見,當服務停止時,如何停止被blockingqueue阻塞的執行緒?blockingqueue blockingqueue newarrayblockingqueue 10 final thread thread newthread new runnable catch ...

阻塞 非阻塞socket的理解

b 阻塞socket是這樣的 b recv socket1,buf,length 去網絡卡緩衝區讀取socket1的資料,讀到的資料儲存到buf 如果網絡卡緩衝區有1個位元組,就返回1個,有兩個就返回兩個,當然不能超過length 如果網絡卡緩衝區沒有資料,那麼就一直等待,直到有資料可讀 是的,很傻...

程序的阻塞 喚醒 掛起 啟用

程序控制的一些概念 程序控制是程序管理中最基本的功能。建立 終止 可負責程序執行中的狀態轉換。程序控制一般是由os的核心中的原語來實現的。原語 primitive 是由若干條指令組成的,用於完成一定功能的乙個過程。它與一般過程的區別在於 它們是 原子操作 action operation 即不可分割...