Windows网络编程Select模型的封装和queue作为数据缓存的使用
来源:互联网 发布:佳词网络怎么样 编辑:程序博客网 时间:2024/06/08 07:19
首先是对Select网络模型的封装,由于会用到数据缓存,所以接收到的数据需要对应一个socket套接字,这样回复数据的时候才知道对象。
- #pragma once
-
-
- struct NetDataBuffer
- {
- char strBuffer[BUFFER_SIZE];
- SOCKET sSocket;
- };
-
- class CIOSelectOper
- {
- public:
- CIOSelectOper();
- ~CIOSelectOper();
-
- public:
-
-
-
-
-
-
- void Listen();
-
-
-
-
-
-
- bool Recv(DB_OUT NetDataBuffer &strData);
-
-
-
-
-
-
- void Send(DB_IN char *pStrBuffer);
-
-
-
-
-
-
- void Send(DB_IN NetDataBuffer strBuffer, DB_IN int nLength);
-
-
-
-
-
-
- bool Handle(DB_IN NetDataBuffer &strData);
-
- private:
- bool m_Init;
- SOCKET m_sListen;
- fd_set m_fdRead;
- fd_set m_fdTempRead;
- };
- #include "IOSelectOper.h"
-
- CIOSelectOper::CIOSelectOper()
- {
- FD_ZERO(&m_fdRead);
- FD_ZERO(&m_fdClient);
- m_Init = false;
- vector<UserData>().swap(m_vUserData);
- }
-
- CIOSelectOper::~CIOSelectOper()
- {
- if (m_sListen)
- {
- closesocket(m_sListen);
- m_sListen = 0;
- }
- if (m_Init)
- {
- WSACleanup();
- }
- vector<UserData>().swap(m_vUserData);
- }
-
- void CIOSelectOper::Listen()
- {
- WSADATA wsaData;
- int nRet = WSAStartup(0x0202, &wsaData);
- if (0 == nRet)
- {
- m_sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
- if (INVALID_SOCKET != m_sListen)
- {
- SOCKADDR_IN addrServer;
- addrServer.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
- addrServer.sin_family = AF_INET;
- addrServer.sin_port = htons(atoi(m_cPluginOper.m_sPort.c_str()));
- nRet = bind(m_sListen, (SOCKADDR*)&addrServer, sizeof(addrServer));
- if (0 == nRet)
- {
- nRet = listen(m_sListen, FD_SETSIZE);
- if (0 == nRet)
- {
- FD_SET(m_sListen, &m_fdRead);
- m_Init = true;
- }
- }
- }
- }
- }
-
- bool CIOSelectOper::Recv(DB_OUT NetDataBuffer &strData)
- {
- bool bRet = false;
- FD_ZERO(&m_fdTempRead);
- m_fdTempRead = m_fdRead;
- timeval time = {0, 100};
- int nRet = select(0, &m_fdTempRead, NULL, NULL, &time);
- if (nRet > 0)
- {
- int nCount = m_fdRead.fd_count;
- for (int i = 0; i < nCount; i++)
- {
- if (FD_ISSET(m_fdRead.fd_array[i], &m_fdTempRead))
- {
-
- if (m_sListen == m_fdRead.fd_array[i])
- {
- SOCKADDR_IN addrClient;
- int nSize = sizeof(SOCKADDR_IN);
- SOCKET sClient = accept(m_sListen, (SOCKADDR*)&addrClient, &nSize);
- if (INVALID_SOCKET != sClient)
- {
- FD_SET(sClient, &m_fdRead);
- }
- }
-
- else
- {
- strData.sSocket = m_fdRead.fd_array[i];
- int nRet = recv(m_fdRead.fd_array[i], strData.strBuffer, BUFFER_SIZE, 0);
-
- if (nRet == 0 || (nRet == SOCKET_ERROR && WSAGetLastError() == WSAECONNRESET))
- {
- closesocket(m_fdRead.fd_array[i]);
- FD_CLR(m_fdRead.fd_array[i], &m_fdRead);
- }
- else if (nRet > 0)
- {
- bRet = true;
- }
- }
- }
- }
- }
- else
- {
- Sleep(100);
- }
- return bRet;
- }
然后是外部调用select封装类进行数据缓冲的存储和读取出来处理,一般是一个线程只负责读取网络数据放进数据缓冲,然后继续读下面的数据,另外一个线程从数据缓冲中读取数据做处理。
- #pragma once
-
- #include "IOSelectOper.h"
- #include "DeamonLock.h"
- #include <queue>
- using namespace std;
-
- class CDeamonServer
- {
- public:
- CDeamonServer();
- ~CDeamonServer();
-
- public:
- void RecvNetBuffer();
- void HandNetBuffer();
- BOOL m_bFlag;
-
- public:
- static UINT WINAPI RecvNetDataThread(LPVOID pParam);
- static UINT WINAPI HandNetDataThread(LPVOID pParam);
-
- private:
- CIOSelectOper m_cIOSelectOper;
- CDeamonLock m_cNetLock;
- HANDLE m_hRecvNet;
- HANDLE m_hHandNet;
- queue<NetDataBuffer> m_vNetDataBuffer;
- };
- #include "DeamonServer.h"
- #include<process.h>
-
- CDeamonServer::CDeamonServer()
- {
- m_cIOSelectOper.Listen();
- m_bFlag = TRUE;
- m_hRecvNet = (HANDLE)_beginthreadex(NULL, 0, RecvNetDataThread, (LPVOID)this, 0, 0);
- m_hHandNet = (HANDLE)_beginthreadex(NULL, 0, HandNetDataThread, (LPVOID)this, 0, 0);
- }
-
- CDeamonServer::~CDeamonServer()
- {
- WaitForSingleObject(m_hRecvNet, INFINITE);
- WaitForSingleObject(m_hHandNet, INFINITE);
- if (NULL != m_hRecvNet)
- {
- CloseHandle(m_hRecvNet);
- m_hObject = NULL;
- }
- if (NULL != m_hHandNet)
- {
- CloseHandle(m_hHandNet);
- m_hObject = NULL;
- }
- }
-
- BOOL CDeamonServer::InitInstance()
- {
- while (m_bFlag)
- {
- WaitForSingleObject(m_hServerObject, INFINITE);
- m_bFlag = FALSE;
- }
- return FALSE;
- }
-
- void CDeamonServer::RecvNetBuffer()
- {
- NetDataBuffer strData;
- if (m_cIOSelectOper.Recv(strData))
- {
- m_cNetLock.LockCritical();
- m_vNetDataBuffer.push(strData);
- m_cNetLock.UnLockCritical();
- }
- }
-
- void CDeamonServer::HandNetBuffer()
- {
- if (!m_vNetDataBuffer.empty())
- {
- m_cNetLock.LockCritical();
- NetDataBuffer strData = m_vNetDataBuffer.front();
- m_vNetDataBuffer.pop();
- m_cNetLock.UnLockCritical();
- }
- else
- {
- Sleep(100);
- }
- }
-
- UINT WINAPI CDeamonServer::RecvNetDataThread(LPVOID pParam)
- {
- CDeamonServer *pThis = (CDeamonServer*)pParam;
- while(pThis->m_bFlag)
- {
- pThis->RecvNetBuffer();
- }
- return 0;
- }
-
- UINT WINAPI CDeamonServer::HandNetDataThread(LPVOID pParam)
- {
- CDeamonServer *pThis = (CDeamonServer*)pParam;
- while(pThis->m_bFlag)
- {
- pThis->HandNetBuffer();
- }
- return 0;
- }
这里会用到类中的一点小技巧,把this指针作为线程参数传给线程,线程里面就可以调用类中的接口。线程中一般会用无限循环,但是很多人喜欢让主线程退出,然后线程被迫退出的方式。或者用ExitThread来退出,这样都不是安全友好的方式。其实我们可以设置一个while的循环条件,外部想要退出的时候把条件置为false,就可以安全退出了。然后在主线程中使用WaitForSingleObject来卡住主线程,等待线程安全执行完了,主线程才结束析构。 0 0