不管用的是什么样的协议方式,基本通讯的原理
到底层 都是 byte[]数组 传递。
1.协议的定义
协议本身只是 通讯时候 前后端 定义的变量类型和顺序的集合。
比如说,要做一个登陆的消息传递,
需要传递的内容是帐号密码
public class MsgLogin
{
public string m_strIDName = null;
public string m_strPassWord = null;
}
前后端都确认好,使用帐号密码登陆,那么这样一个类,就可以称之为一个协议了。
那么客户端填好帐号密码后,转成byte[] 通过Socket发送到服务器,服务器解析这个消息,得到帐号密码,就完成了一次通讯。
byte[] 的内容就是 m_strIDName转换成的byte[] 拼接上 m_strPassWord转换成的byte[]
2.消息号
例如登入,客户端通知服务器要登录了
那么服务器对应的也要回复给客户端,登录是成功或者失败。
那么服务器可以定义通知客户端的协议是:
public class MsgLoginCallBack
{
public bool m_isSuc = false;
}
那么问题就来了,一个游戏里,可以会有好多的通讯,比如购买,点击,广播之类的。 而消息本身只是一串byte[]的内容,
接受端要知道这条消息要干嘛,那就需要消息号。
消息号本身只需要做一个唯一的标记,例如可以做一个枚举:
public enum OperateCode : ushort
{
C2S_LOGIN = 1,
S2C_LOGIN = 2
}
C2S = Client to Server
S2C = Server to Client
(这个基本上约定俗成)
那么代码就变成了
public class MsgLogin
{
public OperateCode m_opCode = OperateCode.C2S_LOGIN;
public string m_strIDName = null;
public string m_strPassWord = null;
}
public class MsgLoginCallBack
{
public OperateCode m_opCode = OperateCode.S2C_LOGIN ;
public bool m_isSuc = false;
}
客户端和服务器统一 一套消息号
这样互相解析第一个字段m_opCode 就能知道当前这条消息,对于本次通讯要做什么事了。
3.数据包的组装
前面说到通讯用到的是byte[]
那么怎么组装和解析这个byte[]那?
google的protobuf提供了很好的解决方案,可以百度一下参考。
这里说一下基本原理,也叫做 字节序,也是比较常用的一套方案。
先说一下消息包的组成。
一般分为 包头 和 包体。
包头 包含 消息号 和 包体长度
包体 包含 各种需要通讯的内容,也就是上面的类包含的变量。
例如:
包头部分:
内容 | 类型 | 长度 |
---|---|---|
消息号 | ushort | 2 |
包体长度 | ushort | 2 |
那么包头的长度就是 2+2=4
包体:
像是上面的登录消息,是2个string,但是帐号密码的长度,我们是不知道的。那就需要做一件事就是 取得string的长度放在byte[]里string内容前面,告诉接收方,我接下去要解析的这个string字段的长度。
public class MsgLogin
{
public string m_strIDName = null;
public string m_strPassWord = null;
}
以这个消息为例
消息的构成就是
内容 | 类型 | 长度 |
---|---|---|
消息号 | ushort | 2 |
包体长度 | ushort | m_strIDName长度 + m_strPassWord长度 |
m_strIDName长度 | ushort | 转换byte[]后取长度 |
m_strIDName内容 | byte[] | byte[]本身 |
m_strPassWord长度 | ushort | 转换byte[]后取长度 |
m_strPassWord内容 | byte[] | byte[]本身 |
public class MsgLogin
{
public OperateCode m_opCode = OperateCode.C2S_LOGIN;
public string m_strIDName = null;
public string m_strPassWord = null;
public byte[] Package()
{
m_strIDName = "1234";
m_strPassWord = "4321";
byte[] bytesOpcode = BitConverter.GetBytes((ushort)m_opCode);
byte[] bytesIDName = System.Text.Encoding.UTF8.GetBytes(m_strIDName);
byte[] bytesPassWord = System.Text.Encoding.UTF8.GetBytes(m_strIDName);
/*
Buffer.BlockCopy拼接byte[]这里简写
帐号长度 = bytesIDName.Length
密码长度 = bytesPassWord.Length
包体长度 = bytesIDName.Length + bytesPassWord.Length
包头:[bytesOpcode + 包体长度]
包体;[帐号长度 + bytesIDName + 密码长度 + bytesPassWord]
消息包 = 包头 + 包体
*/
return ***;
}
}
3.数据包的解析
最终得到byte[]发送,
接收到的时候
取得1,2位 解析 得到消息号
取得3,4位 解析 得到包体长度
按照包体长度向后取到整个包体
在包体中 解析 包体的1,2位解析得帐号(假定长度是ushort)
2(第一个string长度) + 帐号长度 + 1
2(第一个string长度) + 帐号长度 + 2
取得这2位 得到密码长度后,继续解析密码
这样一套 组装 和 解析的概念 就完成了。
程序学无止尽。
欢迎大家沟通,有啥不明确的,或者不对的,也可以和我私聊
我的QQ 334524067 神一般的狄狄