select模型
select(选择)模型是winsock中常见的I/O模型。之所以称其为“select模型”,是由于它的
“中心思想”是利用select函数,实现对I/O的管理!最初设计该模型时,主要面向的是某些使用
Unix操作系统的计算机,它们采用的是Berkeley套接字方案。select模型已经集成到Winsock1.1中。
1
者能否向一个套接字写入数据。
int select ( int nfds, // 忽略
fd_set FAR * readfds, // 等待可读性检查的套接字组的地址
fd_set FAR * writefds, // 等待可写性检查的套接字组的地址
fd_set FAR * exceptfds, // 等待错误检查的套接字组的地址
const struct timeval FAR * timeout); // struct timeval结构体地址,select() 最多等待的时间
// 返回值 0--超时,SOCKET_ERROR--失败
// 说明:此函数的作用是删除fd_set结构体中没有IO操作的套接字
/* 注意:在3个套接字组中至少有一个不为NULL;在非空集合中必须包含一个套接字句柄。
如果timeout设为(0,0),select() 会立即返回,允许应用程序对select操作进行“轮询”。
如:
fd_set fdread;
FD_ZERO(&fdread);
FD_SET(s, &fdread);
select(0, &fdread, NULL, NULL, NULL);
if(FD_ISSET(s, &fdread))
{
//套接字可读
}
*/
2 .管理套接字的结构体
定义:
typedef struct fd_set {
u_int fd_count; /* how many are SET? */ // 元素的个数
SOCKET fd_array[FD_SETSIZE]; /* an array of SOCKETs */
} fd_set;
对struct fd_set结构体操作的宏
FD_SETSIZE 容量,指定fd_array数组大小,默认为64,也可自己修改宏
FD_ZERO( * set ) 置空,使数组的元素值都为3435973836,元素个数为0.
FD_SET(s, * set ) 添加,向 struct fd_set结构体添加套接字s
FD_ISSET(s, * set ) 判断,判断s是否为 struct fd_set结构体中的一员
FD_CLR(s, * set ) 删除,从 struct fd_set结构体中删除成员s
3 .用Select模型获取网络事件
FD_SET AllSockFd; // 装有所有的套接字
FD_ZERO( & AllSockFd);
AllSockFd = ClientSockFd ;
FD_SET(ListenSock, & AllSockFd);
FD_SET ReadSockFd; // 读集合
FD_SET WriteSockFd; // 写集合
while ( 1 )
{
FD_ZERO( & ReadSockFd);
FD_ZERO( & WriteSockFd);
ReadSockFd = AllSockFd;
WriteSockFd = AllSockFd;
int nRet = select( 0 , & ReadSockFd, & WriteSockFd, NULL, NULL);
if (SOCKET_ERROR == nRet)
{
continue ;
}
// 有请求事件发生
if (FD_ISSET(ListenSock, & ReadSockFd))
{
// 接受请求
SOCKET ClientSock;
u_short Port;
bool nRe = ( * (Pam.pListenSock)).Accept( & ClientSock, 0 , & Port);
if (nRe)
{
FD_SET(ClientSock, Pam.pClientSockFd);
// 设置套接字发送缓冲区80K
int nBuf = SOCKET_BUFF;
int nBufLen = sizeof (nBuf);
int nRe = setsockopt(ClientSock, SOL_SOCKET, SO_SNDBUF, ( char * ) & nBuf, nBufLen);
if (SOCKET_ERROR == nRe)
AfxMessageBox( " setsockopt error! " );
// 检查缓冲区是否设置成功
nRe = getsockopt(ClientSock, SOL_SOCKET, SO_SNDBUF, ( char * ) & nBuf, & nBufLen);
if (SOCKET_BUFF != nBuf)
AfxMessageBox( " 检查缓冲区:setsockopt error! " );
else
AfxMessageBox( " 已连接客户端! " );
}
}
// 判断是否可读或可写
for (u_int n = 0 ;n < ClientSockFd.fd_count;n ++ )
{
if (FD_ISSET(ClientSockFd.fd_array[n], & ReadSockFd)) // 发现可读
{
// 接收数据
// 如果失败 删除此元素
}
if (FD_ISSET(ClientSockFd.fd_array[n], & WriteSockFd)) // 发现可写
{
// 发送缓冲区未满可以发送
// 如果失败 删除此元素
}
}
}