网路编程时有时需要给建立连接过程添加时间控制,通常的过程是
1)建立套接字,调用fcntl设置非阻塞(O_NONBLOCK)选项
2)调用connect函数发起连接,判断errno是否是EINPROGRESS(也有可能直接建立连接成功),如果不是,说明出错
3)调用select函数监听套接字是否可写来判断连接是否建立
由于在第一步中,将套接字设置为非阻塞状态,可能有些人会疑惑,是否需要在select之前将套接字设置为阻塞状态?答案是不需要!因为select函数它只会检查监听套接字或文件描述符的可读、可写或异常的状态,套接字是否阻塞不会影响select函数的功能(现在还没看过select函数的源码,个人认为select应该不会检查是否阻塞的选项)。
为了验证上面的结论,写了下面一个测试程序,仅供参考:
#include <stdio.h> #include <fcntl.h> #include <sys/select.h> #include <unistd.h> #include <fcntl.h> #include <stdlib.h> #include <sys/socket.h> #include <sys/types.h> #include <string.h> #include <netinet/in.h> #include <errno.h> int main(void) { int connfd; int fdflag; int ret; fd_set wset; struct timeval outtime; struct sockaddr_in sa; connfd = socket(AF_INET, SOCK_STREAM, 0); if (connfd < 0) { perror("socket"); exit(EXIT_FAILURE); } /* set nonblock mode */ fdflag = fcntl(connfd, F_GETFL); if (fdflag < 0) { perror("fcntl F_GETFL"); exit(EXIT_FAILURE); } fdflag |= O_NONBLOCK; if (fcntl(connfd, F_SETFL, fdflag) < 0) { perror("fcntl F_SETFL"); exit(EXIT_FAILURE); } bzero(&sa, sizeof (sa)); sa.sin_family = AF_INET; sa.sin_port = htons(80); /* ip not exist */ inet_pton(AF_INET, "74.126.71.102", &sa.sin_addr); if (connect(connfd, (struct sockaddr *)&sa, sizeof (sa)) < 0) { if (errno != EINPROGRESS) { perror("connect"); exit (EXIT_FAILURE); } } bzero(&outtime, sizeof (outtime)); outtime.tv_sec = 10; FD_ZERO(&wset); FD_SET(connfd, &wset); if ((ret = select (connfd + 1, NULL, &wset, NULL, &outtime)) <= 0) { if (ret) { perror("select"); } else { fprintf(stderr, "select: timed out\n"); } exit(EXIT_FAILURE); } if (connect(connfd, (struct sockaddr *)&sa, sizeof (sa)) < 0) { if (errno != EISCONN) { perror("connect"); exit(EXIT_FAILURE); } else { fprintf(stdout, "connect success!\n"); } } exit(0); }