经过本人的分析与项目实战,一种基于收发结构体的方案是非常可靠且方便的。
具体方法是,所有的结构体继承自一个结构体,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;
}
真正发送的数据由前四个字节的消息头,和消息体组成,消息头描述了消息体的长度。