Select模型

Socket描述符准备好的条件  

select函数的功能就是允许进程指示内核等待多个事件中的任一个发生,并仅在一个或多个事件发生或经过某个指定的时间之后才唤醒进程;
我们可以调用函数select并通知内核仅在以下情况发生时才返回:
A:集合{1,4,5}中的任何描述符准备好读;或
B:集合{2,7}中的任何描述符准备好写;或
C:集合{1,4}中的任何描述符有异常条件待处理;或
D:经过了10.2秒;
也就是说,通知内核,我们对哪些描述符感兴趣(读、写、异常条件)以及等待多长时间.我们所关心的描述符不受限于socket,任何描述符都可以用select函数来测试;

当select返回时,描述符集合中任何与没有准备好的描述符相对应的位都会被清0,所以,每次调用select时,我们都得将所有描述符集合中关心的位置都置为1;select的返回值表示的所有描述符中已准备好的描述符的个数;如果在任何描述符准备好之前定时器到,则返回0;返回-1表示出错(这是有可能的,比如:函数被一个捕获的信号中断);

引起select返回socket"准备好"的条件有以下三个说明:(TCP V2的图16.52)
一、下列四个条件中的任何一个满足时,socket准备好读:
 1.socket接收缓冲区中已经接收的数据的字节数大于等于socket接收缓冲区低潮限度的当前值;对这样的socket的读操作不会阻塞,并返回一个大于0的值(即:准备好读入的数据的字节数).我们可以用socket选项SO_RCVLOWAT来设置此低潮限度,对于TCP和UDPsocket,其缺省值为1;
 2.连接的读这一半关闭(即:接收到对方发过来的FIN的TCP连接).对于这样的socket的读操作将不阻塞,并且返回0(即:文件结束符,FIN包体长度为0字节);
 3.socket是一个用于监听的socket,并且已经完成的连接数为非0.这样的soocket处于可读状态,是因为socket收到了对方的connect请求,执行了三次握手的第一步:对方发送SYN请求过来,使监听socket处于可读状态;正常情况下,这样的socket上的accept操作不会阻塞;
 4.有一个socket有异常错误条件待处理.对于这样的socket的读操作将不会阻塞,并且返回一个错误(-1),errno则设置成明确的错误条件.这些待处理的错误也可通过指定socket选项SO_ERROR调用getsockopt来取得并清除;
 
二、下列三个条件中的任何一个满足时,socket准备好写:
 1.socket发送缓冲区中的可用空间字节数大于等于socket发送缓冲区低潮限度的当前值,且(i):socket已连接(TCP socket),或者(ii):socket不要求连接(如:UDP socket).这意味着,如果我们将这样的socket设置为非阻塞模式,写操作将不会阻塞,并且返回一个正值(如:由传输层接收的字节数).我们可以用socket选项SO_SNDLOWAT来设置此低潮限度,对于TCP和UDP socket,其缺省值一般是2048Bytes;
 2.连接的写这一半关闭.对于这样的socket的的写操作将产生信号SIGPIPE;
 3.有一个socket异常错误条件待处理.对于这样的socket的写操作将不会阻塞并且返回一个错误(-1),errno则设置成明确的错误条件.这些待处理的错误也可以通过指定socket选项SO_ERROR调用getsockopt函数来取得并清除;

三、如果一个socket存在带外数据或者仍处于带外标记,那么它就有异常条件待处理;

注意:一个socket出错时,它由select标记为既可读又可写;

接收和发送低潮限度的目的是:在select返回可读或可写条件之前,应用进程可以对"有多少数据可以读或有多大空间可用于写"进行控制.例如:如果我们知道除非至少64字节的数据可用,否则我们的应用进程不能完成有效的工作,那么就可以将接收低潮限度设置为64字节,以防止小于64字节的数据准备好读时,select就唤醒我们;
只要UDP socket的发送低潮限度小于发送缓冲区大小(缺省关系常常如此),由于不需要连接,这样的UDP socket总是可写的;

