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不会主动获取消息。