VC++ MFC实现一个简易聊天(客户端篇)(逻辑解耦)

先上图,服务器正在完善中,留作以后博客分享(想增加历史记录缓冲),贴一下EXE下载地址,源码地址在最下面


客户端部分其实实现起来不难,或许最大的难点就在于如何把socket底层的缓冲区调优,以及把解析数据包的逻辑从socket底层操作中解耦出来,以及解析包的算法调优

自从C++11出来以后,C++的很多写法也变得优美起来,本人极力推崇C++11的一些比较稳定优质的方法的,譬如std::function,std::thread等


贴一下主要代码的一些含义

1:自定义的一套帧数据格式解析类,利用包头识别符号,加时间戳,以及尾部校验算法

#pragma once
#include  <iostream>
#include  "winsock2.h"
#include "PB_CSocketQueue.h"



class PB_CSocketPackage
{
	typedef std::function<void(const char * msg)>  LogHandler;

	struct PB_CSocketPackageHeader
	{
		char bDataKind;//0xaa 1
		char bCheckCode;
		short sPacketSize;//2
		short sMainCmdID;//2
		short sSubCmdID;
		time_t unixtime;//0xff  8


		PB_CSocketPackageHeader(short _sPacketSize, short _sMainCmdID, short _sSubCmdID)
			:bDataKind(0xaa), bCheckCode(0xff)
		{
			unixtime = time(NULL);
			sPacketSize = _sPacketSize;
			sMainCmdID = _sMainCmdID;
			sSubCmdID = _sSubCmdID;
		}
	};

public:
	PB_CSocketPackage();
	~PB_CSocketPackage();


#define HEAD_LEN 16
#define FOOT_LEN 1

	void MakePacket(const std::string str, short mainCMD, short subCMD);

	bool Analysis(PB_CSocketQueue &queue);

	//LogHandler loghander;

	std::string framedata;

	char *  packetBuff;
	int  packetSize;

	char *  packetBodyBuff;
	int  packetBodySize;
	PB_CSocketPackageHeader packageheader;
	
};
#include "PB_CSocketPackage.h"

static char GetSum(char *btAryBuffer, int nLen)
{
	char btSum = 0x00;

	for (int nloop = 0; nloop < nLen; nloop++)
	{
		btSum += btAryBuffer[nloop];
	}
	return ((~btSum) + 1)*nLen;
}

static bool CheckBuff(char *btAryBuffer, int nLen){
	char c = GetSum(btAryBuffer, nLen - FOOT_LEN);
	return	btAryBuffer[nLen - FOOT_LEN] == c;
}



PB_CSocketPackage::PB_CSocketPackage() :packageheader(0, 0, 0)
{

	packetBuff = NULL;
}

PB_CSocketPackage::~PB_CSocketPackage()
{
	if (packetBuff != NULL)
	{
		delete packetBuff;
		packetBuff = NULL;
	}
}

void PB_CSocketPackage::MakePacket(const std::string str, short mainCMD, short subCMD)
{
	/*bool flag = CheckCRC16(testarr, 24);
	if (flag)
	{
	OutputDebugString(TEXT("OK"));
	}
	else
	{
	OutputDebugString(TEXT("ERROR"));
	}*/


	packageheader.sMainCmdID = htons(mainCMD);
	packageheader.sSubCmdID = htons(subCMD);
	packageheader.unixtime = htonll(packageheader.unixtime);
	packageheader.unixtime = 0;

	if (packetBuff != NULL)
	{
		delete packetBuff;
		packetBuff = NULL;
	}
	packetBodySize = str.size();
	packetSize = HEAD_LEN + packetBodySize + FOOT_LEN;
	packageheader.sPacketSize = htons(packetSize);//确定包总长度
	packetBuff = new char[packetSize];


	char  *p = packetBuff;
	memcpy(p, &packageheader, HEAD_LEN); p = p + HEAD_LEN;
	memcpy(p, str.data(), packetBodySize); p = p + packetBodySize;


	
	char  g= GetSum(packetBuff, packetSize - FOOT_LEN);
	//short g = htons(ss);
	memcpy(p, &g, FOOT_LEN);


}

bool PB_CSocketPackage::Analysis(PB_CSocketQueue &queue)
{
	int len = queue.length();
	if (len < HEAD_LEN)return false;

	int topN = queue.indexof(0xaa);
	if (topN > 0)
	{
		queue.removeTop(topN);//移除冗余
		len = queue.length();
	}

	if (len > HEAD_LEN)//假如已经够一个头的长度了,那么马上解析头
	{
		queue.copyTo((char*)&packageheader, HEAD_LEN);

		packageheader.sMainCmdID = htons(packageheader.sMainCmdID);
		packageheader.sSubCmdID = htons(packageheader.sSubCmdID);
		packageheader.sPacketSize = htons(packageheader.sPacketSize);
		packageheader.unixtime = htonll(packageheader.unixtime);
		int packetSize = packageheader.sPacketSize;



		if (len >= packetSize)
		{
			if (packetBuff != NULL)delete packetBuff;

			packetBuff = new char[packetSize];

			queue.copyTo(packetBuff, packetSize);//一帧数据全部拷贝走

			packetBodySize = packetSize - HEAD_LEN - FOOT_LEN;
			packetBodyBuff = packetBuff + HEAD_LEN;//数据指向

			//CRC校验数据
			queue.removeTop(packetSize);//移除一帧数据(不关是不是废包)


			bool flag = CheckBuff(packetBuff, packetSize);
			framedata = "";
			if (flag)	framedata.append(packetBodyBuff, packetBodySize);

			return flag;
			//return true;
		}
	}


	return false;
}

