UDP 無連線、不可靠。雙方若都阻塞在 recvfrom,一方封包遺失將導致雙向永久阻塞。需在應用層實現超時重傳機制。
alarm() + 信號 SIGALRMSO_RCVTIMEO/SO_SNDTIMEO (需核心支援)select()/poll()/epoll() 多路複用本文採用方案 1,配合 sigaction 精確控制信號行為。
sigaction 旗標 SA_NODEFER (舊稱 SA_NOMASK)預設信號處理函數執行期間會阻塞同一信號,且返回後**自動重啟**被中斷的系統呼叫 (如 recvfrom),導致超時無效。需設定 SA_NODEFER (不阻塞自身信號) 並**不設定** SA_RESTART,使系統呼叫以 EINTR 失敗返回,由應用層處理重傳邏輯。
#include <signal.h>
#include <unistd.h>
#include <sys/socket.h>
void alarmed(int signo) {
// 發送特殊封包通知對端重傳
sendto(sockfd, "RETRANSMIT", 10, 0, (sockaddr*)&peer, sizeof(peer));
}
int main() {
struct sigaction act = {};
act.sa_handler = alarmed;
act.sa_flags = SA_NODEFER; // 關鍵:不阻塞 SIGALRM,不自動重啟系統呼叫
sigaction(SIGALRM, &act, NULL);
for (;;) {
alarm(60); // 60 秒超時
ssize_t n = recvfrom(sockfd, buf, sizeof(buf), 0, (sockaddr*)&peer, &len);
if (n < 0) {
if (errno == EINTR) continue; // 超時觸發,alarm handler 已發送重傳請求
perror("recvfrom");
}
// 處理正常收到的資料...
}
}
SIGALRM 處理函數 alarmed,旗標 SA_NODEFER。recvfrom 前呼叫 alarm(60)。SIGALRM,recvfrom 返回 -1, errno=EINTR。alarmed 發送協定約定的「重傳請求」封包給對端。EINTR 繼續 recvfrom 等待重傳資料。epoll + 定時器堆 或專用網路庫 (libuv, ASIO, muduo)。alarmed 中僅呼叫 sendto (async-signal-safe),避免非安全函數 (如 malloc, printf)。