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

socketOverlappedEvent.h

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

	文件名: 	socketOverlappedEvent.h
	描述:
			重叠I/O 事件通知模式
	作者:		fengsh
	QQ  :		19985430
	电子邮件:	[email protected]
	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;
}



你可能感兴趣的:(模型设计与实践---(六)重叠IO,事件通知(Overlap Event))