在Linux系统中,当使用非阻塞的connect函数尝试建立网络连接时,如果连接不能立即完成(例如,因为需要等待网络响应),connect函数会返回-1并设置错误码EINPROGRESS。这表示连接操作正在进行中,但尚未完成。
处理EINPROGRESS错误码的一般步骤包括:
设置套接字为非阻塞模式:
在调用connect之前,确保套接字已被设置为非阻塞模式。这通常通过fcntl函数或使用O_NONBLOCK标志在socket调用时完成。
使用select、poll或epoll等待连接完成:
一旦connect返回-1并设置EINPROGRESS,你需要使用select、poll或epoll等系统调用来等待套接字变得可写(对于connect来说,这意味着连接已建立或出错)。
检查套接字的状态:
当select、poll或epoll指示套接字可写时,你需要再次检查套接字的错误状态。这通常通过调用getsockopt函数并查询SO_ERROR选项来完成。如果getsockopt返回0,则表示连接成功;如果返回其他错误码,则表示连接失败,并且该错误码指示了失败的原因。
处理连接结果:
根据getsockopt返回的结果,你可以决定是继续处理成功的连接,还是关闭套接字并处理连接失败的情况。
下面是一个使用select来处理非阻塞connect的示例代码片段:
c复制代码
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
int main() {
int sockfd;
struct sockaddr_in server_addr;
fd_set write_fds;
struct timeval timeout;
int result;
socklen_t len = sizeof(int);
int err;
// 创建套接字
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("socket");
return 1;
}
// 设置套接字为非阻塞模式
fcntl(sockfd, F_SETFL, O_NONBLOCK);
// 设置服务器地址
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(80);
inet_pton(AF_INET, "93.184.216.34", &server_addr.sin_addr); // 示例IP地址
// 发起非阻塞连接
if (connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
if (errno != EINPROGRESS) {
perror("connect");
close(sockfd);
return 1;
}
}
// 设置select超时时间
timeout.tv_sec = 5; // 5秒超时
timeout.tv_usec = 0;
// 等待连接完成
FD_ZERO(&write_fds);
FD_SET(sockfd, &write_fds);
result = select(sockfd + 1, NULL, &write_fds, NULL, &timeout);
if (result < 0) {
perror("select");
close(sockfd);
return 1;
} else if (result == 0) {
fprintf(stderr, "Connect timed out\n");
close(sockfd);
return 1;
}
// 检查连接结果
if (FD_ISSET(sockfd, &write_fds)) {
getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void*)&err, &len);
if (err == 0) {
printf("Connected successfully\n");
// 继续处理连接...
} else {
errno = err;
perror("connect");
close(sockfd);
return 1;
}
}
// 关闭套接字(在实际应用中,你可能不会在这里关闭它)
close(sockfd);
return 0;
}
在这个示例中,我们创建了一个非阻塞的套接字,并尝试连接到指定的服务器。如果connect返回-1并设置EINPROGRESS,我们使用select等待套接字变得可写。然后,我们使用getsockopt检查连接的结果,并根据结果决定是继续处理连接还是关闭套接字。