Linux select函数问题:调用close关闭socket时,select并不会退出等待

oidnwu 2014-04-25 05:02:37
最近在编写Linux下 视频代理服务器时,碰到了一个很奇葩的问题,现在说出来希望能有个人帮我解答。
首先,环境是centOS系统,socket用的是select模式。阻塞和非阻塞、I/O复用不复用都会出现这个问题。
当socket进入select时,另一个线程调用close关掉该socket,select不能退出等待并返回。
代码是从项目中抽出来并经过实验的,会出现这个问题没错。
代码如下:

#include <stdio.h>
#include <stdint.h>
#include <errno.h>
#include <poll.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <fcntl.h>

int proxysvr_sock;

//设置非阻塞
static int SetNonBlocking(int sock)
{
int opts;
opts=fcntl(sock, F_GETFL, 0);

if(opts<0)
{
return -1;
}

opts = opts | O_NONBLOCK;

if(fcntl(sock,F_SETFL,opts)<0)
{
return -1;
}
}

static void *AcceptConnectionsThread(void *arg)
{
struct sockaddr_in sin; /* bind socket address */
socklen_t sinlen; /* length of address */

fd_set readfds;
fd_set errofds;
int ret, sock;
struct timeval timeo;

while (1)
{
FD_ZERO(&readfds);
FD_SET(proxysvr_sock, &readfds);
FD_ZERO(&errofds);
FD_SET(proxysvr_sock, &errofds);

timeo.tv_sec = 5;
timeo.tv_usec = 0;

ret = select(proxysvr_sock + 1, &readfds, (fd_set *)0, &errofds, NULL);
printf("ret = %d\n", ret);
if (ret == 0)
continue;
else if (ret < 0)
break;

if (FD_ISSET(proxysvr_sock, &errofds))
{
printf("exit ok!\n");
break;
}

sinlen = sizeof(sin);
if (FD_ISSET(proxysvr_sock, &readfds))
{
printf("read ok!\n");
if ((sock = accept(proxysvr_sock, (struct sockaddr *)&sin, &sinlen)) < 0)
{
break;
}
}
}

return NULL;
}

int socket_create_listen(int port, int reuse, int backlog)
{
int fd;
struct sockaddr_in addr;

fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (fd < 0)
{
return -1;
}

if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&reuse, sizeof(int)) < 0)
{
printf("setsockopt SO_REUSEADDR error\n");
}

addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(port);
if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) != 0)
{
close(fd);
return -1;
}

SetNonBlocking(fd);//把客户端的socketFD设置为非阻塞
if (listen(fd, backlog) < 0)
{
close(fd);
return -1;
}
return fd;
}

int main()
{
int ret;
int port = 9990;
pthread_t rtspd_tid;

proxysvr_sock = socket_create_listen(port, 1, 100);
if (proxysvr_sock < 0)
return -1;

if ((ret = pthread_create(&rtspd_tid, NULL, AcceptConnectionsThread, (void *)NULL)) != 0)
{
close(proxysvr_sock);
proxysvr_sock = -1;
return -1;
}
while(1)
{
printf("wait for input...\n");
char c = getchar();
printf("c=%c\n", c);
if(c=='q')
break;
}
ret = close(proxysvr_sock);
printf("Ret = %d\n", ret);
if (ret != 0)
{
printf("socket close %d error\r\n", proxysvr_sock);
}
printf("ProxyServer_Uninitialize socket close %d\r\n", proxysvr_sock);
pthread_join(rtspd_tid, 0);
proxysvr_sock = -1;

return 0;
}


希望能有高手解答。先谢过了
...全文
578 2 打赏 收藏 转发到动态 举报
写回复
用AI写文章
2 条回复
切换为时间正序
请发表友善的回复…
发表回复
神一样的小王 2014-08-03
  • 打赏
  • 举报
回复
楼上正解。学习了。
帅得不敢出门 2014-04-26
  • 打赏
  • 举报
回复
这个行为是未指定的,man文档中有说明 Multithreaded applications If a file descriptor being monitored by select() is closed in another thread, the result is unspecified. On some UNIX systems, select() unblocks and returns, with an indication that the file descriptor is ready (a subsequent I/O operation will likely fail with an error, unless another the file descriptor reopened between the time select() returned and the I/O operations was performed). On Linux (and some other systems), closing the file descriptor in another thread has no effect on select(). In summary, any application that relies on a particular behavior in this scenario must be considered buggy.

23,120

社区成员

发帖
与我相关
我的任务
社区描述
Linux/Unix社区 应用程序开发区
社区管理员
  • 应用程序开发区社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