C/C++基于TCP的通信协议解决方案

经过本人的分析与项目实战,一种基于收发结构体的方案是非常可靠且方便的。

具体方法是,所有的结构体继承自一个结构体,stBase,而stBase的成员只有一个整形的消息id,将收到的消息存到缓冲区后
处理好粘包),将缓冲区强制转换成stBase,通过id来判断对应的业务消息,再强制转换成对应的结构体。

消息的定义:

这里处理一个基结构体外,只写了两个业务结构体,


struct stBaseMsg
{
	int nID;
	stBaseMsg()
	{
		nID=0;
	}
};

//用户信息结构体,包含用户的账号、密码
const int Msg_UserInfo = 1000;
struct stUserInfo:public stBaseMsg
{
	char szUsername[50];
	char szPassword[50];
	stUserInfo()
	{
		memset(szUsername,0,sizeof(szUsername));
		memset(szPassword,0,sizeof(szPassword));
		nID = Msg_UserInfo;
	}
};

//用户的聊天结构体
const int Msg_Chat = 1001;
struct stChat:public stBaseMsg
{
	char szContent[200];
	stChat()
	{
		memset(szContent,0,sizeof(szContent));
		nID = Msg_Chat;
	}
	void Clear()
	{
		memset(szContent,0,sizeof(szContent));
	}
};

以下是伪代码,主要展示怎么用这样的功能,其中SendData和NRecvData是我基于send和recv封装的方法,文末会提供源码。

客户端:

代码主要体现发送结构体,

    stUserInfo userinfo;
    strcpy(userinfo.szUsername,"test11");
    strcpy(userinfo.szPassword,"123456");
	
    //发送用户信息到服务器
    //sock为套接字句柄,userinfo为发送缓冲区指针char*,第三个参数是缓冲区大小
    SendData(sock,(char*)&userinfo,sizeof(userinfo));
	
    stChat chat;
    cin>>chat.szContent;

    //发送客户端输入的一段话到服务器
    SendData(sock,(char*)&chat,sizeof(chat));

服务器:接受到消息的处理方式,重点

char szBuff[1024*4];
memset(szBuff,0,sizeof(szBuff));
//接受数据
ret = NRecvData(socketClient,szBuff,1024*4);
if(ret == SOCKET_ERROR)
	return;
//转换缓冲区
stBaseMsg *base = (stBaseMsg *)szBuff;
//根据nID来判断业务逻辑
switch (base->nID)
{
case Msg_UserInfo:
	{
		stUserInfo*userinfo = (stUserInfo*)base;
		cout<<"username:"<szUsername<szPassword<szContent<

这里只是提供了一种思想,用switch语句处理消息id并不好,当有成千上万条的id,会导致switch语句巨大,当然可以想办法优化,但这不是本文重点。

以下是SendData和NRecvData的函数原型,这两个函数基于系统api   recv和send封装,已经处理好了粘包现象。

/*\
	说明: 
		接受数据
	参数:
		sock,套接字句柄
		data,缓冲区
		maxlen,能接受的数据最大长度或大小
	返回值:
		接受的真正长度
*/
int NRecvData(SOCKET sock,char *data,int maxlen = -1)
{
	//先发送消息头,再发送消息体
	int nBodyLen = 0;
	int ret = recv(sock,(char *)&nBodyLen,4,0);
	if(4 == ret && nBodyLen>0 && (maxlen==-1 || maxlen>=nBodyLen))
	{
		ret = recv(sock,data,nBodyLen,0);
	}
	return ret;
}

/*\
	说明: 
		发送数据
	参数:
		sock,套接字句柄
		data,发送缓冲区
		maxlen,发送缓冲区大小
	返回值:
		接受的真正长度
*/
int SendData(SOCKET sock,const char *data,int len)
{
	int ret = send(sock,(char *)&len,4,0);
	if(ret == 4)
	{
		ret = send(sock,data,len,0);
	}
	return ret;
}

真正发送的数据由前四个字节的消息头,和消息体组成,消息头描述了消息体的长度。

你可能感兴趣的:(c++,网络编程,代码碎片)