完成端口通讯类
最近由于项目需要进行网络传输,所以自己整个了基础类,运行效果还不错,所以分析给大家。本基础类采用完成端口模式实现,基于TCP/IP协议,c++语言编写,并在基础类中解决了TCP粘包问题。接口简单使用方便,只需派生相应类即可,无需处理繁琐的异步消息问题、死锁问题。 当然,本基础类并不完美,还有许多可优化的地方,比如服务器端监听还可以优化,数据发送IO包可采用内存池等,待后续有空再优化,一般应用是没有问题的。
1、基础类头文件 Net.h
#pragma once
//**********************************************
//*
//* Copyright (c) 2016, ysextend@126.com
//* All rights reserved.
//*
//* File Name:Net.h
//*
//* Version:1.0
//* Author:yanfh
//* Date :2016/5/18
//*
//**********************************************
#include <map>
#include <iostream>
using namespace std;
#include <winsock2.h>
#pragma comment( lib, "ws2_32.lib" )
class CIo
{
public:
WSAOVERLAPPED m_olp;
WSABUF m_wsaBuf;
SOCKET m_nSocket;
BYTE* m_pData;
bool m_bRead;
CIo();
~CIo();
};
class CPacket
{
public:
CPacket();
~CPacket();
bool Pack(BYTE cmd, BYTE* pHead, int nHeadLen, BYTE* pData, int nDataLen, BYTE*& out, int& len);
bool Unpack(BYTE* pIn, int nLen, BYTE& cmd, int& nPacketLen, BYTE* pHead, int& nHeadLen, BYTE* pData, int& nDataLen);
protected:
int m_nLen;
int m_nPos;
BYTE* m_pData;
BYTE* m_pHead;
int m_nHead;
};
class CNet
{
public:
CNet(void);
~CNet(void);
virtual bool Init();
virtual void UnInit();
virtual void Recv(SOCKET id, char* data, int len);
virtual bool Send(SOCKET id, char* data, int len);
protected:
HANDLE m_hIocp;
bool m_bShutDown;
SOCKET m_hSocket;
int m_nMaxCpu;
HANDLE m_hWorkThread[8];
DWORD m_dwWorkThread[8];
std::map<SOCKET, CIo*> m_mapDevice;
static DWORD __stdcall WorkThread(LPVOID Param);
bool NewSocket(SOCKET id);
};
class CNetClient : public CNet
{
public:
CNetClient(void);
~CNetClient();
bool Connect(char* ip, int port);
void DisConnect();
virtual bool Init();
bool Send(char* data, int len);
protected:
SOCKET m_nClientId;
};
class CNetServer : public CNet
{
public:
CNetServer(void);
~CNetServer();
bool Listen(char* ip, int port);
virtual void NewDevice(SOCKET id);
protected:
HANDLE m_hListenThread;
DWORD m_nListenThreadCode;
HANDLE m_hEvent;
SOCKET m_nServerId;
static DWORD WINAPI ListenThread(LPVOID pParam);
};
2、基础类实现文件
#include "stdafx.h"
#include "Net.h"
#define ReleaseIo(ptr) \
if ( NULL != ptr )\
{\
ptr->m_nSocket = INVALID_SOCKET;\
delete ptr;\
ptr = NULL;\
}\
CIo::CIo()
{
m_pData = new BYTE[1024*8];
memset(&m_olp, 0, sizeof(WSAOVERLAPPED));
memset(m_pData, 0, 1024*8);
m_wsaBuf.buf = (char*)m_pData;
m_wsaBuf.len = 1024*8;
m_nSocket = INVALID_SOCKET;
m_bRead = true;
}
CIo::~CIo()
{
delete[] m_pData;
m_pData = NULL;
}
CPacket::CPacket()
{
m_nPos = 0;
m_nLen = 1024*104*1024;
m_nHead = 4;
m_pHead = new BYTE[m_nHead];
memset(m_pHead, 0xD4, 2);
memset(&m_pHead[2], 0xCD, 2);
m_pData = new BYTE[m_nLen];
}
CPacket::~CPacket()
{
delete[] m_pHead;
delete[] m_pData;
m_pHead = NULL;
m_pData = NULL;
}
bool CPacket::Pack(BYTE cmd, BYTE* pHead, int nHeadLen, BYTE* pData, int nDataLen, BYTE*& out, int& len)
{
len = m_nHead + 4 + 1 + nHeadLen + nDataLen;
if ( len > m_nLen )
return false;
memcpy(m_pData, m_pHead, m_nHead);
memcpy(&m_pData[4], &len, 4);
m_pData[8] = cmd;
if ( nHeadLen > 0 )
memcpy(&m_pData[9], pHead, nHeadLen);
if ( nDataLen > 0 )
memcpy(&m_pData[9+nHeadLen], pData, nDataLen);
out = (BYTE*)m_pData;
return true;
}
bool CPacket::Unpack(BYTE* pIn, int nLen, BYTE& cmd, int& nPacketLen, BYTE* pHead, int& nHeadLen, BYTE* pData, int& nDataLen)
{
if ( m_nPos + nLen <= m_nLen - 1 )
{
memcpy(&m_pData[m_nPos], pIn, nLen);
m_nPos += nLen - 1;
}
else
{
memcpy(&m_pData[m_nPos], pIn, m_nLen-m_nPos-1);
m_nPos = m_nLen - 1;
}
for ( int i = 0; i <= m_nPos - 8; i++ )
{
if ( 0xD4 == m_pData[i] && 0xD4 == m_pData[i+1] && 0xCD == m_pData[i+2] && 0xCD == m_pData[i+3] )
{
memcpy(&nPacketLen, &m_pData[i+4], 4);
if ( i + nPacketLen <= m_nPos + 1 )
{
cmd = m_pData[i+8];
if ( nPacketLen > 9 )
{
memcpy(&nHeadLen, &m_pData[i+9], 4);
memcpy(pHead, &m_pData[i+9], nHeadLen);
memcpy(&nDataLen, &m_pData[i+13], 4);
if ( nDataLen > 0 )
memcpy(pData, &m_pData[i+9+nHeadLen], nDataLen);
}
memmove(m_pData, &m_pData[i], m_nPos + 1 - nPacketLen );
m_nPos -= nPacketLen - 1;
break;
}
}
else if ( i == m_nPos - 8 )
{
memmove(m_pData, &m_pData[i], m_nPos + 1 - 8);
m_nPos = 7;
}
}
return true;
}
CNet::CNet(void)
{
m_hIocp = NULL;
m_bShutDown = false;
m_hSocket = NULL;
m_nMaxCpu = 0;
for ( int i = 0; i < 8; i++)
{
m_hWorkThread[i] = NULL;
m_dwWorkThread[i]= 0;
}
}
CNet::~CNet(void)
{
}
bool CNet::Init()
{
if ( NULL != m_hIocp )
return false;
WSADATA wsd;
if (WSAStartup(MAKEWORD(2,2), &wsd) != 0)
return false;
m_hIocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, NULL, 0);
if ( NULL == m_hIocp )
return false;
SYSTEM_INFO SysInfo;
GetSystemInfo(&SysInfo);
m_nMaxCpu = SysInfo.dwNumberOfProcessors;
if ( 8 < m_nMaxCpu )
m_nMaxCpu = 8;
for ( int i = 0; i < m_nMaxCpu; i++)
m_hWorkThread[i] = CreateThread(NULL,0,WorkThread,(LPVOID)this,m_dwWorkThread[i],NULL);
return true;
}
DWORD CNet::WorkThread(LPVOID Param)
{
CNet* pThis = (CNet*)Param;
BOOL bRet = FALSE;
DWORD dwBytes = 0;
void * re;
OVERLAPPED* pOverlap = NULL;
CIo* pIo = NULL;
while ( !pThis->m_bShutDown )
{
bRet = GetQueuedCompletionStatus(pThis->m_hIocp, &dwBytes, (LPDWORD)&re, (LPOVERLAPPED *)&pOverlap, WSA_INFINITE);
pIo = (CIo*)pOverlap;
if ( NULL == pIo && 0 == dwBytes )
break;
if ( NULL != pIo )
{
if ( pIo->m_bRead )
{
if ( 0 != dwBytes )
{
pThis->Recv(pIo->m_nSocket, pIo->m_wsaBuf.buf, dwBytes);
DWORD dwFlags = 0;
DWORD dwRecv = 0;
int nRet = WSARecv(pIo->m_nSocket, &(pIo->m_wsaBuf), 1, &dwRecv, &dwFlags, &(pIo->m_olp), NULL);
if ( nRet == SOCKET_ERROR && WSAGetLastError() != ERROR_IO_PENDING )
{
closesocket(pIo->m_nSocket);
pIo->m_nSocket = INVALID_SOCKET;
}
}
}
else
{
std::map<SOCKET, CIo*>::iterator it = pThis->m_mapDevice.find(pIo->m_nSocket);
if ( it != pThis->m_mapDevice.end() )
pThis->m_mapDevice.erase(it);
ReleaseIo(pIo);
}
}
}
cout<<"WorkThread exit";
cout<<endl;
return 0xDEAD;
}
bool CNet::NewSocket(SOCKET id)
{
std::map<SOCKET, CIo*>::iterator it = m_mapDevice.find(id);
CIo* pIo = NULL;
if ( it == m_mapDevice.end() )
{
pIo = new CIo;
pIo->m_nSocket = id;
m_mapDevice.insert(std::make_pair(pIo->m_nSocket, pIo));
}
else
((CIo*)it->second)->m_nSocket = id;
if ( NULL == CreateIoCompletionPort( (HANDLE)id, m_hIocp,(DWORD)pIo, 0) )
{
closesocket(pIo->m_nSocket);
ReleaseIo(pIo);
m_mapDevice.erase(it);
return false;
}
DWORD dwFlags = 0;
DWORD dwRecv = 0;
int nRet = WSARecv(pIo->m_nSocket, &(pIo->m_wsaBuf), 1, &dwRecv, &dwFlags, &(pIo->m_olp), NULL);
if ( nRet == SOCKET_ERROR && WSAGetLastError() != ERROR_IO_PENDING )
{
closesocket(pIo->m_nSocket);
ReleaseIo(pIo);
m_mapDevice.erase(it);
return false;
}
return true;
}
void CNet::UnInit()
{
m_bShutDown = true;
for ( int i = 0; i < 200; i++ )
{
PostQueuedCompletionStatus(m_hIocp, 0, NULL, NULL);
bool bSafe = true;
for ( int j = 0; i < m_nMaxCpu; i++ )
{
if ( ::GetExitCodeThread (m_hWorkThread[i], &m_dwWorkThread[i])&&m_dwWorkThread[i] == STILL_ACTIVE )
bSafe = false;
}
if ( bSafe )
break;
else
Sleep(10);
}
for ( std::map<SOCKET, CIo*>::iterator it = m_mapDevice.begin(); it != m_mapDevice.end(); it++)
{
closesocket(it->second->m_nSocket);
m_mapDevice.erase(it);
ReleaseIo(it->second);
}
}
bool CNet::Send(SOCKET id, char* data, int len)
{
CIo* pTemp = new CIo;
memcpy(pTemp->m_pData, data, len);
pTemp->m_nSocket = id;
pTemp->m_bRead = false;
pTemp->m_wsaBuf.len = len;
DWORD dwLen = len;
int nRet = WSASend(pTemp->m_nSocket, &(pTemp->m_wsaBuf), 1, &dwLen, 0, &(pTemp->m_olp), NULL);
if( nRet == SOCKET_ERROR && WSAGetLastError() != WSA_IO_PENDING )
{
closesocket(pTemp->m_nSocket);
ReleaseIo(pTemp);
return false;
}
return true;
}
void CNet::Recv(SOCKET id, char* data, int len)
{
int x = 0;
}
CNetClient::CNetClient()
{
m_nClientId = INVALID_SOCKET;
}
CNetClient::~CNetClient()
{
}
bool CNetClient::Connect(char* ip, int port)
{
if ( INVALID_SOCKET != m_nClientId )
return false;
m_nClientId = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
if ( INVALID_SOCKET == m_nClientId )
return false;
SOCKADDR_IN SockAddr;
memset(&SockAddr,0,sizeof(SOCKADDR_IN));
SockAddr.sin_family = AF_INET;
SockAddr.sin_addr.s_addr = inet_addr(ip);
SockAddr.sin_port = htons(port);
int nRet = connect(m_nClientId, (sockaddr*)&SockAddr, sizeof(SockAddr));
if (nRet == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK)
{
m_nClientId = INVALID_SOCKET;
return false;
}
if ( !NewSocket(m_nClientId) )
{
m_nClientId = INVALID_SOCKET;
return false;
}
return true;
}
void CNetClient::DisConnect()
{
if ( INVALID_SOCKET == m_nClientId )
return;
closesocket(m_nClientId);
m_nClientId = INVALID_SOCKET;
}
bool CNetClient::Init()
{
return CNet::Init();
}
bool CNetClient::Send(char* data, int len)
{
return CNet::Send(m_nClientId, data, len);
}
CNetServer::CNetServer()
{
m_hListenThread = NULL;
m_nListenThreadCode = 0;
m_nServerId = INVALID_SOCKET;
m_hEvent = NULL;
}
CNetServer::~CNetServer()
{
}
bool CNetServer::Listen(char* ip, int port)
{
if ( INVALID_SOCKET != m_nServerId )
return false;
m_nServerId = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_IP, NULL, 0, WSA_FLAG_OVERLAPPED);
if ( INVALID_SOCKET == m_nServerId )
{
m_nServerId = INVALID_SOCKET;
return false;
}
m_hEvent = WSACreateEvent();
if ( WSA_INVALID_EVENT == m_hEvent )
{
m_nServerId = INVALID_SOCKET;
return false;
}
int nRet = WSAEventSelect(m_nServerId, m_hEvent, FD_ACCEPT);
if ( SOCKET_ERROR == nRet )
{
m_nServerId = INVALID_SOCKET;
return false;
}
SOCKADDR_IN InternetAddr;
InternetAddr.sin_family = AF_INET;
InternetAddr.sin_addr.s_addr = inet_addr(ip);
InternetAddr.sin_port = htons(port);
int nResult = bind(m_nServerId, (PSOCKADDR)&InternetAddr, sizeof(InternetAddr));
if ( SOCKET_ERROR == nResult )
{
m_nServerId = INVALID_SOCKET;
return false;
}
nResult = listen(m_nServerId, 20);
if (SOCKET_ERROR == nResult)
{
m_nServerId = INVALID_SOCKET;
return false;
}
m_hListenThread = CreateThread(NULL,0,ListenThread,(LPVOID)this,m_nListenThreadCode,NULL);
if ( NULL == m_hListenThread )
{
closesocket(m_nServerId);
m_nServerId = INVALID_SOCKET;
m_hListenThread = NULL;
m_nListenThreadCode = 0;
return false;
}
return true;
}
DWORD CNetServer::ListenThread(LPVOID pParam)
{
CNetServer* pThis = reinterpret_cast<CNetServer*>(pParam);
if ( pThis )
{
while ( !pThis->m_bShutDown )
{
DWORD dwRet;
dwRet = WSAWaitForMultipleEvents(1, &pThis->m_hEvent, FALSE, 100, FALSE);
if(pThis->m_bShutDown)
break;
if (dwRet == WSA_WAIT_TIMEOUT)
continue;
WSANETWORKEVENTS events;
int nRet = WSAEnumNetworkEvents(pThis->m_nServerId, pThis->m_hEvent, &events);
if (nRet == SOCKET_ERROR)
break;
if ( events.lNetworkEvents & FD_ACCEPT)
{
if ( events.iErrorCode[FD_ACCEPT_BIT] == 0 && !pThis->m_bShutDown)
{
SOCKET clientSocket = INVALID_SOCKET;
int nRet=-1;
int nLen=-1;
nLen = sizeof(SOCKADDR_IN);
clientSocket = WSAAccept(pThis->m_nServerId, NULL, &nLen,0,0);
if (clientSocket != SOCKET_ERROR)
{
if ( pThis->NewSocket(clientSocket) )
{
pThis->NewDevice(clientSocket);
}
}
}
}
Sleep(1);
}
}
return 0x1010;
}
void CNetServer::NewDevice(SOCKET id)
{
}
3、服务器端示例
#include "stdafx.h"
#include "Net.h"
class CMyServer : public CNetServer
{
public:
CMyServer();
virtual void NewDevice(SOCKET id);
virtual void Recv(SOCKET id, char* data, int len);
CPacket m_Send;
CPacket m_Recv;
SOCKET m_ClientId;
};
CMyServer::CMyServer()
{
m_ClientId = INVALID_SOCKET;
}
void CMyServer::NewDevice(SOCKET id)
{
cout<<"device("<<id<<") online"<<endl;
m_ClientId = id;
}
void CMyServer::Recv(SOCKET id, char* data, int len)
{
BYTE Head[1024*8];
BYTE Data[1024*8];
BYTE Cmd = 0;
int HeadLen = 0;
int DataLen = 0;
int PacketLen = 0;
m_Recv.Unpack((BYTE*)data, len, Cmd, PacketLen, Head, HeadLen, Data, DataLen);
Head[HeadLen] = '\0';
cout<<"from device("<<id<<"):"<<&Head[8]<<endl;
}
int _tmain(int argc, _TCHAR* argv[])
{
CMyServer mServer;
if ( mServer.Init() && mServer.Listen("192.168.1.160", 60000) )
{
while (true )
{
if ( INVALID_SOCKET == mServer.m_ClientId )
continue;
// 要发送数据
char szSend[] = "hi, client!";
int nSendLen = sizeof(szSend);
// 打包
BYTE* pData = NULL;
int nLen = 0;
BYTE* pHead = new BYTE[8+nSendLen];
memset(pHead, 0, 8+nSendLen);
int n = 8+nSendLen;
memcpy(pHead, &n, 4);
n = 0;
memcpy(&pHead[4], &n, 4);
memcpy(&pHead[8], szSend, nSendLen);
// 发送
mServer.m_Send.Pack(0, pHead, 8+nSendLen, NULL, 0, pData, nLen);
mServer.Send(mServer.m_ClientId, (char*)pData, nLen);
Sleep(10);
}
}
return 0;
}
4、客户端示例
#include "stdafx.h"
#include "Net.h"
class CMyClient : public CNetClient
{
public:
virtual void Recv(SOCKET id, char* data, int len);
CPacket m_Send;
CPacket m_Recv;
};
void CMyClient::Recv(SOCKET id, char* data, int len)
{
BYTE Head[1024*8];
BYTE Data[1024*8];
BYTE Cmd = 0;
int HeadLen = 0;
int DataLen = 0;
int PacketLen = 0;
m_Recv.Unpack((BYTE*)data, len, Cmd, PacketLen, Head, HeadLen, Data, DataLen);
Head[HeadLen] = '\0';
cout<<"from device("<<id<<"):"<<&Head[8]<<endl;
}
int _tmain(int argc, _TCHAR* argv[])
{
CMyClient mClient;
if ( mClient.Init() && mClient.Connect("192.168.1.160", 60000) )
{
while (true)
{
// 要发送数据
char szSend[] = "hi, server!";
int nSendLen = sizeof(szSend);
// 打包
BYTE* pData = NULL;
int nLen = 0;
BYTE* pHead = new BYTE[8+nSendLen];
memset(pHead, 0, 8+nSendLen);
int n = 8+nSendLen;
memcpy(pHead, &n, 4);
n = 0;
memcpy(&pHead[4], &n, 4);
memcpy(&pHead[8], szSend, nSendLen);
// 发送
mClient.m_Send.Pack(0, pHead, 8+nSendLen, NULL, 0, pData, nLen);
mClient.Send((char*)pData, nLen);
Sleep(10);
}
}
return 0;
}
5、运行效果图

