Windows网络编程Select模型的封装和queue作为数据缓存的使用

来源:互联网 发布:佳词网络怎么样 编辑:程序博客网 时间:2024/06/08 07:19

首先是对Select网络模型的封装,由于会用到数据缓存,所以接收到的数据需要对应一个socket套接字,这样回复数据的时候才知道对象。

[cpp] view plain copy
 print?
  1. #pragma once  
  2.   
  3. //存储从某个套接字接受到的数据,通过套接字可以发送处理后的数据  
  4. struct NetDataBuffer  
  5. {  
  6.     char strBuffer[BUFFER_SIZE];  
  7.     SOCKET sSocket;  
  8. };  
  9.   
  10. class CIOSelectOper  
  11. {  
  12. public:  
  13.     CIOSelectOper();  
  14.     ~CIOSelectOper();  
  15.   
  16. public:  
  17.     /************************************************** 
  18.      *描述  : Listen 
  19.      *参数  :  
  20.      *返回值: 
  21.      *说明  : 函数用于完成网络初始化,绑定和监听 
  22.      **************************************************/  
  23.     void Listen();  
  24.     /************************************************** 
  25.      *描述  : Recv 
  26.      *参数  :  
  27.      *返回值: 
  28.      *说明  : 函数用于Select判断是否有数据并接收数据存入缓存 
  29.      **************************************************/  
  30.     bool Recv(DB_OUT NetDataBuffer &strData);  
  31.     /************************************************** 
  32.      *描述  : Send 
  33.      *参数  :  
  34.      *返回值: 
  35.      *说明  : 函数用于对在m_fdRead中保存的套接字句柄进行发送数据 
  36.      **************************************************/  
  37.     void Send(DB_IN char *pStrBuffer);  
  38.     /************************************************** 
  39.      *描述  : Send 
  40.      *参数  :  
  41.      *返回值: 
  42.      *说明  :  
  43.      **************************************************/  
  44.     void Send(DB_IN NetDataBuffer strBuffer, DB_IN int nLength);  
  45.     /************************************************** 
  46.      *描述  : Handle 
  47.      *参数  : strData     指定某个套接字发送过来的数据 
  48.      *返回值: 
  49.      *说明  : 函数用于对指定套接字进行接收数据判断协议处理 
  50.      **************************************************/  
  51.     bool Handle(DB_IN NetDataBuffer &strData);  
  52.   
  53. private:  
  54.     bool m_Init;//用来标志网络是否初始化成功  
  55.     SOCKET m_sListen;  
  56.     fd_set m_fdRead;//保存从一直以来的套接字,不使用select进行筛选  
  57.     fd_set m_fdTempRead;//当某一时刻某套接字没有数据进行select会被删除掉,所以需要备份的fdRead去select  
  58. };  
