網路超時控制 + 指數補償法超時連線

WJnuHhail發表於2024-08-20

一、知識預覽

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)超時連線的問題引入

image

#define MAX_SLEEP 128


for(int num=1; num<=MSX_SLEEP; num<<=1)
{
    if(connect() == 0)
    {
        break;    
    }
    if(num <= MSX_SLEEP/2)
        sleep(num);
}

image


2)可支援遷移的重連程式碼

image

image

相關文章