首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

喚醒被阻塞 Socket 函數的正確姿勢:shutdown vs close 實測對比

     編輯:關於C語言

問題現場

Linux 下 UDP 服務線程阻塞於 recvfrom(),主線程調用 close(fd) 並 pthread_join() 等待退出,**線程永不返回**,進程掛起。同樣發生於 read()、recv() 等阻塞調用。

根因:close 僅釋放描述符,不喚醒等待隊列

close() 只遞減文件描述符引用計數、釋放內核結構,**不會觸發等待在該 Socket 讀/寫隊列上的進程/線程被喚醒**。因此 recvfrom 繼續沉睡,pthread_join 永久等待。

解決方案:shutdown 破壞通道


#include <sys/socket.h>
int shutdown(int sockfd, int how);
// how: SHUT_RD(關閉讀), SHUT_WR(關閉寫), SHUT_RDWR(雙向關閉)

實測驗證**:在 pthread_join 前插入 shutdown(fd, SHUT_RD) 或 SHUT_RDWR,**服務線程立即從 recvfrom 返回 -1 (errno=ENOTCONN/ECONNRESET)**,隨後正常退出,主線程 join 成功。
關鍵結論**:必須關閉讀通道(SHUT_RD 或 SHUT_RDWR)才能喚醒 recv/recvfrom/read;僅關閉寫通道(SHUT_WR)無效。

對比總結

操作能否喚醒 recv/read能否喚醒 send/write備註
close(fd)否否僅釋放資源
shutdown(fd, SHUT_RD)是否丟棄接收緩衝區數據
shutdown(fd, SHUT_WR)否是發送 FIN,觸發對端 EOF
shutdown(fd, SHUT_RDWR)是是完全關閉

其他可行方案

  1. 設置超時:setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)),阻塞調用定時返回 EAGAIN/EWOULDBLOCK,輪詢退出標誌;
  2. 非阻塞 + I/O 多路復用:fcntl(sock, F_SETFL, O_NONBLOCK) 配合 select/poll/epoll,主循環可控退出;
  3. 信號中斷:向阻塞線程發送信號(需設置 SA_RESTART 行為),較複雜不推薦;
  4. Self-pipe Trick:創建管道,退出時向管道寫入字節,epoll 監聽管道可讀事件喚醒主循環。

工程建議

  • 優先使用 shutdown(fd, SHUT_RDWR) + pthread_join,代碼最少、語義清晰;
  • 新項目採用**非阻塞 + epoll/kqueue/IOCP** 統一事件循環,從架構層面消除阻塞退出難題;
  • 線程池任務採用**取消令牌/標誌位**協作式退出,避免強制取消導致資源洩漏。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved