select模型实现了单线程上处理多个套接字的可能
1.select 函数定义如下
int select(
int nfds;
fd_set* readfds; //指向一个套接字集合
fd_set* writefds;
fd_set* exceptfds; //指向一个套接字集合,用来检查错误
const struct timeval* timeout //结构体 指定函数等待的最长时间 如果为NULL 则最长时间为无限长
);
2.套接字集合
fd_set结构可以把多个套接字连在一起,形成一个套接字集合。而select函数可以测试这个集合中哪些套接字有事件发生
typedef struct fd_set{
u_int fd_count;
SOCKET fd_array[FD_SETSIZE]; //套接字句柄数组 套接字集合 默认的是64
}fd_set;
3.操作fd_set的宏
FD_ZERO(*set) 很显然,初始化set为空集合,集合在使用前应该清空
FD_CLR(*set) 从set移除套接字s
FD_ISSET(s,*set) 检查s是不是set的成员,如果是返回TRUE
FD_SET(s,*set) 添加套接字到集合
主要程序代码:
#include"initsock.h"
#include<stdio.h>
CInitSock theSock; //初始化Winsock库
int main()
{
USHORT nPort=4567; //服务器监听的端口
//创建监听端口
SOCKET sListen=::socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
sockaddr_in sin;
sin.sin_family=AF_INET;
sin.sin_port=htons(nPort);
sin.sin_addr.S_un.S_addr=INADDR_ANY;
//绑定套接字到本地机器
if(::bind(sListen,(sockaddr*)&sin,sizeof(sin))==SOCKET_ERROR)
{
printf("Failed bind()");
return -1;
}
//进入监听模式
::listen(sListen,5);
//select模型处理过程
//第一步:初始化一个套接字 集合 fdSocket,添加监听套接字句柄到这个集合
fd_set fdSocket; //所有可用套接字集合
FD_ZERO(&fdSocket);
FD_SET(sListen,&fdSocket);
while(TRUE)
{
//第二步:将fdSocket集合的一个拷贝fdRead传递给select函数
//当有事件发生时,select函数移除fdRead集合中没有未决I/O操作的套接字句柄,然后返回
fd_set fdRead=fdSocket;
int nRet=::select(0,&fdRead,NULL,NULL,NULL);
if(nRet>0)
{
//第三步:通过将原来fdSocket集合与select处理过的fdRead集合比较
//确定有哪些套接字有未决I/O,并进一步处理这些I/O
for(int i=0;i<(int)fdSocket.fd_count;i++)
{
if(fdSocket.fd_array[i]==sListen) //监听套接字接收到新连接
{
if(fdSocket.fd_count<FD_SETSIZE)
{
sockaddr_in addrRemote;
int nAddrLen=sizeof(addrRemote);
SOCKET sNew=::accept(sListen,(SOCKADDR*)&addrRemote,&nAddrLen);
FD_SET(sNew,&fdSocket);
printf("接收到连接(%s)\n",::inet_ntoa(addrRemote.sin_addr));
}
else
{
printf("Too much connections!\n");
continue;
}
}
else
{
char szText[256];
int nRecv=::recv(fdSocket.fd_array[i],szText,strlen(szText),0);
if(nRecv>0)
{
szText[nRecv]='\0';
printf("接收到数据:%s\n",szText);
}
else
{
::closesocket(fdSocket.fd_array[i]);
FD_CLR(fdSocket.fd_array[i],&fdSocket);
}
}
}
}
else
{
printf("Failed select()\n");
break;
}
}
return 0;
}
其中 initsock
#include<WinSock.h>
#pragma comment(lib,"WS2_32")
class CInitSock
{
public:
CInitSock(BYTE minorVer=2,BYTE majorVer=2)
{
//初始化Wsh_32.dll
WSADATA wsaData;
WORD sockVersion=MAKEWORD(minorVer,majorVer);
if(::WSAStartup(sockVersion,&wsaData)!=0)
{
exit(0);
}
}
~CInitSock()
{
::WSACleanup();
}
};