windows下网络编程(三)——MFC CAsyncSocket 分析
来源:互联网 发布:音频高通滤波算法 编辑:程序博客网 时间:2024/05/02 10:14
一些网络的基本概念
1. 同步:指发送方发出数据后,等收到接收方发回的响应,才发下一个数据包的通信方式
2. 异步:指的是发送方不等接收方响应,便接着发下个数据包的通信方式
3. 阻塞:指调用某函数时,直到该函数完成操作,才返回;否则一直阻塞在该调用上
4. 非阻塞:指调用某操作时,不管操作是否成功都立即返回,而不会挂在该操作上
CAsyncSocket属于异步非阻塞类;
CSocket是MFC在CAsyncSocket基础上派生的一个同步阻塞Socket的封装类(详细内容会在下一篇博文中)
CAsyncSocket异步机制
由于CAsyncSocket采用的是异步非阻塞机制,所以你随时可以发包,也随时可能收到包。
发送、接收函数都是异步非阻塞的,顷刻就能完成,所以收发交错进行着。也正因为如此,仅调用它们并不能保障发送或接收的完成。
例如发送函数Send,调用它可能有3种结果:错误、部分完成、全部完成。其中错误又分两种情况:一种是由各种网络问题导致的失败,你需要马上决定是放弃本次操作,还是启用某种对策;另一种是“忙”,你实际上不用马上理睬。你需要调用GetLastError来判断是哪种情况,GetLastError返回WSAEWOULDBLOCK,代表“忙”,为什么当你Send得
到WSAEWOULDBLOCK却不用理睬呢?因为CAsyncSocket会记得你的SendWSAEWOULDBLOCK了,待发送的数据会写入CAsyncSocket内部的发送缓冲区,并会在不忙的时候自动调用OnSend,发送内部缓冲区里的数据。同样,如果Send只完成了一部分,你也不需要理睬,尚未发送的数据同样会写入CAsyncSocket内部的发送缓冲区,并在不“忙”的时候自动调用OnSend完成发送。
与OnSend协助Send完成工作一样,OnRecieve、OnConnect、OnAccept也会分别协助Recieve、Connect、Accept完成工作。这一切都通过消息机制完成。
在你使用CAsyncSocket之前,必须调用AfxSocketInit初始化WinSock环境,而AfxSocketInit会创建一个隐藏的CSocketWnd对象,由于这个对象由Cwnd派生,因此它能够接收Windows消息。一方面它会接受各个CAsyncSocket的状态报告,另一方面它能捕捉系统发出的各种SOCKET事件。所以它能够成为高层CAsyncSocket对象与WinSock底层之间的桥梁:例如某CAsyncSocket在Send时WSAEWOULDBLOCK了,它就会发送一条消息给CSocketWnd作为报告,CSocketWnd会维护一个报告登记表,当它收到底层WinSock发出的空闲消息时,就会检索报告登记表,然后直接调用报告者的OnSend函数。所以前文所说的CAsyncSocket会自动调用OnXxx,实际上是不对的,真正的调用者是CSocketWnd——它是一个CWnd对象,运行在独立的线程中。
首先CAsyncSocket采用的WSAAsynSelect模型,WSAAsynSelect是一种异步I/O模型,通过该模型,应用程序可以接收以Windows消息为基础的网络事件通知。而我们这里讲的就是CSocketWnd,它是从CWnd继承的。
class CSocketWnd : public CWnd{public:CSocketWnd();protected:LRESULT OnSocketNotify(WPARAM wParam, LPARAM lParam);LRESULT OnSocketDead(WPARAM wParam, LPARAM lParam);DECLARE_MESSAGE_MAP()};
从源码中看出它只处理两个消息,一个是WM_SOCKET_NOTIFY,另一个是WM_SOCKET_DEAD。
我原以为没创建一个Socket就会创建一个CSocketWnd,其实不是,是所有的Socket共享一个CSocketWnd,其实想想也是,要是每创建一个Socket就创建一个CSocketWnd那开销也大了点。
既然是共享的,那它放在哪里呢?
全局中有这样一个结构体:_AFX_SOCK_THREAD_STATE,它保存一个那个共享的窗口句柄:m_hSocketWindow。
当没有创建Socket时,它是Null的。
除了m_hSocketWindow结构体_AFX_SOCK_THREAD_STATE还有几个重要的变量:
1.mCEmbeddedButActsLikePtr<CMapPtrToPtr> m_pmapSocketHandle:记录了CAysncSocket*与没死的SOCKET的映射关系
2.CEmbeddedButActsLikePtr<CMapPtrToPtr> m_pmapDeadSockets:记录了SOCKET与死了的次数的映射关系
3.CEmbeddedButActsLikePtr<CPtrList> m_plistSocketNotifications:记录的所有的SOCKET事件的通知消息
网络事件处理流程
在理解了上面的机制后, 让我们了解下CAsyncSocket的通信流程;
OnSend,除了在对方发送消息来的时候响应外,还会在缓冲区有空闲的时候自动触发;
如果每次发送的数据比较简单,不会造成WASEWOULDBLOCK(阻塞),不会触发OnSend;
因此小数据直接Send就行了,大数据就需要在OnSend判断数据发送是否正确;
如何手动触发OnSend()呢,采用AsyncSelect( FD_WRITE),通知CsocketWnd窗口处理写
数据操作; 同样AsyncSelect(FD_READ)将通知CsocketWnd窗口当有消息传来的时候触发OnRecevie();
BOOL AsyncSelect( long lEvent = FD_READ | FD_WRITE | FD_OOB | FD_ACCEPT | FD_CONNECT | FD_CLOSE ); //请求Socket响应以上事件
消息为何只接收一次
编程中遇到这个问题,发现很多人都遇到过这个问题。
症状如下:Socket连接后只能发送一次消息,发送第二次消息的时候,另一方就接收不到;
原因是:没有让Socket改变响应事件的发式
解决方法:在OnReceive()中,Receive()后调用AsyncSelect(FD_READ);
{
Receive();
AsyncSelect(FD_READ);
}
或则 调用父类的OnReceive()
{
Receive();
CAsyncSocket::OnReceive( nErrorCode);
}
为何服务器Socket不监听
在创建服务器Socket的时候,只有采用SOCK_STREAM(字符流),Listen才能成功;
采用SOCK_DGRAM(数据报文)创建的Socket是面向无连接发式(UDP),所以Listen不成功(有待验证)
Reference:
CAsyncSocket使用总结
浅析CAsyncSocket
CAsyncSocket 的 OnReceive()
CAsyncSocket的OnSend应用
- windows下网络编程(三)——MFC CAsyncSocket 分析
- windows下网络编程(二)——MFC CAsyncSocket
- 网络编程(57)—— Windows下使用CAsyncSocket搭建回声服务端和客户端
- windows下网络编程(四)——MFC CSocket
- MFC下的网络编程(1)CAsyncSocket进行无连接(UDP)通信
- 使用MFC快速实现网络编程 CAsyncSocket
- 使用MFC快速实现网络编程 CAsyncSocket
- 使用MFC快速实现网络编程 CAsyncSocket
- 使用MFC快速实现网络编程 CAsyncSocket
- 使用MFC快速实现网络编程 CAsyncSocket
- 使用MFC快速实现网络编程 CAsyncSocket
- 使用MFC快速实现网络编程 CAsyncSocket
- 使用MFC快速实现网络编程 CAsyncSocket
- 使用MFC快速实现网络编程 CAsyncSocket
- 使用MFC快速实现网络编程 CAsyncSocket
- 使用MFC快速实现网络编程 CAsyncSocket
- MFC快速实现网络编程CAsyncSocket类
- [转]使用MFC快速实现网络编程 CAsyncSocket
- 5个最好的 javascript 调试工具
- openstack swift 客户端
- EXTJS 4 form.submit 超时
- 【原创】商论之:给“美团网”做品牌战略规划!
- 数据魔方与量子统计
- windows下网络编程(三)——MFC CAsyncSocket 分析
- java数据类型转换
- 优秀程序员不得不知道的20个位运算技巧
- 调用excel函数
- Oracle常用查看表结构命令
- Hello ActiveMQ!
- 求表示方法(整数划分问题)
- [转]log4j配置祥解(From:http://www.blogjava.net/kit-soft/archive/2009/08/28/292977.html)
- 从零开始编译webkit 步步为营铁定成功! 之 WINDOWS 8 X64 ON VS 2010