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不会主动获取消息。
简化版下载地址