select模型

int select(
	int nfds, // 忽略,仅是为了与Berkeley 套接字兼容
	fd_set* readfds, // 指向一个套接字集合,用来检查其可读性
	fd_set* writefds, // 指向一个套接字集合,用来检查其可写性
	fd_set* exceptfds, // 指向一个套接字集合,用来检查错误
	const struct timeval* timeout // 指定此函数等待的最长时间,如果为NULL,则最长时间为无限大
);

函数调用成功,返回发生网络事件的所有套接字数量的总和。如果超过了时间限制,返回0,失败则返回SOCKET_ERROR
FD_ZERO(*set)    初始化set 为空集合。集合在使用前应该总是清空。
FD_CLR(s, *set)    从set 移除套接字s。
FD_ISSET(s, *set)    检查s 是不是set 的成员,如果是返回TRUE。
FD_SET(s, *set)    添加套接字到集合
select 函数返回之后,如果有下列事件发生,其对应的套接字就会被标识:
(1)readfds 集合。
    数据可读。
    连接已经关闭、重启或者中断。
    如果listen 已经被调用,并且有一个连接未决,accept 函数将成功。
(2)writefds 集合。
    数据能够发送。
    如果一个非阻塞连接调用正在被处理,连接已经成功。
(3)exceptfds 集合。
    如果一个非阻塞连接调用正在被处理,连接失败。
    OOB 数据可读。
当 select 返回时,它通过移除没有未决I/O 操作的套接字句柄修改每个fd_set 集合。例如,想要测试套接字s 是否可读时,必须将它添加到readfds 集合,然后等待select 函数返回。当select 调用完成后再确定s 是否仍然还在readfds 集合中,如果还在,就说明s 可读了。3个参数中的任意两个都可以是NULL
添加到fd_set 结构的套接字数量是有限制的,默认情况下,最大值是FD_SETSIZE,它在winsock2.h 文件中定义为64

#include <WINSOCK2.H>
#include <STDIO.H>

#pragma comment(lib, "Ws2_32.lib")

// 1. 初始化一个套接字集合, 添加sListen到该集合
// 2. 将fdSock的一个拷贝传递给select进行检测
// 3. 通过比较fdSock与fdRead, 确定有哪些未决IO, 并进一步处理
void main()
{
	unsigned short listenPort = 8000;
	WSADATA wsaData;
	WSAStartup(MAKEWORD(2,2), &wsaData);

	SOCKET sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	sockaddr_in sin;
	sin.sin_family = AF_INET;
	sin.sin_port = htons(listenPort);
	sin.sin_addr.S_un.S_addr = INADDR_ANY;
	bind(sListen, (sockaddr*)&sin, sizeof(sin));
	listen(sListen, 5);
	printf("sock is listenning ... \n");

	// select 模型处理
	// 1. 初始化一个套接字集合, 添加sListen到该集合
	fd_set fdSock;
	FD_ZERO(&fdSock);
	FD_SET(sListen, &fdSock);
	while (TRUE)
	{
		BOOL bExit = FALSE;
		// 2. 将fdSock的一个拷贝传递给select进行检测
		fd_set fdRead = fdSock;
		timeval timeout;
		timeout.tv_sec = 3;
		timeout.tv_usec = 0;
		int iRet = select(0, &fdRead, NULL, NULL, &timeout);
		if (SOCKET_ERROR == iRet)
		{
			printf("SOCKET_ERROR is return form select\n");
			break;
		}
		else if (0 == iRet)
		{
			printf("one time out\n");
		}
		else
		{
			// 3. 通过比较fdSock与fdRead, 确定有哪些未决IO, 并进一步处理
			for (int i=0; i<(int)fdSock.fd_count; i++)
			{
				if ( FD_ISSET(fdSock.fd_array[i], &fdRead) )
				{
					if ( fdSock.fd_array[i] == sListen ) // 监听套接字可读
					{
						if ( fdSock.fd_count < FD_SETSIZE )
						{
							sockaddr_in addrRemote;
							int addrLen = sizeof(addrRemote);
							SOCKET sRemote = accept(sListen, (sockaddr*)&addrRemote, &addrLen);
							FD_SET(sRemote, &fdSock);
							printf("one client connect, current client count %d\n", fdSock.fd_count);
						}
						else
						{
							printf("too much connection\n");
						}
					}
					else
					{
						char szBuff[256];
						int iRecv = recv(fdSock.fd_array[i], szBuff, sizeof(szBuff)-1, 0);
						if (iRecv > 0)   // 可读
						{
							szBuff[iRecv] = '\0';
							printf("[sock %d]recv data: %s\n", fdSock.fd_array[i], szBuff);
							if ( strstr(szBuff, "EXIT") != NULL )
							{
								bExit = TRUE;
								break;
							}
						}
						else if (SOCKET_ERROR == iRecv)  // 出错
						{
							printf("one error from recv: error code %d\n", WSAGetLastError());
							closesocket(fdSock.fd_array[i]);
							FD_CLR(fdSock.fd_array[i], &fdSock);
						}
						else // 连接关闭、重启或者中断
						{
							closesocket(fdSock.fd_array[i]);
							FD_CLR(fdSock.fd_array[i], &fdSock);
							printf("one client disconnect, sock %d, current client count %d\n", fdSock.fd_array[i], fdSock.fd_count);
						}
					}
				}
			}
			if (bExit)
				break;
		}
	}

	for (int i=0; i<(int)fdSock.fd_count; i++)
	{
		closesocket(fdSock.fd_array[i]);
	}

	printf("exit server\n");
	WSACleanup();
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值