默认情况下,系统为我们分配的套接字(文件描述符)是阻塞的,我们使用阻塞的套接字(文件描述符)执行connect、read、write、accept等函数的话,会阻塞一段系统默认的超时时间,当然这个超时时间可以通过setsockopt函数的相关参数进行设置,但不论如何都会阻塞一段这个指定的时间。如果您的程序是单线程,或者该线程需要执行多任务,一旦阻塞这就不能执行其他任务了,这并不是我们希望看到的,那么有没有办法让基于该套接字(文件描述符)的动作(如connect)立刻返回呢?答案是肯定的。
如果我们将套接字(文件描述符)设置为非阻塞模式,那么执行句居如connect这样默认阻塞的函数时,除非能够立马返回结果,否则系统会立刻令其返回一个错误值并接管该函数的最终运行结果。然会立刻返回的问题解决了,但是仍给系统以后的函数,我们如何去获得其最终执行结果呢?我们可以使用select来监控该套接字(文件描述符),让它帮我们与系统交互,以获得函数的最终执行结果。
下面就给出一个基于非阻塞套接字的connect函数实例。
#include <stdio.h> #include <types.h> #include <time.h> #define SLEEP_TIME 2 #define PORT_UP 8848 #define CPPLIVE "192.168.1.100" int main(void) { int error, ret; int fd_num; int new_fd; int non_blocking = 1; fd_set wtitefds; struct timeval select_timeval; struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(PORT_UP); addr.sin_addr.s_addr = inet_addr(CPPLIVE); if ((new_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("socket"); return -1; } //Set Non-blocking if (ioctl(node->remoteFd, FIONBIO, &non_blocking) < 0) perror("ioctl"); ret = connect(new_fd, (struct sockaddr *)&addr, sizeof(addr)); if ( 0 == ret) { printf("connect to %s successed!\n", CPPLIVE); return 0; } if (-1 == ret && errno != EINPROGRESS) { perror("connect"); close(new_fd); return -1; } while (1) { FD_ZERO(&wtitefds); FD_SET(new_fd, &wtitefds); fd_num = select(new_fd+1, NULL, &wtitefds, NULL, &select_timeval); if (fd_num < 0) { perror("select"); continue; } else if (0 == fd_num) { printf("select timeout...\n"); //resert the select_time after timeout select_timeval.tv_sec = SLEEP_TIME; select_timeval.tv_usec = 0; //you can add some task here, so the task could work a time every SLEEP_TIME continue; } if (FD_ISSET(new_fd, &wtitefds)) { printf("the connect have done\n"); if (getsockopt(new_fd, SOL_SOCKET, SO_ERROR, &error, &error_len) != 0) { perror("getsockopt"); return -1; } if (error) { perror("connect"); return -1; } else { printf("connect to %s successed!\n", CPPLIVE); return 0; } } } return 0; }
除非注明,文章均为CppLive 编程在线原创,转载请注明出处,谢谢。