CAsyncSocketEx浅析

CAsyncSocketEx是Tim Kosse发布到codeproject.com一个开源库。利用消息驱动,通过CAsyncSocketEx类封装win32的网络接口。

以下通过一个简化的CAsyncSocketEx对其实现进行初步解析。

通过CAsyncSocketEx的create和消息处理函数WindowProc可以很清晰的整理出整个库的脉络。

 

BOOL CAsyncSocketEx::Create( UINT nSocketPort /*=0*/, int nSocketType /*=SOCK_STREAM*/, long lEvent /*=FD_READ | FD_WRITE | FD_OOB | FD_ACCEPT | FD_CONNECT | FD_CLOSE*/, LPCTSTR lpszSocketAddress /*=NULL*/ )
{
	...//对工作环境进行必要的检测
	BOOL res=InitAsyncSocketExInstance(); //创建一个隐形窗口
	...
	SOCKET hSocket=socket(AF_INET, nSocketType, 0); //创建一个sokect
	if (hSocket==INVALID_SOCKET)
			return FALSE;
	m_SocketData.hSocket=hSocket; //将创建的用hsocket保存socket
	AttachHandle(hSocket);		//将socket与窗口关联起来
	if (!AsyncSelect(lEvent)) //将注册网络事件
	{
		Close();
		return FALSE;
	}
	if (!Bind(nSocketPort, lpszSocketAddress))  //绑定本地端口
	{
		Close();
		return FALSE;
	}
	return TRUE;
}
static LRESULT CALLBACK WindowProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
	if (message>=WM_SOCKETEX_NOTIFY) //是否网络事件
	{
		//Verify parameters
		ASSERT(hWnd);
		CAsyncSocketExHelperWindow *pWnd=(CAsyncSocketExHelperWindow *)GetWindowLong(hWnd, GWL_USERDATA);
		ASSERT(pWnd);

		if (message<static_cast<UINT>(WM_SOCKETEX_NOTIFY+pWnd->m_nWindowDataSize)) //Index is within socket storage
		{
			//Lookup socket and verify if it's valid
			CAsyncSocketEx *pSocket=pWnd->m_pAsyncSocketExWindowData[message-WM_SOCKETEX_NOTIFY].m_pSocket; //找到对应CAsyncSocketEx对象
			SOCKET hSocket=wParam;
			if (!pSocket)
				return 0;
			if (hSocket==INVALID_SOCKET)
				return 0;
			if (pSocket->m_SocketData.hSocket!=hSocket)
				return 0;

			int nEvent=lParam&0xFFFF;
			int nErrorCode=lParam>>16;

			//Dispatch notification

			//Dispatch to CAsyncSocketEx instance
			switch (nEvent) //根据各个事件调用各个子类对象的处理函数
			{
				case FD_READ:
					{
						DWORD nBytes;
						if (!pSocket->IOCtl(FIONREAD, &nBytes))
						nErrorCode = WSAGetLastError();
						if (nBytes != 0 || nErrorCode != 0)
						pSocket->OnReceive(nErrorCode);
					}
					break;
				case FD_FORCEREAD: //Forceread does not check if there's data waiting
					pSocket->OnReceive(nErrorCode);
					break;
				case FD_WRITE:
					pSocket->OnSend(nErrorCode);
					break;
				case FD_CONNECT:
					pSocket->OnConnect(nErrorCode);
					break;
				case FD_ACCEPT:
					pSocket->OnAccept(nErrorCode);
					break;
				case FD_CLOSE:
					pSocket->OnClose(nErrorCode);
					break;
			}
		}

		return 0;
	}
	else if (message == WM_USER+1) //连接事件
	{
		//WSAAsyncGetHostByName reply

		//Verify parameters
		ASSERT(hWnd);
		CAsyncSocketExHelperWindow *pWnd=(CAsyncSocketExHelperWindow *)GetWindowLong(hWnd, GWL_USERDATA);
		ASSERT(pWnd);

		CAsyncSocketEx *pSocket;
		for (int i=0; i<pWnd->m_nWindowDataSize; i++)  //遍历窗口关联的CAsyncSocketEx对象
		{
			pSocket = pWnd->m_pAsyncSocketExWindowData[i].m_pSocket;
			if (pSocket && pSocket->m_hAsyncGetHostByNameHandle &&
					pSocket->m_hAsyncGetHostByNameHandle == (HANDLE)wParam)
				break;
		}
		if (i == pWnd->m_nWindowDataSize)
			return 0;

		int nErrorCode = lParam >> 16;
		int len = lParam % 0xFFFF;
		if (nErrorCode)
		{
			pSocket->OnConnect(nErrorCode);
			return 0;
		}

		SOCKADDR_IN sockAddr;
		memset(&sockAddr,0,sizeof(sockAddr));
		sockAddr.sin_family=AF_INET;
		sockAddr.sin_addr.s_addr = ((LPIN_ADDR)((LPHOSTENT)pSocket->m_pAsyncGetHostByNameBuffer)->h_addr)->s_addr;

		sockAddr.sin_port = htons(pSocket->m_nAsyncGetHostByNamePort);

		BOOL res = pSocket->Connect((SOCKADDR*)&sockAddr, sizeof(sockAddr)); //尝试连接
		delete [] pSocket->m_pAsyncGetHostByNameBuffer;
		pSocket->m_pAsyncGetHostByNameBuffer=0;
		pSocket->m_hAsyncGetHostByNameHandle=0;

		if (!res)
			if (GetLastError()!=WSAEWOULDBLOCK)
				pSocket->OnConnect(GetLastError()); //连接处理函数
		return 0;
	}
	return DefWindowProc(hWnd, message, wParam, lParam);
}

由此可以看出这个库的主要脉络:创建一个隐形的窗口是为了利用消息驱动,

然后把sokect与窗口关联起来,一旦收到网络消息则找到相对应的socket,相应事件调用相对应的函数。

在实际使用此类库过程中应实现一个继承自CAsyncSocketEx的类。实现各自的处理函数。

 

层的概念:在CAsyncSocketExLayer中有一个CAsyncSocketEx类的成员变量m_pOwnerSocket作为在此类库中最底层的接口,其上每增加一层,作为都加入到后一链表结点使用双向链表实现。CAsyncSocketExLayer *m_pNextLayer;CAsyncSocketExLayer *m_pPrevLayer;没个都以其之前的层为数据接口,每次收到网络事件均根据协议解析后作为下一层的数据源。

ps:若程序中不包含窗口,则需要自己实现抓取并分派消息的动作,CAsyncSocketExHelperWindow不会主动获取消息。

简化版下载地址

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值