异步事件模型:
像操作系统注册信息(socket、网络事件、事件),当指定网络时间按发生,将事件置为信号。
特点:被动通知
个数限制 64 (多线程 多数组)
性能较优
阻塞:自己拷贝数据
如果有两个事件发生信号,只会发生索引最小的那个。
#include "ctcpnet.h"
#include <winsock2.h>
#include <windows.h> //winsock.h
#include <iostream>
#include <QDebug>
using namespace std;
CTCPNet::CTCPNet()
{
m_sock = NULL;
m_flagquit = true;
m_nEventNum = 0;
ZeroMemory(m_aryhEvent,sizeof(m_aryhEvent));
ZeroMemory(m_arySocket,sizeof(m_arySocket));
}
bool CTCPNet::InitNetWork()
{
//1.选择种类 火锅 韩餐 烤肉--
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD(2, 2);
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0) {
printf("WSAStartup failed with error: %d\n", err);
return false;
}
if (LOBYTE(wsaData.wVersion) != 2 ||
HIBYTE(wsaData.wVersion) != 2)
{
UnInitNetWork();
return false;
}
else
printf("The Winsock 2.2 dll was found okay\n");
//2.雇个店长--
m_sock = socket(AF_INET,SOCK_STREAM,0);
if(m_sock == INVALID_SOCKET)
{
UnInitNetWork();
return false;
}
//3.找个地方
sockaddr_in addrserver;
addrserver.sin_family = AF_INET;
addrserver.sin_port = htons(8899);
addrserver.sin_addr.S_un.S_addr = 0;
if(SOCKET_ERROR == bind(m_sock,( const sockaddr *)&addrserver,sizeof(addrserver)))
{
UnInitNetWork();
return false;
}
//4.店长宣传
if(SOCKET_ERROR == listen(m_sock,10))
{
UnInitNetWork();
return false;
}
//向windows注册
HANDLE hEvent = WSACreateEvent();//默认人工 无信号
if (!WSAEventSelect(m_sock,hEvent,FD_ACCEPT|FD_CLOSE))
{
//SUCCESS
m_aryhEvent[m_nEventNum] = hEvent;
m_arySocket[m_nEventNum] = m_sock;
m_nEventNum++;
}
m_thread = (HANDLE)_beginthreadex(0,0,&ThreadProc,this,0,0);
if(m_thread)
{
m_lstThread.push_back(m_thread);
}
return true;
}
unsigned _stdcall CTCPNet::ThreadProc(void *lpvoid)
{
CTCPNet *pthis =(CTCPNet*)lpvoid;
DWORD dwIndex;
WSANETWORKEVENTS we;
char szbuf[1024]={0};
//接收连接
while(pthis->m_flagquit)
{
//等多个事件--有可能是ED_ACCEPT FD_READ FD_CLOSE
dwIndex =WSAWaitForMultipleEvents(pthis->m_nEventNum,//等多少个事件
pthis->m_aryhEvent,//事件数组 数组元素的类型为WSAEVENT类型
FALSE,//等一个事件,还是等所有的事件,如果只需等众多事件中的一个到来,就开始工作,那么就设为FALSE,
WSA_INFINITE,//等待的时间,如果是无限时等待,则把它设为:WSA_INFINITE.
0);
dwIndex -= WSA_WAIT_EVENT_0;//代表数组的下标 最小的索引的值
//判断发生什么事
if (WSAEnumNetworkEvents(pthis->m_arySocket[dwIndex],
pthis->m_aryhEvent[dwIndex],
&we))
continue;
//01 10 11
if (we.lNetworkEvents & FD_ACCEPT)
{
//阻塞
SOCKET sockWaiter = accept(pthis->m_sock,0,0);
if (sockWaiter == INVALID_SOCKET){
continue;
}
//异步事件向windows注册
HANDLE hEvent = WSACreateEvent();//默认人工 无信号
if (!WSAEventSelect(sockWaiter,hEvent,FD_READ|FD_CLOSE))
{
pthis->m_aryhEvent[pthis->m_nEventNum] = hEvent;
pthis->m_arySocket[pthis->m_nEventNum] = sockWaiter;
pthis->m_nEventNum++;
//success
}
}
//读操作:直接
if (we.lNetworkEvents & FD_READ)
{
int nRecvNum = recv(pthis->m_arySocket[dwIndex],szbuf,sizeof(szbuf),0);
if (nRecvNum > 0)
{
cout<<szbuf<<endl;
}
}
//某个事件关闭,将数组中最后一个覆盖给关闭的那个
// 1.关闭套接字
// 2.关闭事件
// 3.将数组中最后一个覆盖给关闭的事件和套接字
// 4.数目--
if (we.lNetworkEvents & FD_CLOSE)
{
//关闭套接字
closesocket(pthis->m_arySocket[dwIndex]);
//关闭事件
WSACloseEvent(pthis->m_aryhEvent[dwIndex]);
if (pthis->m_nEventNum>1)
{
pthis->m_arySocket[dwIndex] = pthis->m_arySocket[pthis->m_nEventNum];
pthis->m_aryhEvent[dwIndex] = pthis->m_aryhEvent[pthis->m_nEventNum];
}
pthis->m_nEventNum--;
}
}
return 0;
}
unsigned _stdcall CTCPNet::ThreadRecv(void *lpvoid)
{
//一个线程对应一个waiter 用map
CTCPNet *pthis =(CTCPNet*)lpvoid;
pthis->RecvData();
return 0;
}
void CTCPNet::RecvData()
{
//获取线程id映射sockWaiter
SOCKET sockWaiter = m_mapIdToSock[GetCurrentThreadId()];
int nPackSize;
int nRecvNum;
char* szbuf=NULL;
int offset;
while(m_flagquit)
{
nRecvNum = recv(sockWaiter,(char*)&nPackSize,sizeof (int),0);
if (nRecvNum<0)
{
//客户端下线
if (WSAGetLastError() == WSAECONNRESET)
{
break;
}
continue;
}
if (nPackSize<=0) continue;
//解决粘包问题
szbuf = new char[nPackSize];
offset = 0;
while(nPackSize)
{
nRecvNum = recv(sockWaiter,szbuf+offset,nPackSize,0);
offset += nRecvNum;
nPackSize -= nRecvNum;
}
//处理数据
cout<<szbuf<<endl;
delete []szbuf;
szbuf = NULL;
}
}
bool CTCPNet::SendData(SOCKET sock,char *szbuf,int nlen)
{
if(sock == INVALID_SOCKET || !szbuf || nlen<0)
return false;
//发送大小
if(send(sock,(char*)&nlen,sizeof(int),0) <= 0)
return false;
//发送内容
if(send(sock,szbuf,nlen,0)<=0)
return false;
return true;
}
void CTCPNet::UnInitNetWork()
{
//销毁线程
m_flagquit = false;
list<HANDLE>::iterator ite = m_lstThread.begin();
while(ite != m_lstThread.end())
if (*ite)
{
if (WAIT_TIMEOUT==WaitForSingleObject(*ite,100))
{
TerminateThread(*ite,-1);
}
CloseHandle(*ite);
*ite = NULL;
}
ite++;
m_lstThread.clear();
//关闭套接字
if(m_sock){
closesocket(m_sock);
m_sock = NULL;
}
WSACleanup();
}
该博客介绍了如何在Windows环境下使用CTCPNet类实现TCP网络的异步事件模型。通过WSAStartup初始化Winsock,创建监听套接字,并使用WSAEventSelect向操作系统注册网络事件。当事件发生时,使用WSAWaitForMultipleEvents和WSAEnumNetworkEvents处理接受连接、读取数据和关闭套接字等操作,实现了非阻塞的网络通信。

被折叠的 条评论
为什么被折叠?



