摘要:
本文介紹在套接字的I/O操作上設置超時的三種方法。

圖片可能有點寬,看不到的童鞋可以點擊圖片查看完整圖片。。
使用SIGALRM為connect設置超時
設置方法:
void handle_msg(int sockfd) { char sendbuf[BUFSIZE]; char recvbuf[BUFSIZE]; signal(SIGALRM, sig_alrm); //監聽SIGALRM信號 while(1) { memset( sendbuf, '/0', BUFSIZE ); memset( recvbuf, '/0', BUFSIZE ); PRintf("%s", "send msg:"); gets(sendbuf); if (strlen(sendbuf) > 0) send(sockfd,sendbuf,strlen(sendbuf),0); if ( !strcmp(sendbuf, "exit")) break; alarm(5); //設置超時事件為5s,同時設置服務器回射前sleep 10秒,以讓recv函數超時 if (recv(sockfd,recvbuf,BUFSIZE,0) > 0) { alarm(0); printf("recv back:%s/n/n", recvbuf); } else { if (errno == EINTR) fprintf(stderr, "socket timeout/n"); else fprintf(stderr, "receive error/n"); } } close( sockfd ); return;}static void sig_alrm(int signo) { fprintf(stderr, "recv SIGALRM, return./n"); return;}運行截圖:

雖然設置了SIGALRM信號處理函數,但是如圖所示,本例依然可以等待讀取回射信息,因為信號處理函數里只是return。
設置方法:
使用select的內置時間限制,阻塞在select代替recv函數的阻塞。
void handle_msg(int sockfd) { char sendbuf[BUFSIZE]; char recvbuf[BUFSIZE]; while(1) { memset( sendbuf, '/0', BUFSIZE ); memset( recvbuf, '/0', BUFSIZE ); printf("%s", "send msg:"); gets(sendbuf); if (strlen(sendbuf) > 0) send(sockfd,sendbuf,strlen(sendbuf),0); if ( !strcmp(sendbuf, "exit")) break; if (readable_timeo(sockfd, 5) == 0) { fprintf(stderr, "socket timeout/n"); } else{ recv(sockfd,recvbuf,BUFSIZE,0); printf("recv back:%s/n/n", recvbuf); } } close( sockfd ); return;}int readable_timeo(int fd, int sec) { fd_set rset; struct timeval tv; FD_ZERO(&rset); FD_SET(fd, &rset); tv.tv_sec = sec; tv.tv_usec = 0; return select(fd+1, &rset, NULL, NULL, &tv);}
運行截圖:

由運行截圖可以看到,超時警告運行正常。
需要注意一點的是,雖然客戶端在超時之后繼續發送消息,但是服務器回射的消息(hello world)依然被接收,這導致我們再次發送消息時,從緩沖區中讀出了延遲收到的hello world。
這里只是演示超時技術,因此對此并不做進一步處理。
使用SO_RCVTIMEO套接字選項為recv設置超時
設置方法:
void handle_msg(int sockfd) { char sendbuf[BUFSIZE]; char recvbuf[BUFSIZE + 1]; int n; struct timeval tv; tv.tv_sec = 5; tv.tv_usec = 0; setsockopt( sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv) ); while(1) { memset( sendbuf, '/0', BUFSIZE ); memset( recvbuf, '/0', BUFSIZE ); printf("%s", "send msg:"); gets(sendbuf); if (strlen(sendbuf) > 0) send(sockfd,sendbuf,strlen(sendbuf),0); if ( !strcmp(sendbuf, "exit")) break; if ( (n=recv(sockfd,recvbuf,BUFSIZE,0)) < 0 ) { if (errno == EWOULDBLOCK) { fprintf(stderr, "socket timeout/n"); continue; } else fprintf(stderr, "recv error"); } else{ printf("recv back:%s/n/n", recvbuf); } } close( sockfd ); return;}運行截圖:

可以看到,超時警報成功運行,但依然有上一例中的延遲接收的情況發生,不作處理。
示例源碼上傳到了github上,地址:https://github.com/zs634134578/UNP/tree/tryTimeout
參考資料:
《UNIX網絡編程 卷1:套接字聯網API(第3版)》
新聞熱點
疑難解答