Linux 下 UDP 服務線程阻塞於 recvfrom(),主線程調用 close(fd) 並 pthread_join() 等待退出,**線程永不返回**,進程掛起。同樣發生於 read()、recv() 等阻塞調用。
close() 只遞減文件描述符引用計數、釋放內核結構,**不會觸發等待在該 Socket 讀/寫隊列上的進程/線程被喚醒**。因此 recvfrom 繼續沉睡,pthread_join 永久等待。
#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) | 是 | 是 | 完全關閉 |
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)),阻塞調用定時返回 EAGAIN/EWOULDBLOCK,輪詢退出標誌;fcntl(sock, F_SETFL, O_NONBLOCK) 配合 select/poll/epoll,主循環可控退出;SA_RESTART 行為),較複雜不推薦;shutdown(fd, SHUT_RDWR) + pthread_join,代碼最少、語義清晰;