目录
异步事件模型的概念
WSAEventSelect模型是WindowsSockets提供的另外一个有用的异步I/O模型。该模型允许一个或多个套接字上接收以事件为基础的网络事件通知。Windows Sockets应用程序在创建套接字后,调用WSAEventSlect()函数,将一个事件对象与网络事件集合关联在一起。当网络事件发生时,应用程序以事件的形式接收网络事件通知。
工作流程:
1.通过WSAEventSelect()函数 向windows注册
2.监测事件什么时候有信号WSAWaitForMultipleEvents ()
3.判断发生的是什么网络事件WSAEnumNetworkEvents()
4.根据发生的事件进行相应的处理
WSAEventSelect模型的优势和不足
优势:可以在一个非窗口的Windows Sockets程序中,实现多个套接字的管理。性能较优
不足:
1.每个WSAEventSelect模型最多只能管理64个套接字。当应用程序中需要管理多于64个套接字时,就需要额外创建线程。
2.由于使用该模型开发套接字应用程序需要调用几个相关函数才能完成。因此,该模型增加了开发的难度,增加了开发人员的编码量。从这个角度讲,该模型不如WSAAysnceSelect模型方便。
代码:
TCPServer.h
#ifndef TCPSERVER_H
#define TCPSERVER_H
#include <winsock2.h>
#include <stdio.h>
#include <stdlib.h>
#include <list>
#include <windows.h>
#include <map>
//#define MAXNUM 64
const int MAXNUM = 64;
//socket----包大小、缓冲区、偏移量
struct SocketInfo{
int nPackSize;
char *pszbuf;
int offset;
};
class TCPServer
{
public:
TCPServer();
~TCPServer();
public:
//1.初始化网络--加载、创建socket\bind\listen
bool initNetWork(const char* szip = "127.0.0.1",unsigned short nport = 1234);
void unInitNetWork(const char* szerr = "null"); //卸载网络
bool sendData(SOCKET sockWaiter,const char* szbuf,int nlen); //向指定客户端发送数据
void recvData(); //接收数据
static DWORD WINAPI threadProc(LPVOID lpvoid);
private:
SOCKET m_socklisten;
std::list<HANDLE> m_lstThread;
bool m_bFlagQuit;
std::map<DWORD,SOCKET> m_mapThreadIdToSocket;
SOCKET m_arysocket[MAXNUM];
HANDLE m_aryEvent[MAXNUM];
int m_nEventNum;
std::map<SOCKET,SocketInfo*> m_mapSocketToInfo;
};
#endif // TCPSERVER_H
TCPServer.cpp
#include "tcpserver.h"
TCPServer::TCPServer()
{
m_socklisten = 0;
m_bFlagQuit = true;
m_nEventNum = 0;
ZeroMemory(m_aryEvent,sizeof(m_aryEvent));
ZeroMemory(m_arysocket,sizeof(m_arysocket));
}
TCPServer::~TCPServer()
{
}
bool TCPServer::initNetWork(const char *szip, unsigned short nport)
{
//1.选择种类 韩餐 火锅 串串香 川菜 -- 加载库
WORD wVersionRequested;
WSADATA wsaData;
int err;
/* Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h */
wVersionRequested = MAKEWORD(2, 2);
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0) {
/* Tell the user that we could not find a usable */
/* Winsock DLL. */
printf("WSAStartup failed with error: %d\n", err);
return false;
}
/* Confirm that the WinSock DLL supports 2.2.*/
/* Note that if the DLL supports versions greater */
/* than 2.2 in addition to 2.2, it will still return */
/* 2.2 in wVersion since that is the version we */
/* requested. */
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
/* Tell the user that we could not find a usable */
/* WinSock DLL. */
printf("Could not find a usable version of Winsock.dll\n");
unInitNetWork();
return false;
}
else
printf("The Winsock 2.2 dll was found okay\n");
//2.雇人-店长--
m_socklisten = socket(AF_INET,SOCK_STREAM,0);
if(m_socklisten == INVALID_SOCKET){
unInitNetWork("socket err");
return false;
}
//3.选择地址--
sockaddr_in addrserver;
addrserver.sin_family = AF_INET;
addrserver.sin_addr.S_un.S_addr = inet_addr(szip);
addrserver.sin_port = htons(nport);
if( SOCKET_ERROR == bind(m_socklisten,(sockaddr*)&addrserver,sizeof(addrserver))){
unInitNetWork("bind err");
return false;
}
//4.宣传--
if( SOCKET_ERROR == listen(m_socklisten,1000)){ // 1_1_1_1______
unInitNetWork("listen err");
return false;
}
HANDLE hevent = WSACreateEvent(); //默认人工 匿名无信号事件
if(0 ==WSAEventSelect(m_socklisten,hevent,FD_ACCEPT)){
//注册成功
m_arysocket[m_nEventNum] = m_socklisten;
m_aryEvent[m_nEventNum] = hevent;
++m_nEventNum;
}
// //创建接收连接的线程
HANDLE hThread = CreateThread(0,0,&threadProc,this,0,0);
if(hThread)
m_lstThread.push_back(hThread);
return true;
}
DWORD TCPServer::threadProc(LPVOID lpvoid)
{
TCPServer *pthis = (TCPServer*)lpvoid;
DWORD dwIndex;
WSANETWORKEVENTS wwe;
sockaddr_in addrclient;
int nsize = sizeof(addrclient);
while(pthis->m_bFlagQuit){
//等事件,判断哪个事件有信号
dwIndex =WSAWaitForMultipleEvents(pthis->m_nEventNum, //事件的个数
pthis->m_aryEvent, //监听事件的数组
FALSE,//任意一个有信号就返回
WSA_INFINITE,//等待时间
0
);
dwIndex -=WSA_WAIT_EVENT_0;
//判断发生什么事了
if(WSAEnumNetworkEvents(pthis->m_arysocket[dwIndex],
pthis->m_aryEvent[dwIndex],
&wwe))
continue;
// 处理
if(wwe.lNetworkEvents & FD_ACCEPT){
printf("wait client connect......\n");
SOCKET sockWaiter = accept(pthis->m_socklisten,(sockaddr*)&addrclient,&nsize);
printf("client ip:%s port:%d\n",inet_ntoa(addrclient.sin_addr),addrclient.sin_port );
//向windows注册
HANDLE hEvent = WSACreateEvent();
if( 0==WSAEventSelect(sockWaiter,hEvent,FD_READ|FD_CLOSE)){
pthis->m_aryEvent[pthis->m_nEventNum] =hEvent;
pthis->m_arysocket[pthis->m_nEventNum] = sockWaiter;
++pthis->m_nEventNum;
}
}
if(wwe.lNetworkEvents & FD_READ){
SocketInfo *p = pthis->m_mapSocketToInfo[pthis->m_arysocket[dwIndex]];
if(p == NULL){
p = new SocketInfo;
p->nPackSize =0;
p->offset =0;
p->pszbuf = NULL;
//接收包大小
int nReadNum = recv(pthis->m_arysocket[dwIndex],(char*)&p->nPackSize,sizeof(int),0);
if(nReadNum >0){
p->pszbuf = new char[p->nPackSize];
}
pthis->m_mapSocketToInfo[pthis->m_arysocket[dwIndex]] = p;
}else{
//接收包数据
int nReadNum = recv(pthis->m_arysocket[dwIndex],p->pszbuf+p->offset,p->nPackSize,0);
if(nReadNum>0){
p->offset+=nReadNum;
p->nPackSize-=nReadNum;
if(p->nPackSize ==0){
//todo
printf("client say:%s\n",p->pszbuf);
delete []p->pszbuf;
pthis->m_mapSocketToInfo[pthis->m_arysocket[dwIndex]] = NULL;
}
}
}
}
if(wwe.lNetworkEvents & FD_CLOSE){
closesocket(pthis->m_arysocket[dwIndex]);
WSACloseEvent(pthis->m_aryEvent[dwIndex]);
if(pthis->m_nEventNum >1){
pthis->m_aryEvent[dwIndex] = pthis->m_aryEvent[pthis->m_nEventNum-1];
pthis->m_arysocket[dwIndex] = pthis->m_arysocket[pthis->m_nEventNum-1];
}
--pthis->m_nEventNum;
}
}
return 0;
}
void TCPServer::unInitNetWork(const char* szerr )
{
printf(szerr);
if(m_socklisten){
closesocket(m_socklisten);
m_socklisten = 0;
}
WSACleanup();
//结束所有的线程
m_bFlagQuit = false;
auto ite = m_lstThread.begin();
while(ite !=m_lstThread.end()){
//判断线程状态
if(WAIT_TIMEOUT == WaitForSingleObject(*ite,100))
TerminateThread(*ite,-1);
CloseHandle(*ite);
*ite = NULL;
++ite;
}
m_lstThread.clear();
}
bool TCPServer::sendData(SOCKET sockWaiter, const char *szbuf, int nlen)
{
//发送的包大小
if(send(sockWaiter,(const char*)&nlen,sizeof(int),0) <=0)
return false;
//发送包内容
if(send(sockWaiter,szbuf,nlen,0) <=0)
return false;
return true;
}