2:PB_CSocketClient客户端类,这里就利用我 上一篇博客自制的缓冲区类

#pragma once
#ifndef  PB_CSOCKETCLIENT_H_
#define PB_CSOCKETCLIENT_H_

#include <iostream>
#include <functional>
#include "winsock2.h"
#include <vector>
#include <mutex>
#include "PB_CSocketQueue.h"
#pragma comment(lib, "ws2_32.lib")


class PB_CSocketClientException
{
public:
	int Code;
	std::string Msg;
	PB_CSocketClientException(const char * msg, int code = -1){
		Msg = msg;
		Code = code;
	}
};

enum class PB_CSocketClient_EVENT
{
	ClientConnect = 100,
	ClientDisConnect,
	SocketError
};


class PB_CSocketClient
{

	typedef std::function<void(const char * msg, PB_CSocketClient_EVENT)>  ClientSocketEventHandler;

	typedef std::function<int(PB_CSocketQueue&)> AnalysisHandler;


public:
	PB_CSocketClient();
	~PB_CSocketClient();

	void SetSocketHandle(SOCKET _csocket);
	int Send(const char* pBuf, int len);
	int Receive();

	//纯客户端方法
	bool CConnect(const char *ip, int port);
	void CStartReceiveSync(AnalysisHandler DataReceived);

	bool IsConnected() const{
		return isConnected;
	}

	void SetConnected(bool val) {
		isConnected = val;
	}

	std::string GetClientIPPort();
	void SClose();
	bool SShutDown();


	PB_CSocketQueue m_queue;
	

	SOCKET csocket;
	sockaddr_in clientaddr;
	sockaddr_in serverAddress;

	ClientSocketEventHandler EvenHandler;

	std::string logstr;

	void * PTRAnalysisClass;
	
private:
	bool isConnected;	//链接状态
};

#endif


#include "PB_CSocketPackage.h"

static char GetSum(char *btAryBuffer, int nLen)
{
	char btSum = 0x00;

	for (int nloop = 0; nloop < nLen; nloop++)
	{
		btSum += btAryBuffer[nloop];
	}
	return ((~btSum) + 1)*nLen;
}

static bool CheckBuff(char *btAryBuffer, int nLen){
	char c = GetSum(btAryBuffer, nLen - FOOT_LEN);
	return	btAryBuffer[nLen - FOOT_LEN] == c;
}



PB_CSocketPackage::PB_CSocketPackage() :packageheader(0, 0, 0)
{

	packetBuff = NULL;
}

PB_CSocketPackage::~PB_CSocketPackage()
{
	if (packetBuff != NULL)
	{
		delete packetBuff;
		packetBuff = NULL;
	}
}

void PB_CSocketPackage::MakePacket(const std::string str, short mainCMD, short subCMD)
{
	/*bool flag = CheckCRC16(testarr, 24);
	if (flag)
	{
	OutputDebugString(TEXT("OK"));
	}
	else
	{
	OutputDebugString(TEXT("ERROR"));
	}*/


	packageheader.sMainCmdID = htons(mainCMD);
	packageheader.sSubCmdID = htons(subCMD);
	packageheader.unixtime = htonll(packageheader.unixtime);
	packageheader.unixtime = 0;

	if (packetBuff != NULL)
	{
		delete packetBuff;
		packetBuff = NULL;
	}
	packetBodySize = str.size();
	packetSize = HEAD_LEN + packetBodySize + FOOT_LEN;
	packageheader.sPacketSize = htons(packetSize);//确定包总长度
	packetBuff = new char[packetSize];


	char  *p = packetBuff;
	memcpy(p, &packageheader, HEAD_LEN); p = p + HEAD_LEN;
	memcpy(p, str.data(), packetBodySize); p = p + packetBodySize;


	
	char  g= GetSum(packetBuff, packetSize - FOOT_LEN);
	//short g = htons(ss);
	memcpy(p, &g, FOOT_LEN);


}

bool PB_CSocketPackage::Analysis(PB_CSocketQueue &queue)
{
	int len = queue.length();
	if (len < HEAD_LEN)return false;

	int topN = queue.indexof(0xaa);
	if (topN > 0)
	{
		queue.removeTop(topN);//移除冗余
		len = queue.length();
	}

	if (len > HEAD_LEN)//假如已经够一个头的长度了,那么马上解析头
	{
		queue.copyTo((char*)&packageheader, HEAD_LEN);

		packageheader.sMainCmdID = htons(packageheader.sMainCmdID);
		packageheader.sSubCmdID = htons(packageheader.sSubCmdID);
		packageheader.sPacketSize = htons(packageheader.sPacketSize);
		packageheader.unixtime = htonll(packageheader.unixtime);
		int packetSize = packageheader.sPacketSize;



		if (len >= packetSize)
		{
			if (packetBuff != NULL)delete packetBuff;

			packetBuff = new char[packetSize];

			queue.copyTo(packetBuff, packetSize);//一帧数据全部拷贝走

			packetBodySize = packetSize - HEAD_LEN - FOOT_LEN;
			packetBodyBuff = packetBuff + HEAD_LEN;//数据指向

			//CRC校验数据
			queue.removeTop(packetSize);//移除一帧数据(不关是不是废包)


			bool flag = CheckBuff(packetBuff, packetSize);
			framedata = "";
			if (flag)	framedata.append(packetBodyBuff, packetBodySize);

			return flag;
			//return true;
		}
	}


	return false;
}

附上: 源代码下载地址





  • 0
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值