目录
异步事件模型的概念
工作流程:
WSAEventSelect模型的优势和不足
代码:
异步事件模型的概念
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\listenbool 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;}elseprintf("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){//todoprintf("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;
}