#pragma once
//**********************************************
//*
//* Copyright (c) 2015, ysextend@126.com
//* All rights reserved.
//*
//* 文件名称:Net.h
//*
//* 当前版本:1.0
//* 作 者:yanfh
//* 创建日期:2016/5/18
//*
//**********************************************
#include <map>
#include <iostream>
using namespace std;
#include <winsock2.h>
#pragma comment( lib, "ws2_32.lib" )
class CIo
{
public:
WSAOVERLAPPED m_olp;
WSABUF m_wsaBuf;
SOCKET m_nSocket;
BYTE* m_pData;
bool m_bRead;
CIo();
~CIo();
};
class CPacket
{
public:
CPacket();
~CPacket();
bool Pack(BYTE cmd, BYTE* pHead, int nHeadLen, BYTE* pData, int nDataLen, BYTE*& out, int& len);
bool Unpack(BYTE* pIn, int nLen, BYTE& cmd, int& nPacketLen, BYTE* pHead, int& nHeadLen, BYTE* pData, int& nDataLen);
protected:
int m_nLen;
int m_nPos;
BYTE* m_pData;
BYTE* m_pHead;
int m_nHead;
};
class CNet
{
public:
CNet(void);
~CNet(void);
virtual bool Init();
virtual void UnInit();
virtual void Recv(SOCKET id, char* data, int len);
virtual bool Send(SOCKET id, char* data, int len);
protected:
HANDLE m_hIocp;
bool m_bShutDown;
SOCKET m_hSocket;
int m_nMaxCpu;
HANDLE m_hWorkThread[8];
DWORD m_dwWorkThread[8];
std::map<SOCKET, CIo*> m_mapDevice;
static DWORD __stdcall WorkThread(LPVOID Param);
bool NewSocket(SOCKET id);
};
class CNetClient : public CNet
{
public:
CNetClient(void);
~CNetClient();
bool Connect(char* ip, int port);
void DisConnect();
virtual bool Init();
bool Send(char* data, int len);
protected:
SOCKET m_nClientId;
};
class CNetServer : public CNet
{
public:
CNetServer(void);
~CNetServer();
bool Listen(char* ip, int port);
virtual void NewDevice(SOCKET id);
protected:
HANDLE m_hListenThread;
DWORD m_nListenThreadCode;
HANDLE m_hEvent;
SOCKET m_nServerId;
static DWORD WINAPI ListenThread(LPVOID pParam);
};
浙公网安备 33010602011771号