CAsyncSocket使用总结

转载请注明出处blog.csdn.net/tianhai110

这几天都在研么MFC的套接字类CAsyncSocket的用法, 将一些心得和实践中遇到的问题总结一下。

 

一、        一些网络的基本概念

1.       同步发送方发出数据后,等接收方发回响应以后才发下一个数据包的通讯方式。

2.       异步:发送方发出数据后,不等接收方发回响应,接着发送下个数据包的通讯方式。

3.       阻塞:指调用某函数时,直到该函数完成操作,才返回;否则一直阻塞在该调用上

4.       非阻塞:指调用某操作时,不管操作是否成功都立即返回,而不会挂在该操作上

 

CAsyncSocket属于异步非阻塞类;

CSocketMFCCAsyncSocket基础上派生的一个同步阻塞Socket的封装类

 

二、       CAsyncSocket的使用(伪码)

服务器端:

 

m_pListSocket    =     new    CAsyncSocket(); 
m_pListSocket
->  Create( 端口,地址);                    //  创建

m_pListSocket
-> Listen();                             //  开始监听

 

m_pListSocket::OnAccept( )                       
//  有客户端请求连接时响应

{

m_pSocket   
=   new   CAsyncSocket(); 
m_pListSocket
-> Accept(m_pSocket);       // 建立通信,成功后m_pSocket就用于发送和接受.

 

// m_pSocket 就相当于连接的那个客户端了

}


 

m_pSocket::OnRecive( 
int  nErrorCode)

{

         
if( nErrorCode == 0)

{

         Recevie();                            
// 接受客户端发送来的信息

}


         CAsyncSocket::OnRecive( nErrorCode)

}


 

m_pSocket::OnSend()

{

         Send();                        
// 发送信息,该事件触发条件见下节

}


 

m_pListSocket
-> Close();

 

delete m_pListSocket;

delete m_pSocket;

 

 

客户端:

 

m_pClientSocket    =     new    CAsyncSocket(); 
m_pClientSocket 
->  Create( 端口,地址);                       //  创建

m_pClientSocket
-> Connect();                                          //  连接服务器,最终将触发服务器的OnAccept();

m_ pClientSocket::OnConnect()                                            
//  当连接上服务器

{

         

}


 

m_ pClientSocket::OnRecive( 
int  nErrorCode)

{

         
if( nErrorCode == 0)

{

         Recevie();                            
// 接受客户端发送来的信息

}


         CAsyncSocket::OnRecive( nErrorCode)

}


 

m_pClientSocket::OnSend( 
int  nErrorCode)

{

         Send();                                  
// 发送信息

}


 

m_pClientSocket
-> Close();

 

三、       CAsyncSocket异步机制

由于CAsyncSocket采用的是异步非阻塞机制,所以你随时可以发包,也随时可能收到包。

发送、接收函数都是异步非阻塞的,顷刻就能完成,所以收发交错进行着。也正因为如此,仅调用它们并不能保障发送或接收的完成。

 

例如发送函数Send,调用它可能有3种结果:错误、部分完成、全部完成。其中错误又分两种情况:一种是由各种网络问题导致的失败,你需要马上决定是放弃本次操作,还是启用某种对策;另一种是,你实际上不用马上理睬。你需要调用GetLastError来判断是哪种情况,GetLastError返回WSAEWOULDBLOCK,代表,为什么当你Send
WSAEWOULDBLOCK却不用理睬呢?因为CAsyncSocket会记得你的SendWSAEWOULDBLOCK了,待发送的数据会写入CAsyncSocket内部的发送缓冲区,并会在不忙的时候自动调用OnSend,发送内部缓冲区里的数据。同样,如果Send只完成了一部分,你也不需要理睬,尚未发送的数据同样会写入CAsyncSocket内部的发送缓冲区,并在不“忙”的时候自动调用OnSend完成发送。

OnSend协助Send完成工作一样,OnRecieveOnConnectOnAccept也会分别协助RecieveConnectAccept完成工作。这一切都通过消息机制完成。

 

