select模式是一个广泛在Winsock中使用的I/O模式,这个模式的设计源于UNIX系统
主要使用select函数管理,故称之为select模式
select函数可以判断套接字是否存在数据,或者是否可以写入数据
select Function
The select function determines the status of one or more sockets, waiting if necessary, to perform synchronous I/O.
int select( __in int nfds, //this is parameter can ignore. set to 0. __in_out fd_set* readfds, //Optional pointer to a set of sockets to be checked for readability. __in_out fd_set* writefds, //Optional pointer to a set of sockets to be checked for writability. __in_out fd_set* exceptfds, //Optional pointer to a set of sockets to be checked for errors. __in const struct timeval* timeout //Maximum timer for select to wait.Set timeout parameter to NULL for blocking operations. );
Return Value
The select function return the total parameter number of socket handles that are ready and contained in the fd_set structure.
fd_set Structure
The fd_set structure is used by various Windows Sockets functions and service providers, such as the select function, to place sockets into a "set" for various purposes, such as testing a given socket for readability using the readfds parameter of the select function.
typedef struct fd_set { u_int fd_count; //Number of sockets in the set. SOCKET fd_array[FD_SETSIZE]; //Array of sockets that are in the set. } fd_set;
● FD_ZERO(*set) 初始化set为空集合。集合在使用前应该总是清空
● FD_CLR(s, *set) 从set移除套接字s
● FD_ISSET(s, *set) 检查s是不是set的成员,如果是返回true
● FD_SET(s, *set) 添加套接字到集合
使用select模式的步骤:
1. 加载套接字: WSAStartup();
2. 创建套接字;socket()
3. 绑定本地地址到套接字:bind();
4. 侦听:listen();
5 . 进入循环
6. 声明并初始化一个套接字集合,向这个套接字添加监听套接字句柄
7. 使用select函数移除该套接字集合没有发生I/O活动的套接字
8. 处理发生活动的I/O
9. 返回第6步
以下是Server代码
#include <winsock2.h>
#include <iostream>
#pragma comment(lib, "WS2_32")
using namespace std;
int main()
{
WSADATA wsadata;
if(0 != ::WSAStartup(MAKEWORD(2, 2), &wsadata)) //load socket, and set it's version.
{
cout<<"WSAStartup() -- FAILED"<<endl;
WSACleanup();
return -1;
}
SOCKET s = socket(AF_INET, SOCK_STREAM, 0); //create socket.
if(INVALID_SOCKET == s)
{
cout<<"socket() -- FAILED"<<endl;
WSACleanup();
return -1;
}
sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(4567);
sin.sin_addr.S_un.S_addr = INADDR_ANY;
if(SOCKET_ERROR == bind(s, (sockaddr*)&sin, sizeof(sin))) //bind socket
{
cout<<"bind() -- FAILED"<<endl;
WSACleanup();
return -1;
}
::listen(s, 10);
while(true)
{
fd_set fdSet;
FD_ZERO(&fdSet);
FD_SET(s, &fdSet); //add s to fdSet
int nLen = ::select(0, &fdSet, NULL, NULL, NULL); //Remove not active I/O
if(0 < nLen)
{
for(int i=0; i<(int)fdSet.fd_count; ++i)
{
if(FD_ISSET(fdSet.fd_array[i], &fdSet))
{
sockaddr_in addr;
int len = sizeof(addr);
SOCKET sock = ::accept(s, (sockaddr*)&addr, &len);
FD_SET(sock, &fdSet);
cout<<"New connect:%d"<<inet_ntoa(addr.sin_addr)<<endl;
}
else
{
char szTex[256];
int txtLen = ::recv(fdSet.fd_array[i], szTex, sizeof(szTex), 0);
if(0 < txtLen)
{
cout<<szTex<<endl;
}
else
{
closesocket(fdSet.fd_array[i]);
FD_CLR(fdSet.fd_array[i], &fdSet);
}
}
}
}
else
{
cout<<"Fail connect"<<endl;
Sleep(2000);
}
}
closesocket(s);
WSACleanup();
return 0;
}