[cpp] view plain copy
 print?
  1. #include "IOSelectOper.h"  
  2.   
  3. CIOSelectOper::CIOSelectOper()  
  4. {  
  5.     FD_ZERO(&m_fdRead);  
  6.     FD_ZERO(&m_fdClient);  
  7.     m_Init = false;  
  8.     vector<UserData>().swap(m_vUserData);  
  9. }  
  10.   
  11. CIOSelectOper::~CIOSelectOper()  
  12. {  
  13.     if (m_sListen)  
  14.     {  
  15.         closesocket(m_sListen);  
  16.         m_sListen = 0;  
  17.     }  
  18.     if (m_Init)  
  19.     {  
  20.         WSACleanup();  
  21.     }  
  22.     vector<UserData>().swap(m_vUserData);  
  23. }  
  24.   
  25. void CIOSelectOper::Listen()  
  26. {  
  27.     WSADATA wsaData;  
  28.     int nRet = WSAStartup(0x0202, &wsaData);  
  29.     if (0 == nRet)  
  30.     {  
  31.         m_sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);  
  32.         if (INVALID_SOCKET != m_sListen)  
  33.         {  
  34.             SOCKADDR_IN addrServer;  
  35.             addrServer.sin_addr.S_un.S_addr = htonl(INADDR_ANY);  
  36.             addrServer.sin_family = AF_INET;  
  37.             addrServer.sin_port = htons(atoi(m_cPluginOper.m_sPort.c_str()));  
  38.             nRet = bind(m_sListen, (SOCKADDR*)&addrServer, sizeof(addrServer));  
  39.             if (0 == nRet)  
  40.             {  
  41.                 nRet = listen(m_sListen, FD_SETSIZE);  
  42.                 if (0 == nRet)  
  43.                 {             
  44.                     FD_SET(m_sListen, &m_fdRead);  
  45.                     m_Init = true;  
  46.                 }  
  47.             }  
  48.         }  
  49.     }     
  50. }  
  51.   
  52. bool CIOSelectOper::Recv(DB_OUT NetDataBuffer &strData)  
  53. {  
  54.     bool bRet = false;  
  55.     FD_ZERO(&m_fdTempRead);  
  56.     m_fdTempRead = m_fdRead;  
  57.     timeval time = {0, 100};  
  58.     int nRet = select(0, &m_fdTempRead, NULL, NULL, &time);  
  59.     if (nRet > 0)  
  60.     {  
  61.         int nCount = m_fdRead.fd_count;  
  62.         for (int i = 0; i < nCount; i++)  
  63.         {  
  64.             if (FD_ISSET(m_fdRead.fd_array[i], &m_fdTempRead))  
  65.             {  
  66.                 //当调用了listen而且有一个连接正在建立  
  67.                 if (m_sListen == m_fdRead.fd_array[i])  
  68.                 {  
  69.                     SOCKADDR_IN addrClient;  
  70.                     int nSize = sizeof(SOCKADDR_IN);  
  71.                     SOCKET sClient = accept(m_sListen, (SOCKADDR*)&addrClient, &nSize);  
  72.                     if (INVALID_SOCKET != sClient)  
  73.                     {  
  74.                         FD_SET(sClient, &m_fdRead);  
  75.                     }  
  76.                 }  
  77.                 //有数据可以读入  
  78.                 else  
  79.                 {  
  80.                     strData.sSocket = m_fdRead.fd_array[i];  
  81.                     int nRet = recv(m_fdRead.fd_array[i], strData.strBuffer, BUFFER_SIZE, 0);  
  82.                     //连接已经关闭,重设,中止  
  83.                     if (nRet == 0 || (nRet == SOCKET_ERROR && WSAGetLastError() == WSAECONNRESET))  
  84.                     {  
  85.                         closesocket(m_fdRead.fd_array[i]);  
  86.                         FD_CLR(m_fdRead.fd_array[i], &m_fdRead);  
  87.                     }  
  88.                     else if (nRet > 0)  
  89.                     {  
  90.                         bRet = true;//返回true说明成功读到了数据  
  91.                     }  
  92.                 }  
  93.             }  
  94.         }  
  95.     }  
  96.     else  
  97.     {  
  98.         Sleep(100);  
  99.     }  
  100.     return bRet;  
  101. }  

然后是外部调用select封装类进行数据缓冲的存储和读取出来处理,一般是一个线程只负责读取网络数据放进数据缓冲,然后继续读下面的数据,另外一个线程从数据缓冲中读取数据做处理。

[cpp] view plain copy
 print?
  1. #pragma once  
  2.   
  3. #include "IOSelectOper.h"  
  4. #include "DeamonLock.h"  
  5. #include <queue>  
  6. using namespace std;  
  7.   
  8. class CDeamonServer  
  9. {  
  10. public:  
  11.     CDeamonServer();  
  12.     ~CDeamonServer();  
  13.   
  14. public:  
  15.     void RecvNetBuffer();  
  16.     void HandNetBuffer();  
  17.     BOOL m_bFlag;//控制线程的退出标志  
  18.   
  19. public:  
  20.     static UINT WINAPI RecvNetDataThread(LPVOID pParam);  
  21.     static UINT WINAPI HandNetDataThread(LPVOID pParam);  
  22.   
  23. private:  
  24.     CIOSelectOper m_cIOSelectOper;  
  25.     CDeamonLock m_cNetLock;  
  26.     HANDLE m_hRecvNet;  
  27.     HANDLE m_hHandNet;  
  28.     queue<NetDataBuffer> m_vNetDataBuffer;//存储网络端的数据  
  29. };  
