1. 程式人生 > >C socket: 關於connect超時設定

C socket: 關於connect超時設定

使用阻塞的socket, 可以設定讀寫超時,
struct timeval tv_timeout;
tv_timeout.tv_sec = 60;
tv_timeout.tv_usec = 0;
if (setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, (void *) &tv_timeout, sizeof(struct timeval)) < 0) {
    perror("setsockopt");
}
tv_timeout.tv_sec = 60;
tv_timeout.tv_usec = 0;
if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (void *) &tv_timeout, sizeof(struct timeval)) < 0) {
    perror("setsockopt");
}

但是這個不會影響connect.
如何設定connect超時呢, 通過訊號alarm? 感覺不是一個好的辦法.比較好的辦法是通過select或者poll判斷超時.
首先設定socket fd為非阻塞, connect判斷返回值, 如果返回0, 說明connect成功, 如果返回值等於-1並且錯誤的errno為EINPROGRESS時呼叫select或者poll判斷socket fd的可寫狀態, 通過select或者poll的超時設定來判斷是否超時.
man page是這麼寫的

EINPROGRESS
The socket is non-blocking and the connection cannot be com-
pleted immediately. It is possible to select(2) or poll(2) for
completion by selecting the socket for writing. After select(2)
indicates writability, use getsockopt(2) to read the SO_ERROR
option at level SOL_SOCKET to determine whether connect() com-
pleted successfully (SO_ERROR is zero) or unsuccessfully
(SO_ERROR is one of the usual error codes listed here, explain-
ing the reason for the failure).

下邊是示例程式碼:

int opt = 1;
//set non-blocking
if (ioctl(sockfd, FIONBIO, &opt) < 0) {
    close(sockfd);
    perror("ioctl");
    exit(0);
}

if (connect(sockfd, (struct sockaddr *) &server_addr, sizeof(struct sockaddr)) == -1) {
    if (errno == EINPROGRESS) {
        int error;
        int len = sizeof(int);
        tv_timeout.tv_sec  = 60; 
        tv_timeout.tv_usec = 0;
        FD_ZERO(&set);
        FD_SET(sockfd, &set);
        if(select(sockfd + 1, NULL, &set, NULL, &tv_timeout) > 0) {
            getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&len);
            if(error != 0) {
                close(sockfd);
                exit(0);
            }
        } else { //timeout or select error
            close(sockfd);
            exit(0);
        }
    } else {
        close(sockfd);
        perror("connect");
        exit(0);
    }
}

opt = 0;
//set blocking
if (ioctl(sockfd, FIONBIO, &opt) < 0) {
    close(sockfd);
    perror("ioctl");
    exit(0);
}