模型设计与实践---(六)重叠IO,事件通知(Overlap Event)

时间:2022-07-08 00:16:36

socketOverlappedEvent.h

/********************************************************************
创建时间: 2013/04/11

文件名: socketOverlappedEvent.h
描述:
重叠I/O 事件通知模式
作者: fengsh
QQ : 19985430
电子邮件: fengsh998@163.com
Blog : http://blog.csdn.net/fengsh998
@CopyRight fengsh


WSASocket、AcceptEx、WSACreateEvent、WSAWaitForMultipleEvents、WSAResetEvent、WSAGetOverlappedResult、WSARecv、WSASetEvent。具体使用方法各位可以看下面的例子或者查一下MSDN,里面有介绍。
首先我们先看看重叠模型的使用步骤:
创建一个带Overlapped标志的Socket句柄(其实也可以是文件句柄);
准备好一个与这个Socket句柄对应的Overlapped对象,并准备好事件句柄,以便让后面的WaitForXXX函数使用
使用支持Overlapped的函数对上面的句柄作操作(向这个句柄投递请求),这些函数都有一个共同点,就是它们都有个参数是Overlapped类型的。例如AcceptEx、WriteFile、WSARecv等;
在一个循环或者一个线程中使用WSAWaitForMultipleEvents来等待事件的发生;
事件发生后,使用WSAGetOverlappedResult 来取得对应的结果并做处理。
继续向句柄投递操作请求(向它发东西啊,收东西啊等等的!)。
*********************************************************************/
#pragma once
#include "socketbase.h"
#include "SocketConst.h"

typedef struct tagOVERLAPPED
{
WSAOVERLAPPED overlap;
WSABUF Buffer;
char buf[BUFFERMAX];
DWORD dwNumOfBytesRecved;
DWORD Flags;
}PER_IO_OPERATION_DATA,*LPPER_IO_OPERATION_DATA;

typedef struct tagLISTENDATA
{
int iCount;
SOCKET stAddr[MAXIMUM_WAIT_OBJECTS];
WSAEVENT wtHandle[WSA_MAXIMUM_WAIT_EVENTS];
LPPER_IO_OPERATION_DATA optiondata[MAXIMUM_WAIT_OBJECTS];
}LISTENDATA,*PTR_LISTENDATA;

class CSocketOverlappedEvent :
public CSocketBase
{
public:
CSocketOverlappedEvent(void);
~CSocketOverlappedEvent(void);

int startServer() ;
int stopServer() ;
bool sendtext(const std::string& content) ;

//调整数组长度
void adjustDataSize(int idx);
void deleteDataByIndex(int idx);
void destoryAllData();
void doOverlapDataListen(SOCKET client);
bool terminated();

PTR_LISTENDATA m_data;
private:
void *wcid;
void *dataid;
bool m_bterminate;
};


socketOverlappedEvent.cpp

#include "socketOverlappedEvent.h"
#include "socketThread.h"

//客户端连接等待线程
static void* wait_client_thread(void* param);
static void* data_listen_thread(void* param);

CSocketOverlappedEvent::CSocketOverlappedEvent(void):m_bterminate(false)
{
m_data = (PTR_LISTENDATA)malloc(sizeof(LISTENDATA));
memset(m_data,0,sizeof(LISTENDATA));
}

CSocketOverlappedEvent::~CSocketOverlappedEvent(void)
{
delete[] m_data;
}

int CSocketOverlappedEvent::startServer()
{
if (initSocket())
{
m_bterminate = false;

socket_thread_create(&wcid,wait_client_thread,(void*)this);

socket_thread_create(&dataid,data_listen_thread,(void*)this);
}
return -1;
}

int CSocketOverlappedEvent::stopServer()
{
dispatchcallback(cbServerClose,NULL);

m_bterminate = true;
closesocket(m_listenSocket);

destoryAllData();
return -1;
}

bool CSocketOverlappedEvent::sendtext( const std::string& content )
{
for (int i = 0;i < m_data->iCount; i++)
{
sendData(m_data->stAddr[i],content);
}
return true;
}

void CSocketOverlappedEvent::adjustDataSize(int idx)
{
//当前IDX与最后一个赋值
if( idx < m_data->iCount - 1)
{
m_data->stAddr[idx] = m_data->stAddr[m_data->iCount-1];
m_data->wtHandle[idx] = m_data->wtHandle[m_data->iCount-1];
m_data->optiondata[idx] = m_data->optiondata[m_data->iCount-1];
}

m_data->optiondata[--m_data->iCount]=NULL;
}

void CSocketOverlappedEvent::deleteDataByIndex( int idx )
{
closesocket(m_data->stAddr[idx]);
WSACloseEvent(m_data->wtHandle[idx]);
HeapFree(GetProcessHeap(),0,m_data->optiondata[idx]);
}

void CSocketOverlappedEvent::destoryAllData()
{
int icount = m_data->iCount;
for (int i=0;i < icount; i++)
{
deleteDataByIndex(i);
}

memset(m_data,0,sizeof(LISTENDATA));
}

