1 #include <unistd.h> 2 #include<stdio.h> 3 #include <signal.h> 4 #include <sys/time.h> 5 #include <sys/socket.h> 6 #include <fcntl.h> 7 #include <arpa/inet.h> 8 #include <string.h> 9 10 typedef int TINTEGER; 11 12 TINTEGER g_fdSocket; // Socket句柄 13 struct sockaddr_in g_localAddr; // 本端地址 14 15 16 void ALARMProc(int isig) 17 { 18 printf("12345678219\n"); 19 } 20 21 int main() 22 { 23 sigset(SIGALRM, ALARMProc); 24 25 struct timeval timenext; 26 struct timeval timeinterval; 27 timenext.tv_sec = 10; 28 timenext.tv_usec = 0; 29 timeinterval.tv_sec = 10; 30 timeinterval.tv_usec = 0; 31 32 struct itimerval value; 33 value.it_interval = timenext; 34 value.it_value = timeinterval; 35 36 setitimer(ITIMER_REAL , &value, NULL); 37 38 const char* pManagerIP = "0.0.0.0"; 39 int iPort = 56489; 40 // 创建套接字 41 g_fdSocket = socket(AF_INET, SOCK_STREAM, 0); 42 // 设置端口属性为非阻塞 43 fcntl(g_fdSocket, F_SETFL, fcntl(g_fdSocket, F_GETFL) | O_NONBLOCK); 44 // 设置端口句柄不可被子进程继承 45 fcntl(g_fdSocket, F_SETFD, 1); 46 // 设置可复用端口 47 int iReuseAddrFlag=1; 48 int iSetResult; 49 // 设置终端信息 50 setsockopt(g_fdSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&iReuseAddrFlag, sizeof(iReuseAddrFlag)); 51 g_localAddr.sin_family = AF_INET; 52 g_localAddr.sin_addr.s_addr = inet_addr(pManagerIP); 53 g_localAddr.sin_port = htons(iPort); 54 // 绑定套接字 55 bind(g_fdSocket, (struct sockaddr *)&g_localAddr, sizeof(g_localAddr)); 56 // 侦听端口 57 listen(g_fdSocket, 5); 58 59 while(1) 60 { 61 // 阻塞方式等待指定时间,检查是否有新的连接接入 62 fd_set rset; 63 FD_ZERO(&rset); 64 FD_SET(g_fdSocket, &rset); 65 //设置接收连接超时时间 66 struct timeval waitTime; 67 waitTime.tv_sec = 1; 68 waitTime.tv_usec = 0; 69 select(g_fdSocket + 1, &rset, NULL, NULL, &waitTime); 70 if (FD_ISSET(g_fdSocket, &rset)) 71 { 72 int fd = 0; //接受客户端连接的socket 73 struct sockaddr_in inClientAddr; //用于存放对端的地址 74 memset(&inClientAddr, 0, sizeof(inClientAddr)); 75 socklen_t iLen = sizeof(struct sockaddr_in); 76 fd = accept(g_fdSocket, (struct sockaddr *)(&inClientAddr),(socklen_t*) &iLen); 77 if (fd < 0) 78 { 79 printf("accept connect failed\n"); 80 continue; 81 } 82 char* pLinkIP = inet_ntoa(inClientAddr.sin_addr); 83 int iLinkPort = ntohs(inClientAddr.sin_port); 84 85 printf("accept client ip %s, port %d\n", pLinkIP, iLinkPort); 86 } 87 printf("no connect\n"); 88 } 89 //while(1) 90 //{ 91 // printf("+\n"); 92 // sleep(1); 93 //}; 94 return 0; 95 }
这段代码运行,按照预期是每隔十个"no connect",输出一个"12345678219"。但实际运行过程中却发现竟然会在输出"12345678219"的同时,输出"accept connect failed"。求大神告知原因
问题搞明白了。SELECT自身也是通过SIGALRM信号来唤醒自身的。一般情况下,如果是SGALARM唤醒的话,是因为超时时间到,这种情形,fd就不会被加到fd_set中。但是假如程序里面别的地方定义了setimer的话,setimer也会定时触发SIGALRM信号至目标进程,导致被SELECT阻塞的地方被唤醒,但此时却没达到超时时间,故而被当做以为目标句柄fd发生了变化,更改为ready,此时fd被无情的添加到了FD_SET中,故而进入了分支判断。当然实际上fd被没用发生状态变化,故而accept失败,打出了那个失败的日志。
上面分析错了。刚才我把信号都试验了下。SIGALARM与SIGVLARM信号并没有唤醒SELECT。倒是kill -15唤醒了select,而且存在一定的偶然性。迷茫中,看来settimer与select的底层都不是简单的什么SIGALRM信号搞定的。谁知道答案的,求指教