一个Select模型在一个线程中最好同时只管理64个套接字,虽然可以重新定义FD_SETSIZE宏的大小,但是在一个线程中可能影响Select的效率,当SOCKET的数量大于64的时候,可以采用分段轮询或者采用多线程采用多个Select的方法来进行管理。例如套接字列表为128,在第一次轮询时,将前64个放入队列中用Select进行状态查询,待本次操作全部结束后.将后64个再加入轮询队列中进行轮询处理.这样处理需要在非阻塞式下工作.以此类推,Select也能支持无限多个.

Select模型代码:

#include <WINSOCK2.H>
#include <iostream>

#pragma comment(lib,"ws2_32.lib")
#define  PORT 55500

int    g_iTotalConn = 0;

SOCKET g_CliSocketArr[FD_SETSIZE];

using namespace std;

DWORD WINAPI WorkerThread(LPVOID lpParameter);
int CALLBACK AccpetCondition(LPWSABUF lpCallerId,LPWSABUF lpCallerData, LPQOS lpSQOS,LPQOS lpGQOS,LPWSABUF lpCalleeId, LPWSABUF lpCalleeData,GROUP FAR * g,DWORD dwCallbackData);
int main()
{

	WSADATA wsaData;
	SOCKET sListen,sClient;
	sockaddr_in siLocal,siClient;
	int isiSize=sizeof(sockaddr_in);
	DWORD dwThreadId;
	WSAStartup(0x0202,&wsaData);
	sListen=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

	siLocal.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
	siLocal.sin_family=AF_INET;
	siLocal.sin_port=htons(PORT);

	bind(sListen,(sockaddr*)&siLocal,sizeof(sockaddr));

	listen(sListen,64);

	CreateThread(NULL,0,WorkerThread,NULL,0,&dwThreadId);

	while(true)
	{
		//sClient=accept(sListen,(sockaddr*)&siClient,&isiSize);
		sClient = WSAAccept(sListen,(sockaddr*)&siClient,&isiSize,AccpetCondition,0);
		if(sClient != INVALID_SOCKET)
		{
			g_CliSocketArr[g_iTotalConn++]=sClient;
		}
		else
		{
			cout<<"已操作最大连接数"<<endl;
		}
	
	}
	return 0;
}

DWORD WINAPI WorkerThread(LPVOID lpParam)
{
	int i;
	fd_set fdRead;
	int iRet;
	timeval tv={1,0};
	char szMessage[8292];
	while(true)
	{
		if(!g_iTotalConn)
		{
			cout<<"套接字为空!"<<endl;
			continue;
		}
		FD_ZERO(&fdRead);
		for(i=0;i<g_iTotalConn;i++)
		{
			FD_SET(g_CliSocketArr[i],&fdRead);
		}

		iRet = select(0,&fdRead,NULL,NULL,&tv);

		if(iRet == 0)
		{
			continue;
		}

		for(i = 0;i<g_iTotalConn;i++)
		{
			if(FD_ISSET(g_CliSocketArr[i],&fdRead))
			{
				iRet = recv(g_CliSocketArr[i],szMessage,8192,0);
				if(iRet == 0  || (iRet == SOCKET_ERROR && WSAGetLastError() == WSAECONNRESET))
				{
					cout<<"socket Closed"<<endl;
					if(i < g_iTotalConn-1)
					{
						g_CliSocketArr[i--]=g_CliSocketArr[--g_iTotalConn];
					}
					if(g_iTotalConn == 1)
					{
						g_iTotalConn --;
					}
				}
				else
				{
					szMessage[iRet]='\0';
					cout<<g_iTotalConn<<endl;
					cout<<szMessage<<endl;
				}
			}
		}
	}
}

int CALLBACK AccpetCondition(LPWSABUF lpCallerId,LPWSABUF lpCallerData, LPQOS lpSQOS,LPQOS lpGQOS,LPWSABUF lpCalleeId, LPWSABUF lpCalleeData,GROUP FAR * g,DWORD dwCallbackData)
{
	if(g_iTotalConn < FD_SETSIZE)
	{
		return CF_ACCEPT;
	}
	else
	{
		return CF_REJECT;
	}
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值