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

最近在编写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;
}

希望能有高手解答。先谢过了

感谢大神“帅得不敢出门”(ID:zmlovelx)慷慨解答,问题答案就在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.

©️2020 CSDN 皮肤主题: 深蓝海洋 设计师:CSDN官方博客 返回首页