要进行网络编程就要和Socket打交道,Socket有同步梗阻方法和异步非梗阻方法两种使用,事实上同步和异步在我们编程的生涯中可能遇到了很多,而Socket也没什么出格。虽然同步好用,不费劲,但不能满足一些应用场合,其效率也很低。
或许初涉编程的人不能理解“同步(或梗阻)”和“异步(或非梗阻)”,其实简单两句话就能讲清楚,同步和异步往往都是针对一个函数来说的,“同步”就是函数直到其要执行的成果全部完成时才返回,,而“异步”则是,函数仅仅做一些简单的事情,然后顿时返回,而它所要实现的成果留给另外线程或者函数去完成。例如,SendMessage就是“同步”函数,它不单发送动静到动静行列队伍,还需要期待动静被执行完才返回;相反PostMessage就是个异步函数,它只管发送一个动静,而不管这个动静是否被措置惩罚惩罚,就顿时返回。
一、Socket API
首先应该知道,有Socket1.1供给的原始API函数,和Socket2.0供给的一组扩展函数,两套函数。这两套函数有反复,但是2.0供给的函数成果更强大,函数数量也更多。这两套函数可以灵活混用,分袂包罗在头文件Winsock.h,Winsock2.h,分袂需要引入库wsock32.lib、Ws2_32.lib。
1、默认用作同步梗阻方法,那就是当你从不挪用WSAIoctl()和ioctlsocket()来转变Socket IO模式,也从不挪用WSAAsyncSelect()和WSAEventSelect()来选择需要措置惩罚惩罚的Socket事件。正是由于函数accept(),WSAAccept(),connect(),WSAConnect(),send(),WSASend(),recv(),WSARecv()等函数被用作梗阻方法,所以可能你需要放在专门的线程里,这样以不影响主措施的运行和主窗口的刷新。
2、如果作为异步用,那么措施主要就是要措置惩罚惩罚事件。它有两种措置惩罚惩罚事件的步伐:
第一种,它常关联一个窗口,也就是异步Socket的事件将作为动静发往该窗口,这是由WinSock扩展规范里的一个函数WSAAsyncSelect()来实现和窗口关联。最终你只需要措置惩罚惩罚窗口动静,来收发数据。
第二种,用到了扩展规范里另一个关于事件的函数WSAEventSelect(),它是用事件东西的方法来措置惩罚惩罚Socket事件,也就是,你必需首先用WSACreateEvent()来创建一个事件东西,然后挪用WSAEventSelect()来使得Socket的事件和这个事件东西关联。最终你将要在一个线程里用WSAWaitForMultipleEvents()来期待这个事件东西被触发。这个过程也稍显庞大。
二、CAsyncSocket
看类名就知道,它是一个异步非梗阻Socket封装类,CAsyncSocket::Create()有一个参数指明了你想要措置惩罚惩罚哪些Socket事件,你关心的事件被指定以后,这个Socket默认就被用作了异步方法。那么CAsyncSocket内部到底是如何将事件交给你的呢?
CAsyncSocket的Create()函数,除了创建了一个SOCKET以外,还创建了个CSocketWnd窗口东西,并使用WSAAsyncSelect()将这个SOCKET与该窗口东西关联,以让该窗口东西措置惩罚惩罚来自Socket的事件(动静),然而CSocketWnd收到Socket事件之后,只是简单地回调CAsyncSocket::OnReceive(),CAsyncSocket::OnSend(),CAsyncSocket::OnAccept(),CAsyncSocket::OnConnect()等虚函数。所以CAsyncSocket的派生类,只需要在这些虚函数里添加发送和接收的代码。
简化后,大抵的代码为:
bool CAsyncSocket::Create( long lEvent ) file://参数lEvent是指定你所关心的Socket事件
{
m_hSocket = socket( PF_INET, SOCK_STREAM, 0 ); file://创建Socket自己
CSocketWnd* pSockWnd = new CSocketWnd; file://创建响应事件的窗口,实际的这个窗口在AfxSockInit()挪用时就被创建了。
pSockWnd->Create(...);
WSAAsyncSelect( m_hSocket, pSockWnd->m_hWnd, WM_SOCKET_NOTIFY, lEvent ); file://Socket事件和窗口关联
}
static void PASCAL CAsyncSocket::DoCallBack(WPARAM wParam, LPARAM lParam)
{
CAsyncSocket Socket;
Socket.Attach( (SOCKET)wParam ); //wParam就是触发这个事件的Socket的句柄
int nErrorCode = WSAGETSELECTERROR(lParam); //lParam是错误码与事件码的合成
switch (WSAGETSELECTEVENT(lParam))
{
case FD_READ:
pSocket->OnReceive(nErrorCode);
break;
case FD_WRITE:
pSocket->OnSend(nErrorCode);
break;
case FD_OOB:
pSocket->OnOutOfBandData(nErrorCode);
break;
case FD_ACCEPT:
pSocket->OnAccept(nErrorCode);
break;
case FD_CONNECT:
pSocket->OnConnect(nErrorCode);
break;
case FD_CLOSE:
pSocket->OnClose(nErrorCode);
break;
}
}
CSocketWnd类大抵为:
BEGIN_MESSAGE_MAP(CSocketWnd, CWnd)
ON_MESSAGE(WM_SOCKET_NOTIFY, OnSocketNotify)
END_MESSAGE_MAP()