一、知識預覽
1)超時控制的概念引入
2)使用setsockopt設定超時控制
3)使用select設定超時控制
4)使用SIGALRM和alarm設定超時控制
5)指數補償法的超時連線機制
二、概念引入
① 什麼是網路超時控制
超時實際上是一種介於阻塞與非阻塞之間的折中等待的方案:
阻塞操作,意味著如果條件不滿足時需要一直等待,等待的最長時間是:永久等待
非阻塞操作,意味著如果條件不滿足時立即放棄,等待最長時間是:0
以上兩種方式都比較極端,有時候我們需要等待某個資源,但又不想等太久,這時就需要用到所謂超時控制:
超時控制,意味著如果條件不滿足時等待時間可以自定義為:n
超時控制可以理解為主要分為兩種情況:
a.連線超時
b.讀取資料超時
不管是TCP還是UDP,還是別的型別的套接字,他們本身就有超時屬性。
② 超時屬性
超時屬性就是用來實現超時連線的一個概念。
如何設定超時屬性實現超時控制,有以下方法:
直接使用setsockopt設定套接字的超時屬性,簡單易用,童叟無欺。
利用 select 函式自帶的超時屬性,群體控制,也挺好用。
利用 SIGALRM 和它的搭檔 alarm() 函式,自定鬧鐘,也能達到目的。
三、 使用setsocketopt設定超時屬性
struct timeval tv; //設定超時時間為3秒
tv.tv_sec = 3;
tv.tv_usec = 0;
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
接字sockfd被設定了接收超時(SO_RCVTIMEO)為3秒,這意味著:
如果 sockfd 是一個待連線監聽套接字,那麼其等待對端連線最多等3秒鐘。---連線超時
如果 sockfd 是一個已連線套接字,那麼其等待對端資料最多等3秒鐘。 ---讀取資料超時控制
四、 使用select設定超時屬性
在多路複用IO中,函式 select() 也可以在多路監聽的同時設定超時,設定超時屬性程式碼如下:
struct timeval tv;
tv.tv_sec = 3;
tv.tv_usec = 0;
select(maxfd+1, &rset, NULL, NULL, &tv);
以上select設定了監聽對端資料最多等待3秒。
五、 利用 SIGALRM 和 alarm()設定超時屬性
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>
#define SERVER_IP "127.0.0.1"
#define SERVER_PORT 12345
#define TIMEOUT_SECONDS 5
int sockfd;
void timeout_handler(int signum) {
printf("Connection timeout. Closing socket.\n");
close(sockfd); // 關閉套接字
exit(1);
}
int main() {
struct sockaddr_in server_addr;
// 建立套接字
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("Socket creation failed");
exit(1);
}
// 設定伺服器地址結構
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(SERVER_PORT);
server_addr.sin_addr.s_addr = htonl(SERVER_IP);
// 設定SIGALRM訊號的處理函式
signal(SIGALRM, timeout_handler);
// 設定超時時間
alarm(TIMEOUT_SECONDS);
// 進行連線
if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
if (errno == EINTR) {
printf("Connect operation timed out.\n");
} else {
perror("Connect failed");
}
close(sockfd); // 關閉套接字
exit(1);
}
// 如果連線成功,取消定時器
alarm(0);
printf("Connected successfully!\n");
// 此處可以繼續進行資料交換或其他操作
close(sockfd);
return 0;
}
六、指數補償法實現超時連線
1)超時連線的問題引入
#define MAX_SLEEP 128
for(int num=1; num<=MSX_SLEEP; num<<=1)
{
if(connect() == 0)
{
break;
}
if(num <= MSX_SLEEP/2)
sleep(num);
}