/* WSAGetLastError();
WSANOTINITIALISED 在调用本API之前应成功调用WSAStartup()。
WSAENETDOWN 网络子系统失效。
WSAENOTCONN 套接口未连接。
WSAEINTR 通过WSACancelBlockingCall()函数取消(阻塞)调用。
WSAEINPROGRESS 一个阻塞的WinSock调用正在进行中,或者服务提供者仍在处理一个回调函数
WSAENETRESET 由于远端的复位造成连接的中止。
WSAENOTSOCK 描述字不是一个套接口。
WSAEOPNOTSUPP 设置了MSG_OOB,但是该套接口不是诸如SOCK_STREAM流类型的,与套接口相关的通讯域不支持带外数据,或者套接口是单向的,只支持发送操作。
WSAESHUTDOWN 套接口已经关闭;一个套接口以SD_RECEIVE或 SD_BOTH的how参数shutdown()后,无法进行WSARecv()调用。
WSAEWOULDBLOCK 重叠套接口:太多重叠的输入/输出请求。非重叠套接口:套接口被标志为非阻塞,但是操作不能立即完成。
WSAEINVAL 套接口未用bind()捆绑,或者套接口未用重叠标志创建。
WSAECONNABORTED 由于超时或其他错误导致虚电路中止。
WSAECONNRESET 虚电路被远端复位。
WSAEDISCON 远端优雅的结束了连接。
WSA_IO_PENDING 成功启动一个重叠操作,过后将有完成指示。
*/

void CSocketOverlappedEvent::doOverlapDataListen( SOCKET client )
{
int idx = m_data->iCount;
m_data->stAddr[idx] = client;
//分配内存
m_data->optiondata[idx] =
(LPPER_IO_OPERATION_DATA)HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,sizeof(PER_IO_OPERATION_DATA));
m_data->optiondata[idx]->Buffer.len = BUFFERMAX;
m_data->optiondata[idx]->Buffer.buf = m_data->optiondata[idx]->buf;
//创建事件
m_data->wtHandle[idx] = m_data->optiondata[idx]->overlap.hEvent = WSACreateEvent();

//监听网络事件
WSARecv(m_data->stAddr[idx],&m_data->optiondata[idx]->Buffer,1,
&m_data->optiondata[idx]->dwNumOfBytesRecved,&m_data->optiondata[idx]->Flags,
&m_data->optiondata[idx]->overlap,NULL);

m_data->iCount++;
}

bool CSocketOverlappedEvent::terminated()
{
return m_bterminate;
}

static void* wait_client_thread(void *param)
{
CSocketOverlappedEvent *overlapped = (CSocketOverlappedEvent*)param;

DISPATCHPARAM dp;
memset(&dp,0,sizeof(DISPATCHPARAM));
SOCKET socketClient;

while(true)
{

SOCKADDR_IN addrClient;
int addrClientSize=sizeof(SOCKADDR_IN);
socketClient=accept(overlapped->m_listenSocket,(struct sockaddr*)&addrClient,&addrClientSize);
if (socketClient==INVALID_SOCKET)
{
socketClient = NULL;
if (overlapped->checkSocketError(WSAGetLastError()))
{
break;
}
continue;
}
else
{
overlapped->doOverlapDataListen(socketClient);

strcpy(dp.info.ip,inet_ntoa(addrClient.sin_addr));
dp.info.port = addrClient.sin_port;
overlapped->dispatchcallback(cbHasConnect,&dp);
}
}

return 0;
}

static void* data_listen_thread(void* param)
{
CSocketOverlappedEvent *overlapped = (CSocketOverlappedEvent*)param;

DISPATCHPARAM dp;
memset(&dp,0,sizeof(DISPATCHPARAM));

int nRet,index;
DWORD cbTransfered;

while(true)
{

if (overlapped->terminated())
{
break;
}

nRet = WSAWaitForMultipleEvents(overlapped->m_data->iCount,overlapped->m_data->wtHandle,FALSE,1000,FALSE);

if (nRet == WSA_WAIT_FAILED || nRet == WSA_WAIT_TIMEOUT)
{
continue;
}

index = nRet - WAIT_OBJECT_0;

//重设事件,已重新接收消息
WSAResetEvent(overlapped->m_data->wtHandle[index]);

WSAGetOverlappedResult(overlapped->m_data->stAddr[index],&overlapped->m_data->optiondata[index]->overlap,
&cbTransfered,true,&overlapped->m_data->optiondata[index]->Flags);

if (cbTransfered == 0)
{
overlapped->deleteDataByIndex(index);
overlapped->adjustDataSize(index);
}
else
{
strcpy(dp.msg,overlapped->m_data->optiondata[index]->buf);
//回调到界面
overlapped->dispatchcallback(cbCommunication,&dp);
//回调到接收完成
overlapped->dispatchcallback(cbRecviced,NULL);

//ZeroMemory(&overlapped->m_data->optiondata[index]->overlap,sizeof(OVERLAPPED));
WSARecv(overlapped->m_data->stAddr[index],&overlapped->m_data->optiondata[index]->Buffer,1,
&overlapped->m_data->optiondata[index]->dwNumOfBytesRecved,&overlapped->m_data->optiondata[index]->Flags,
&overlapped->m_data->optiondata[index]->overlap,NULL);
}

}

return 0;
}