选择模型是I/O模型中最简单的一个。Server端通过创建两个套接字集合fdOld和fdNew,在循环中通过事件添加和移除未决IO套接字句柄。测试的时候先启动服务端再启动客户端。
以下为Server端源代码(在VS2010下测试通过):
#include "stdafx.h"
#include<WinSock2.h>
#include<Windows.h>
#include<iostream>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
BOOL InitSocket();
BOOL SelectMode();
int _tmain(int argc, _TCHAR* argv[])
{
if(InitSocket()==FALSE)
{
return 0;
}
if(SelectMode()==FALSE)
{
}
WSACleanup;
return 0;
}
BOOL InitSocket()
{
WSAData wsaData={0};
if(WSAStartup(MAKEWORD(2,2),&wsaData)==0)
{
return TRUE;
}
return FALSE;
}
BOOL SelectMode()
{
//创建监听套接字
SOCKET sListenSocket;
sListenSocket=socket(AF_INET,SOCK_STREAM,0);
if(sListenSocket==INVALID_SOCKET)
{
return FALSE;
}
sockaddr_in ServerAddr;
ServerAddr.sin_family=AF_INET;
ServerAddr.sin_port=htons(2356);
ServerAddr.sin_addr.S_un.S_addr=INADDR_ANY;
int iResult=bind(sListenSocket,(SOCKADDR*)&ServerAddr,sizeof(ServerAddr));
if(iResult==SOCKET_ERROR)
{
return FALSE;
}
listen(sListenSocket,5);
//创建套接字集合
fd_set fdOld;
fd_set fdNew;
//初始化套接字集合
fdOld.fd_count=0;
fdNew.fd_count=0;
//将监听套接字放到fdOld集合当中
FD_SET(sListenSocket,&fdOld);
while(true)
{
//将fdOld集合的一个拷贝fdNew传递给Select函数
//当有事件发生时,select函数移除fdNew集合中没有未决I/O操作的套接字句柄,然后返回
fdNew=fdOld;
int iReturn=select(0,&fdNew,NULL,NULL,NULL);//第一个参数忽略,第二个参数指向一个套接字集合,检查其可读性,第三个参数指向一个套接字集合,检查其可写性
//第四个参数指向一个套接字集合,检查错误,第五个参数指定此函数等待的最长时间,如果为NULL,则为无限长
int i=0;
for(i=0;i<fdOld.fd_count;i++)
{
if(FD_ISSET(fdOld.fd_array[i],&fdNew)!=0)//确定哪些套接字有未决I/O
{
if(fdOld.fd_array[i]==sListenSocket)//如果该套接字为监听套接字
{
//定义一个通信套接字
SOCKET sClientSocket;
sockaddr_in Clientaddr;
int addrlen=sizeof(Clientaddr);
sClientSocket=accept(sListenSocket,(SOCKADDR*)&ServerAddr,&addrlen);
if(sClientSocket==INVALID_SOCKET)
{
continue;
}
//将通信套接字放入fdOld中
if(fdOld.fd_count<FD_SETSIZE)
{
FD_SET(sClientSocket,&fdOld);
}
else
{
send(sClientSocket,"Waiting",strlen("Waiting"),0);
closesocket(sClientSocket);
}
continue;
}
else//如果该套接字为通信套接字
{
char szBuffer[0x1000]={0};
int iLength=recv(fdOld.fd_array[i],szBuffer,0x1000,0);
if(iLength<0)//返回值小于零说明客户端关闭了,则关闭fdOld集中中相应的通信套接字
{
closesocket(fdOld.fd_array[i]);
FD_CLR(fdOld.fd_array[i],&fdOld);
cout<<"One Client Lost"<<endl;
continue;
}
szBuffer[iLength]=‘\0‘;
cout<<szBuffer<<endl;
}
}
}
}
}
以下为Client源代码:
#include "stdafx.h"
#include <WinSock2.h> //这个头文件必须放前面,在Ws2_32.dll里
#include <Windows.h> //在kernel32.dll里
#include <iostream>
#pragma comment(lib,"Ws2_32.lib") //加载静态库
using namespace std;
SOCKET sClientSocket;
bool InitSocket();//启动套接字库
DWORD WINAPI RecvThread();//声明句柄
int _tmain(int argc, _TCHAR* argv[])
{
cout<<"Client"<<endl;
if(InitSocket()==false)//初始化失败
{
cout<<"Init Socket Error"<<endl;
return 0;
}
//通信套接字
sClientSocket = socket(AF_INET,SOCK_STREAM,0);
if (sClientSocket==INVALID_SOCKET)
{
cout<<"Create Listen Socket Error\r\n";
WSACleanup();
return 0;
}
//初始化网卡
SOCKADDR_IN ServerAddr;
ServerAddr.sin_family=AF_INET;//指示协议家族
ServerAddr.sin_addr.S_un .S_addr=inet_addr("192.168.1.113");//指示IP,inet_addr将IP地址转化为计算机能理解的形式
ServerAddr.sin_port=htons(2356); //端口,指示数据包在应用层相应端口来接受,就是发送数据和接受数据要用相同的端口,如果不是在广域网上,可以自己指示
//连接
int iRet=connect(sClientSocket,(sockaddr*)&ServerAddr,sizeof(ServerAddr));
if(iRet==SOCKET_ERROR)
{
cout<<"fail to connect";
}
HANDLE hThread =CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)RecvThread,NULL,0,NULL);//启动句柄
if(iRet==SOCKET_ERROR)//如果绑定失败,释放内存
{
cout<<"Connect Error"<<endl;
closesocket(sClientSocket);
WSACleanup();
return 0;
}
//接收数据
char szBuffer[0x1000]={0};
while(true)//死循环,让通信套接字持续工作
{
cin>>szBuffer;
send(sClientSocket,szBuffer,strlen(szBuffer),0);//发送数据
}
WaitForSingleObject(hThread,INFINITE);
WSACleanup();//解除与Socket库的绑定并且释放Socket库占用的资源
return 0;
}
DWORD WINAPI RecvThread()
{
while(true)
{
char szBuffer[0x1000]={0};
DWORD dwReturn=recv(sClientSocket,szBuffer,0x1000,0);//接收数据
if(dwReturn<=0)
{
break;
}
szBuffer[dwReturn]=‘\0‘;//记得加上\0
cout<<szBuffer<<endl;
}
return 0;
}
bool InitSocket()
{
WORD v1 = 0;
v1 = MAKEWORD(2,2);//指示Winsock2.2版本
WSADATA wsaData = {0};
if ((WSAStartup(v1,&wsaData)==0))//初始化Winsock Dll
{
return TRUE;
}
return FALSE;
}