原文链接:https://blog.csdn.net/libaineu2004/article/details/40395917

  摘要部分重点:

  1、CAsyncSocket类逐个封装了WinSock API,为高级网络程序员提供了更加有力而灵活的方法。

  2、CSocket类从CAysncSocket继承而来,CSocket类和CSocketFile类可以与CArchive类一起合作来管理发送和接收的数据,这使管理数据收发更加便利。CSocket对象提供阻塞模式,这对于CArchive的同步操作是至关重要的。阻塞函数(如Receive()、Send()、ReceiveFrom()、SendTo() 和Accept())直到操作完成后才返回控制权,因此如果需要低层控制和高效率,就使用CAsyncSock类;如果需要方便,则可使用CSocket类。

  3、CAsyncSocket与CSocket的区别----前者是异步通信,后者是同步通信;前者是非阻塞模式,后者是阻塞模式。

  4、CAsyncSocket的异步机制

  你随时可以发包,也随时可能接收到包。发送、接收函数都是异步非阻塞的,顷刻就能返回,所以收发交错进行着,你可以一直工作,保持很高的效率,但是,正因为发送、接收函数都是异步非阻塞的,所以仅调用他们并不能保障发送或接收的完成。例如发送函数Send,调用它可能有4种结果:

  a、错误,Send()==SOCKET_ERROR,GetLastError()!=WSAEWOULDBLOCK,这种情况可能由各种网络问题导
致,你需要马上决定是放弃本次操作,还是启用某种对策。

  b、忙,Send()==SOCKET_ERROR,GetLastError()==WSAEWOULDBLOCK,导致这种情况的原因是,你的发送缓冲区已被填满或对方的接受缓冲区已被填满。这种情况你实际上不用马上理睬。因为CAsyncSocket会记得你的Send WSAEWOULDBLOCK了,待发送的数据会写入CAsyncSocket内部的发送缓冲区,并会在不忙的时候自动调用OnSend,发送内部缓冲区里的数据。
  d、部分完成,0<Send(pBuf,nLen)<nLen,导致这种情况的原因是,你的发送缓冲区或对方的接收缓冲区中剩余的空位不足以容纳你这次需要发送的全部数据。处理这种情况的通常做法是继续发送尚未发送的数据直到全部完成或WSAEWOULDBLOCK。这种情况很容易让人产生疑惑,既然缓冲区空位不足,那么本次发送就已经填满了缓冲区,干嘛还要继续发送呢,就像WSAEWOULDBLOCK了一样直接交给OnSend去处理剩余数据的发送不是更合理吗?然而很遗憾,CAsyncSocket不会记得你只完成了部分发送任务从而在合适的时候触发OnSend,因为你并没有WSAEWOULDBLOCK。你可能认为既然已经填满缓冲区,继续发送必然会WSAEWOULDBLOCK,其实不然,假如WSAEWOULDBLOCK是由于对方读取接收缓冲区不及时引起的,继续发送的确很可能会WSAEWOULDBLOCK,但假如WSAEWOULDBLOCK是由于发送缓冲区被填满,就不一定了,因为你的网卡处理发送缓冲区中数据的速度不见得比你往发送缓冲区拷贝数据的速度更慢,这要取决与你竞争CPU、内存、带宽资源的其他应用程序的具体情况。假如这时候CPU负载较大而网卡负载较低,则虽然刚刚发送缓冲区是满的,你继续发送也不会WSAEWOULDBLOCK。
  e、完成,Send(pBuf,nLen)==nLen
  5、OnSend与OnReceived的逻辑完全不同

  使用CAsyncSocket时,Send流程和Recieve流程是不同的,不理解这一点就不可能顺利使用CAsyncSocket。MSDN对CAsyncSocket的解释很容易让你理解为:只有OnSend被触发时你Send才有意义,你才应该Send,同样只有OnRecieve被触发时你才应该Recieve。很不幸,你错了:你会发现,连接建立的同时,OnSend就第一次被触发了,嗯,这很好,但你现在还不想Send,你让OnSend返回,干点其他的事情,等待下一次OnSend试试看?实际上,你再也等不到OnSend被触发了。因为,除了第一次以外,OnSend的任何一次触发,都源于你调用了Send,但碰到了WSAEWOULDBLOCK!所以,使用CAsyncSocket时,针对发送的流程逻辑应该是:你需两个成员变量,一个发送任务表,一个记录发送进度。你可以,也应该,在任何你需要的时候,主动调用Send来发送数据,同时更新任务表和发送进度。而OnSend,则是你的负责擦屁股工作的助手,它被触发时要干的事情就是根据任务表和发送进度调用Send继续发。若又没能将任务表全部发送完成,更新发送进度,退出,等待下一次OnSend;若任务表已全部发送完毕,则清空任务表及发送进度。使用CAsyncSocket的接收流程逻辑是不同的:你永远不需要主动调用Recieve,你只应该在OnRecieve中等待。由于你不可能知道将要抵达的数据类型及次序,所以你需要定义一个已收数据表作为成员变量来存储已收到但尚未处理的数据。每次OnRecieve被触发,你只需要被动调用一次Recieve来接受固定长度的数据,并添加到你的已收数据表后。然后你需要扫描已收数据表,若其中已包含一条或数条完整的可解析的业务数据包,截取出来,调用业务处理窗口的处理函数来处理或作为消息参数发送给业务处理窗口。而已收数据表中剩下的数据,将等待下次OnRecieve中被再次组合、扫描并处理。

  我注:

  上面第5点说了这么多,结合我个人实际而言,我一般会这么写这段程序,使用原始api,建立一个发送数据队列(队列操作保持原子性),建立一个线程,在线程里循环检查队列,发送每一包数据。

  上面第5条说的是同一个意思,你需要一个外在的发送数据队列,和一个发送控制(在我这里就是数据包的个数--这是发送控制的关键变量)。CAsyncSocket::Send函数参数里是不约定数据包大小的,也就是我一次发送1M数据,调用这个函数也是可以的。但底层发送区不会有这么大,必然导致Send函数阻塞,我需要做的是分包数据块,初始化发包个数N,调用Send一个数据包后,N-1,当前面一包数据发送完后,OnSend函数就会被(CSocketWnd)调用。在OnSend函数里根据发包个数N与发送数据块缓存继续发送数据就可以了。

  

  (以下这段话结合了《把脉VC++》这本书里的内容和上述文档)

  MFC CAsyncSocket和CSocket完成了对WinSock的包装,在原始api编程时,接收数据必定是开线程操作的,到了这里就不需要了,原理在于这里使用了异步机制、Windows 窗口消息机制、在对应的Windows窗体中帮我们建立了一个线程。

