zoukankan      html  css  js  c++  java
  • MFC中CAsyncSocket和CSocket

      原文链接: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窗体中帮我们建立了一个线程。

  • 相关阅读:
    visual studio2010中C#生成的,ArcGIS二次开发的basetool的dll,注册为COM组件tlb文件,并在arcmap中加载使用
    EPSG:4326
    返回mapcontrol上的已被选择的element
    设置mapcontrol的鼠标样式
    设置mapcontrol的鼠标样式
    2016年6月11日 星期六 晴
    2016年6月10日 星期五 晴
    Docker安装部署
    LVS+DR
    mysql MHA
  • 原文地址:https://www.cnblogs.com/kanite/p/8953577.html
Copyright © 2011-2022 走看看