学而不思则罔,思而不学则殆

有其事必有其理, 有其理必有其事

  IT博客 :: 首页 :: 联系 :: 聚合  :: 管理
  85 Posts :: 12 Stories :: 47 Comments :: 0 Trackbacks

这两个类对Winsock API进行了封装,CAsyncSocket是一个异步非阻塞套接字类,CSocket是继承于CAsyncSocket的同步阻塞套接字类。使用这两个类编程无需自己处理Winsock的I/O模型。

CAsyncSocket类提供的唯一抽象就是将与套接字相连的windows消息以回调函数的形式完成,在创建程序时只需要重载这几个函数就可以实现Winsock的I/O操作。

异步模型效率更高,使用起来更灵活,当然也比较难,因为你要手动检测会显得错误或者阻塞的具体情况。当你需要通信的对端系统可能只允许你建立一个SOCKET连接时,就用CAsyncSocket。

CAsyncSocket用于在少量连接时,处理大批量无步骤依赖性的业务。CSocket用于处理步骤依赖性业务,或在可多连接时配合多线程使用。

CAsyncSocket事件处理

当你使用CAsyncSocket::Create创建一个指定兴趣事件的异步套接字时,这些消息究竟是怎么接收和处理的呢?

MFC定义了一个内部的类CSocketWnd,当调用Create函数创建一个套接字时,就会将该套接字连接到一个窗口(CSoketWnd的对象),并且使用WSAAsyncSelect(Winsock I/O模型)将套接字和此窗口对相关联。CAsyncSocket的DoCallBack函数为该窗口的回调函数。这样,当一个网络事件发生时,经过MFC的消息循环,DoCallBack函数会根据不同的事件调用相应的消息处理函数。MFC将这些消息处理函数定义为虚函数,在编程时必须重载需要的消息处理函数CAsyncSocket::OnReceive(),CAsyncSocket::OnSend(),CAsyncSocket::OnAccept(),CAsyncSocket::OnConnect(),CAsyncSocket::OnClose(),CAsyncSocket:OnOutOfBandData()。

客户方在使用CAsyncSocket::Connect()时,往往返回一个WSAEWOULDBLOCK的错误(其它的某些函数调用也如此),实际上这不应该算作一个错误,它是Socket提醒我们,由于你使用了非阻塞Socket方式,所以(连接)操作需要时间,不能瞬间建立。既然如此,我们可以等待呀,等它连接成功为止,于是许多程序员就在调用Connect()之后,Sleep(0),然后不停地用WSAGetLastError()或CAsyncSocket::GetLastError()查看Socket返回的错误,直到返回成功为止。这是一种错误的做法,断言,你不能达到预期目的。事实上,我们可以在Connect()调用之后等待CAsyncSocket::OnConnect()事件被触发,CAsyncSocket::OnConnect()是要表明Socket要么连接成功了,要么连接彻底失败了。至此,我们在CAsyncSocket::OnConnect()被调用之后就知道是否Socket连接成功了,还是失败了。
类似的,Send()如果返回WSAEWOULDBLOCK错误,我们在OnSend()处等待,Receive()如果返回WSAEWOULDBLOCK错误,我们在OnReceive()处等待,以此类推。

总之,尽量使用这些回调函数处理事件,而不要自己查询处理。

CSocket事件处理

主要是要了解他继承了CAsyncSocket之后,如何从异步非阻塞编程同步阻塞模式的。

CSocket在Connect()返回WSAEWOULDBLOCK错误时,不是在OnConnect(),OnReceive()这些事件终端函数里去等待。你先必须明白Socket事件是如何到达这些事件函数里的。这些事件处理函数是靠CSocketWnd窗口对象回调的,而窗口对象收到来自Socket的事件,又是靠线程消息队列分发过来的。总之,Socket事件首先是作为一个消息发给CSocketWnd窗口对象,这个消息肯定需要经过线程消息队列的分发,最终CSocketWnd窗口对象收到这些消息就调用相应的回调函数(OnConnect()等)。
   所以,CSocket在调用Connect()之后,如果返回一个WSAEWOULDBLOCK错误时,它马上进入一个消息循环,就是从当前线程的消息队列里取关心的消息,如果取到了WM_PAINT消息,则刷新窗口,如果取到的是Socket发来的消息,则根据Socket是否有操作错误码,调用相应的回调函数(OnConnect()等)。
大致的简化代码为:

BOOL CSocket::Connect( ... )
{
   if( !CAsyncSocket::Connect( ... ) )
   {
    if( WSAGetLastError() == WSAEWOULDBLOCK ) //由于异步操作需要时间,不能立即完成,所以Socket返回这个错误
    {
     //进入消息循环,以从线程消息队列里查看FD_CONNECT消息,直到收到FD_CONNECT消息,认为连接成功。
     while( PumpMessages( FD_CONNECT ) );
    }
   }
}

CAsyncSocket、CSocket编程

在MFC中进行socket编程需要在应用程序类的Initlnstance中调用AfxSocketlnit初始化套接字。如果使用AppWizard创建应用程序的基本框架时,选中了“WindowsSockets”复选框,那么将自动添加初始化代码。

if (!AfxSocketInit())
    {
        AfxMessageBox(IDP_SOCKETS_INIT_FAILED);
        return FALSE;
    }

CSocket不需要bind(),在Create()时会调用bind()函数绑定此套接字。

CSocket一个重要的用处是可用于串行化技术,要结合CSocketFile 和 CArchive。

服务器端程序:

CSocketFile file(&sockRecv);
CArchive arin(&file,CArchive::load);
CArchive arout(&file,CArchive::load);
arin>>dwValue;   //发送数据
arout <<dwValue;//接收数据

客户端程序:
CSocketFile file(&sockClient);
CArchive arin(&file,CArchive::load);
CArchive arout(&file,CArchive::load);
arin>>dwValue;   //发送数据
arout<<dwValue;//接收数据

posted on 2013-09-27 17:41 易道 阅读(282) 评论(0)  编辑 收藏 引用 所属分类: window 编程
只有注册用户登录后才能发表评论。