[cpp] view plain copy
 print?
  1. #include "DeamonServer.h"  
  2. #include<process.h>  
  3.   
  4. CDeamonServer::CDeamonServer()  
  5. {  
  6.     m_cIOSelectOper.Listen();  
  7.     m_bFlag = TRUE;  
  8.     m_hRecvNet = (HANDLE)_beginthreadex(NULL, 0, RecvNetDataThread, (LPVOID)this, 0, 0);  
  9.     m_hHandNet = (HANDLE)_beginthreadex(NULL, 0, HandNetDataThread, (LPVOID)this, 0, 0);  
  10. }  
  11.   
  12. CDeamonServer::~CDeamonServer()  
  13. {  
  14.     WaitForSingleObject(m_hRecvNet, INFINITE);  
  15.     WaitForSingleObject(m_hHandNet, INFINITE);  
  16.     if (NULL != m_hRecvNet)  
  17.     {  
  18.         CloseHandle(m_hRecvNet);  
  19.         m_hObject = NULL;  
  20.     }  
  21.     if (NULL != m_hHandNet)  
  22.     {  
  23.         CloseHandle(m_hHandNet);  
  24.         m_hObject = NULL;  
  25.     }  
  26. }  
  27.   
  28. BOOL CDeamonServer::InitInstance()  
  29. {  
  30.     while (m_bFlag)  
  31.     {  
  32.         WaitForSingleObject(m_hServerObject, INFINITE);  
  33.         m_bFlag = FALSE;  
  34.     }  
  35.     return FALSE;  
  36. }  
  37.   
  38. void CDeamonServer::RecvNetBuffer()  
  39. {  
  40.     NetDataBuffer strData;  
  41.     if (m_cIOSelectOper.Recv(strData))  
  42.     {  
  43.         m_cNetLock.LockCritical();  
  44.         m_vNetDataBuffer.push(strData);  
  45.         m_cNetLock.UnLockCritical();  
  46.     }  
  47. }  
  48.   
  49. void CDeamonServer::HandNetBuffer()  
  50. {  
  51.     if (!m_vNetDataBuffer.empty())  
  52.     {     
  53.         m_cNetLock.LockCritical();  
  54.         NetDataBuffer strData = m_vNetDataBuffer.front();  
  55.         m_vNetDataBuffer.pop();  
  56.         m_cNetLock.UnLockCritical();  
  57.     }     
  58.     else  
  59.     {  
  60.         Sleep(100);  
  61.     }  
  62. }  
  63.   
  64. UINT WINAPI CDeamonServer::RecvNetDataThread(LPVOID pParam)  
  65. {  
  66.     CDeamonServer *pThis = (CDeamonServer*)pParam;  
  67.     while(pThis->m_bFlag)  
  68.     {     
  69.         pThis->RecvNetBuffer();  
  70.     }  
  71.     return 0;  
  72. }  
  73.   
  74. UINT WINAPI CDeamonServer::HandNetDataThread(LPVOID pParam)  
  75. {  
  76.     CDeamonServer *pThis = (CDeamonServer*)pParam;  
  77.     while(pThis->m_bFlag)  
  78.     {     
  79.         pThis->HandNetBuffer();  
  80.     }  
  81.     return 0;  
  82. }  
这里会用到类中的一点小技巧,把this指针作为线程参数传给线程,线程里面就可以调用类中的接口。线程中一般会用无限循环,但是很多人喜欢让主线程退出,然后线程被迫退出的方式。或者用ExitThread来退出,这样都不是安全友好的方式。其实我们可以设置一个while的循环条件,外部想要退出的时候把条件置为false,就可以安全退出了。然后在主线程中使用WaitForSingleObject来卡住主线程,等待线程安全执行完了,主线程才结束析构。
0 0
原创粉丝点击