使用select()时陷入无限循环,输入Socket命令无响应
问题分析与修复
你的程序输入无响应的核心问题是读取输入时fgets参数错误,同时还有几个会导致select行为异常的bug,逐一说明:
1. fgets读取长度错误
你用strlen(buffer)作为fgets的第二个参数,但刚执行memset(buffer, 0, 300)后,buffer全是0,strlen(buffer)返回0,导致fgets读取0个字符,根本没获取到输入内容。
修复:把fgets的长度参数改成sizeof(buffer),确保能读取到最大缓冲区长度的内容:
fgets(buffer, sizeof(buffer), stdin);
2. maxfd初始化与更新逻辑错误
初始时host.TCPincoming的值是-1,你把maxfd设为这个值,后续select(maxfd + 1, ...)就变成select(0, ...),这会导致select不监听任何大于0的文件描述符(比如UDPsocket、TCPsocket等)。同时,你重复对无效fd(值为-1)执行FD_SET,这是未定义行为,会破坏fd_set结构。
修复:
- 初始化
maxfd为0(stdin的文件描述符是0) - 只在fd有效(不等于-1)时,才添加到
fd_set并更新maxfd:
FD_ZERO(&(host.rfds)); FD_SET(0, &(host.rfds)); // 监听stdin maxfd = 0; // 处理UDPsocket if (host.UDPsocket != -1) { FD_SET(host.UDPsocket, &(host.rfds)); if (host.UDPsocket > maxfd) maxfd = host.UDPsocket; } // 处理TCPcord if (host.TCPcord != -1) { FD_SET(host.TCPcord, &(host.rfds)); if (host.TCPcord > maxfd) maxfd = host.TCPcord; } // 处理TCPincoming if (host.TCPincoming != -1) { FD_SET(host.TCPincoming, &(host.rfds)); if (host.TCPincoming > maxfd) maxfd = host.TCPincoming; } // 处理TCPsocket_next if (host.TCPsocket_next != -1) { FD_SET(host.TCPsocket_next, &(host.rfds)); if (host.TCPsocket_next > maxfd) maxfd = host.TCPsocket_next; }
3. select的timeout被修改问题
select调用后会修改传入的timeval结构体,把它设置为剩余的超时时间。如果不重新初始化,后续循环中timeout可能变成0,导致select立即返回,造成CPU空转。
修复:把timeout的初始化放到while(1)循环内部,每次循环都重置超时时间:
while(1) { struct timeval timeout; timeout.tv_sec = 60; timeout.tv_usec = 0; // 后续的FD_SET、select逻辑... }
4. 冗余的FD_SET操作
你在代码开头已经对host.TCPcord、host.TCPincoming执行了FD_SET,后面又在if判断中重复执行,这完全冗余,且当fd为-1时会触发未定义行为,必须删除开头的无效FD_SET调用。
修复后的核心代码片段
while(1) //main loop { struct timeval timeout; // 每次循环重新初始化超时 timeout.tv_sec = 60; timeout.tv_usec = 0; FD_ZERO(&(host.rfds)); FD_SET(0, &(host.rfds)); // 监听stdin int maxfd = 0; // 加入UDPsocket if (host.UDPsocket != -1) { FD_SET(host.UDPsocket, &(host.rfds)); if (host.UDPsocket > maxfd) maxfd = host.UDPsocket; } // 加入TCPcord if (host.TCPcord != -1) { FD_SET(host.TCPcord, &(host.rfds)); if (host.TCPcord > maxfd) maxfd = host.TCPcord; } // 加入TCPincoming if (host.TCPincoming != -1) { FD_SET(host.TCPincoming, &(host.rfds)); if (host.TCPincoming > maxfd) maxfd = host.TCPincoming; } // 加入TCPsocket_next if (host.TCPsocket_next != -1) { FD_SET(host.TCPsocket_next, &(host.rfds)); if (host.TCPsocket_next > maxfd) maxfd = host.TCPsocket_next; } printf("starts loop\n"); counter = select (maxfd + 1, &(host.rfds), NULL, NULL, &timeout); if (counter > 0) { if(FD_ISSET(0, &(host.rfds))) { printf("starts reading buffer\n"); char buffer[300]; memset(buffer, 0, 300); fgets(buffer, sizeof(buffer), stdin); // 修复读取长度 int n = 0; char* token[5]; token[n]=strtok(buffer, " \n"); while(token[n]!=NULL && n<5) { n++; token[n] = strtok(NULL, " \n"); } for (int i = 0; i<n; i++) { token[i][strcspn(token[i], "\n")] = '\0'; printf("\ninput token: %s\n", token[i]); } if ((strcmp(token[0], "join") == 0) || (strcmp(token[0], "j") == 0)) { if (host.connected) { printf("you are already registered\n"); exit(0); } printf("input command: join\n"); join(host_ptr, token[1], token[2]); } else if ((strcmp(token[0], "exit") == 0) || strcmp(token[0], "x") == 0) { printf("Exit command. exiting..."); exit(EXIT_SUCCESS); } } // 其他FD_ISSET处理逻辑... } }
内容的提问来源于stack exchange,提问作者A Burial At Ornans
相关产品推荐
相关产品推荐

