C socket: 關於connect超時設定
阿新 • • 發佈:2019-01-07
使用阻塞的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); }