MFC中CAsyncSocket和CSocket的更多相关文章

  1. [转载]CAsyncSocket及CSocket注解

    MFC疑难注解:CAsyncSocket及CSocket MFC对SOCKET编程的支持其实是很充分的,然而其文档是语焉不详的.以至于大多数用VC编写的功能稍复杂的网络程序,还是使用API的.故CAs ...

  2. MFC中使用FLASH

    一.准备工作 第一步:下载并安装Adobe Flash Player. 从官方网站(http://get.adobe.com/cn/flashplayer/)上下载最新的Flash Player(大约 ...

  3. MFC中换行实现

    在mfc中编辑框允许输入多行时,换行符被表示为<归位><换行>即"\r\n",用ascii码表示为13 10 如果为编辑框中想要输入换行,就请将编辑框的属性 ...

  4. MFC中的各种DC区别

    转载自:xntop的<区别MFC中的CClientDC.CWindowDC.CPaintDC.CMetaFileDC> CClientDC及其子类 1. CClientDC类只能在客户区绘 ...

  5. VC++ MFC中如何将应用程序的配置信息保存到注册表中(二)

    在上一篇中介绍了几个写入注册表数据和读取注册表数据的接口,并介绍了使用方法. 这一片教你如何使得你的应用程序在下次打开时保持上一次关闭前的状态. 在上一篇添加的代码的基础上,要添加WM_CLOSE消息 ...

  6. MFC中对话框类(Dialog)的应用

    转载http://hi.baidu.com/jackywdx/item/feee8041d2c2e12310ee1e85 Windows应用程序通常是通过对话框接收用户输入.向用户输出信息,本节介绍应 ...

  7. MFC中添加消息响应函数

    转自:http://blog.csdn.net/eddy_liu/article/details/8474677 目前,用MFC设计的Windows应用程序几乎都采用文档/视图结构.这种程序框架与简单 ...

  8. MFC中使用Duilib--2

    在上一篇文章"MFC中使用Duilib--1"中, 没有用到资源文件,即xml,本篇讲怎样加载文件. 1.  在exe输出目录下,创建一个skin目录,里面放入需要用到的图片文件, ...

  9. MFC中,如何自定义用户消息

    1.用处 在多个类之间传递消息.当需要响应用户操作,本类却无法实现时,可以向系统发出消息.然后让系统中的需要的位置实现它. 2.方法 2.1定义这个消息,并让拥有者发送这个这个消息,传递一个整型参数 ...

随机推荐

  1. java解决前后台跨域问题

    这篇文章主要介绍了使用Cors实现JavaWeb跨域请求问题的方法,非常不错,具有参考借鉴价值,需要的朋友可以参考下 之前用jsonp 解决跨域问题,现在用CORS实现跨域请求解决java 跨域问题: ...

  2. 【Java工具】在代码头部加版权

    import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io ...

  3. uva 11178二维几何(点与直线、点积叉积)

    Problem D Morley’s Theorem Input: Standard Input Output: Standard Output Morley’s theorem states tha ...

  4. 湘潭oj1203/邀请赛A题 数论+java大数

    求 n%1+n%2+n%3+n%4+.........n%n=,n<=10^12次. 开始时盲目地找规律,结果一无所获.后来经学长点拨,天资愚钝,搞了半天才明白. 先上图: 对于该题,在求区间( ...

  5. hdu3987,最小割时求最少割边数

    题意:求最小割时候割边最少的数量.算法:先求dinic一遍,跑出残网络,再把该网络中满流量(残量为0)的边 残量改为1,其他边残量改为无穷,则再跑一次最大流,所得即为答案.(思,最小割有喝多组,但是要 ...

  6. js等待提示通用类

    function WaitingTip (options){       if(!options){                options = {                contain ...

  7. ORA-01940: cannot drop a user that is currently connected 问题解析

    https://www.linuxidc.com/Linux/2012-12/76448.htm

  8. spring data jpa使用原生sql查询

    spring data jpa使用原生sql查询 @Repository public interface AjDao extends JpaRepository<Aj,String> { ...

  9. 74.Search in a 2D Matrix

    /* * 74.Search in a 2D Matrix * 12.5 by Mingyang * 这里面的对应挺巧的: * 这个就是将2D矩阵转化成1行数组的对应表.所以对于二分查找法的初始值为: ...

  10. 关于linter

    各类代码都有规则格式检查工具,称之为linter 比如:csslint/jslint/eslint/pylint sumlime提供了一个linter的框架SublimeLinter,在里面可以使用各 ...