在你使用CAsyncSocket之前,必须调用AfxSocketInit初始化WinSock环境,而AfxSocketInit会创建一个隐藏的CSocketWnd对象,由于这个对象由Cwnd派生,因此它能够接收Windows消息。一方面它会接受各个CAsyncSocket的状态报告,另一方面它能捕捉系统发出的各种SOCKET事件。所以它能够成为高层CAsyncSocket对象与WinSock底层之间的桥梁:例如某CAsyncSocketSendWSAEWOULDBLOCK了,它就会发送一条消息给CSocketWnd作为报告,CSocketWnd会维护一个报告登记表,当它收到底层WinSock发出的空闲消息时,就会检索报告登记表,然后直接调用报告者的OnSend函数。所以前文所说的CAsyncSocket会自动调用OnXxx,实际上是不对的,真正的调用者是CSocketWnd——它是一个CWnd对象,运行在独立的线程中。

 

四、       网络事件处理流程

 

在理解了上面的机制后, 让我们了解下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);

 

 

 

 

Void CMyAsyncSocket::OnReceive( int  nErrorCode)

{

         Receive();

         AsyncSelect(FD_READ);

}

 

或则 调用父类的OnReceive()

 

Void CMyAsyncSocket::OnReceive(  int  nErrorCode)

{

         Receive();

 

         CAsyncSocket::OnReceive( nErrorCode);

}


 

六、   为何服务器Socket不监听

在创建服务器Socket的时候,只有采用SOCK_STREAM(字符流),Listen才能成功;

采用SOCK_DGRAM(数据报文)创建的Socket是面向无连接发式(UDP),所以Listen不成功(有待验证)

 

  • 0
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
是的,CAsyncSocket可以实现多线程。CAsyncSocketMFC中用于异步套接字通信的类,它可以在后台线程中进行网络通信操作,从而避免阻塞主线程。 使用CAsyncSocket进行多线程通信的一般步骤如下: 1. 创建一个派生自CAsyncSocket的自定义套接字类,并重写其中的虚拟函数,如OnAccept、OnReceive等,以处理相应的事件。 2. 在主线程中创建一个CAsyncSocket对象,并调用其Create函数来创建套接字。 3. 在主线程中调用CAsyncSocket对象的AsyncConnect、AsyncSend等函数来进行异步通信操作。 4. 在后台线程中创建一个独立的消息循环,并在其中调用CAsyncSocket对象的AsyncSelect函数来接收并处理套接字事件。 5. 后台线程中的消息循环会在接收到套接字事件时,调用自定义套接字类中相应的虚拟函数来处理事件,可以在这些函数中执行具体的业务逻辑。 以下是一个简单的示例代码,演示了如何使用CAsyncSocket实现多线程通信: ```cpp // 自定义套接字类 class CMySocket : public CAsyncSocket { public: virtual void OnAccept(int nErrorCode); virtual void OnReceive(int nErrorCode); }; // 主线程中创建CAsyncSocket对象 CMySocket clientSocket; clientSocket.Create(); // 启动后台线程 AfxBeginThread(SocketThreadFunc, &clientSocket); // 后台线程函数 UINT SocketThreadFunc(LPVOID pParam) { CMySocket* pSocket = (CMySocket*)pParam; CMyWinApp* pApp = (CMyWinApp*)AfxGetApp(); // 在后台线程中创建消息循环 MSG msg; while (pApp->PumpMessage()) { if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) { if (!pApp->GetMessage(&msg, NULL, 0, 0)) { break; } pApp->DispatchMessage(&msg); } } return 0; } // 自定义套接字类的事件处理函数 void CMySocket::OnAccept(int nErrorCode) { // 处理连接请求 } void CMySocket::OnReceive(int nErrorCode) { // 处理接收数据 } ``` 上述代码中,主线程中创建了一个CMySocket对象,然后启动了一个后台线程。在后台线程中,使用消息循环来接收并处理套接字事件。当有连接请求或数据接收时,会调用自定义套接字类中相应的虚拟函数来处理事件。 请注意,上述代码只是一个简单示例,实际使用时可能需要进行错误处理和更全面的逻辑。同时,还需要注意在后台线程中使用异步通信函数时,需要保证线程安全性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值