完成端口通讯类

  最近由于项目需要进行网络传输,所以自己整个了基础类,运行效果还不错,所以分析给大家。本基础类采用完成端口模式实现,基于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);
};

posted @ 2016-06-06 16:18  ysextend  阅读(193)  评论(0)    收藏  举报