当前位置导航:炫浪网>>网络学院>>编程开发>>C++教程>>C++进阶与实例

CAsyncSocket,CSocket的使用方法

    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事件[Page]
  {
   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 ); file://wParam/就是触发这个事件的Socket的句柄
   int nErrorCode = WSAGETSELECTERROR(lParam); file://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()

  LRESULT CSocketWnd::OnSocketNotify(WPARAM wParam, LPARAM lParam)

共3页 首页 上一页 1 2 3 下一页 尾页 跳转到
相关内容
赞助商链接