异步接收数据、粘包分包处理、 心跳、消息分发功能
消息分发 :
字符串协议模型
//字节流协议模型
//每帧处理消息的数量
public
int
num = 15;
//消息列表
public
List
<
ProtocolBase
> msgList =
new
List
<
ProtocolBase
>();
//委托类型
public
delegate
void
Delegate
(
ProtocolBase
proto);
//消息监听表
private
Dictionary
<
string
,
Delegate
> eventDict =
new
Dictionary
<
string
,
Delegate
>();
private
Dictionary
<
string
,
Delegate
> onceDict =
new
Dictionary
<
string
,
Delegate
>();
//消息分发 区别调用onceDict的方法后悔清空对应的Key,使得“回掉一次注册一次”,而eventDict没有这种限制,“可以注册一次终身执行”
public
void
DispatchMsgEvent(
ProtocolBase
protocol)
{
string
name = protocol.GetName();
Debug
.Log(
"分发处理消息 "
+ name);
if
(eventDict.ContainsKey(name))
{
eventDict[name](protocol);
}
if
(onceDict.ContainsKey(name))
{
onceDict[name](protocol);
onceDict[name] = null;
onceDict.Remove(name);
}
}
//异步Socket线程与Update方法不在同一线程中,为了避免线程竞争,使用lock(msgList)锁住列表
public
void
Update()
{
for
(
int
i = 0; i < num; i++)
{
if
(msgList.Count > 0)
{
DispatchMsgEvent(msgList[0]);//处理一条删除一条,保证消息列表不会太长
lock
(msgList)
msgList.RemoveAt(0);
}
else
{
break;
}
}
//网络链接模块 流程 Socket-->connect-->receive
//接收回调
private
void
ReceiveCb(
IAsyncResult
ar)
{
try
{
int
count = socket.EndReceive(ar);
//读缓冲区的长度大于0
buffCount = buffCount + count;
ProcessData();
socket.BeginReceive(readBuff, buffCount,
BUFFER_SIZE - buffCount, SocketFlags.None,
ReceiveCb, readBuff);
}
catch
(
Exception
e)
{
Debug
.Log(
"ReceiveCb失败:"
+ e.Message);
status = Status.None;
}
}
//消息处理
private
void
ProcessData()
{
//小于长度字节
if
(buffCount <
sizeof
(
Int32
))
return;
//以消息长度拷贝到新数组
Array
.Copy(readBuff, lenBytes,
sizeof
(
Int32
));
//拿到消息长度
msgLength = BitConverter.ToInt32(lenBytes, 0);
//流里的消息产长度小于 一条消(包体+包头)
if
(buffCount < msgLength +
sizeof
(
Int32
))
return;
//处理消息
ProtocolBase
protocol = proto.Decode(readBuff,
sizeof
(
Int32
), msgLength);
Debug
.Log(
"收到消息 "
+ protocol.GetDesc());
//添加进消息集合 Update中一直在判断消息集合的长度 而消息的出车一定要在Update执行之前
lock
(msgDist.msgList)
{
msgDist.msgList.Add(protocol);
}
//清除已处理的消息
int
count = buffCount - msgLength -
sizeof
(
Int32
);
//从第一条消息末尾拷贝 自己拷贝自己的数据长度
Array.Copy(readBuff,sizeof(Int32)+ msgLength, readBuff, 0, count);
//读缓冲区的长度大于0,调用本身,实现循环
buffCount = count;
if
(buffCount > 0)
{
ProcessData();
}
}
//心跳
public
void
Update()
{
//消息
msgDist.Update();
//如果在链接状态下的话
if
(status ==
Status
.Connected)
{//当前时间-上次发送时>30 就发送心跳协议
if
(
Time
.time - lastTickTime > heartBeatTime)
{
ProtocolBase
protocol =
NetMgr
.GetHeatBeatProtocol();
Send(protocol);
lastTickTime = Time.time;
}
}
}
//心跳
public
static
ProtocolBase
GetHeatBeatProtocol()
{
//具体的发送内容根据服务端设定改动
ProtocolBytes
protocol =
new
ProtocolBytes
();
protocol.AddString("HeatBeat");
return
protocol;
}
注册发送协议 例:
ProtocolBytes //字节流协议模型
//发送
ProtocolBytes
protocol =
new
ProtocolBytes
();
protocol.AddString("Register");
protocol.AddString(idInput.text);
protocol.AddString(pwInput.text);
Debug
.Log(
"发送 "
+ protocol.GetDesc());
NetMgr.srvConn.Send(protocol, OnRegBack);
//发送协议后,客户端需要建厅服务端的返回,发送之后OnRegBack将被调用
public
void
OnRegBack(
ProtocolBase
protocol)
{
ProtocolBytes
proto = (
ProtocolBytes
)protocol;
int
start = 0;
string
protoName = proto.GetString(start,
ref
start);
int
ret = proto.GetInt(start,
ref
start);
if
(ret == 0)
{
Debug.Log("注册成功!");
PanelMgr.instance.OpenPanel<LoginPanel>("");
Close();
}
else
{
Debug.Log("注册失败!");
}
还有一种情况(发送之后返回的不是同名的协议,可以写成(
Send(
ProtocolBase
protocol,"LoginRet",
MsgDistribution
.
Delegate
cb)的形式,监听服务端返回的
LoginRet
具体方法:
public
bool
Send(
ProtocolBase
protocol,
string
cbName,
MsgDistribution
.
Delegate
cb)
{
if
(status !=
Status
.Connected)
return
false
;
msgDist.AddOnceListener(cbName, cb);//添加进委托集合,协议键对应方法值
return
Send(protocol);
}
))
//实际发送消息的方法
public
bool
Send(
ProtocolBase
protocol)
{
if
(status !=
Status
.Connected)
{
Debug.LogError("[Connection]还没链接就发送数据是不好的");
return
true
;
}
byte[] b = protocol.Encode();
byte
[] length =
BitConverter
.GetBytes(b.Length);
byte[] sendbuff = length.Concat(b).ToArray();
socket.Send(sendbuff);
Debug
.Log(
"发送消息 "
+ protocol.GetDesc());
return
true
;
}
客户端接收协议
产生自己 拿到自己的位置旋转,写入流 发送协议
//添加玩家
void
AddPlayer(
string
id,
Vector3
pos,
int
score)
{
GameObject
player = (
GameObject
)Instantiate(prefab, pos,
Quaternion
.identity);
TextMesh
textMesh = player.GetComponentInChildren<
TextMesh
>();
textMesh.text = id +
":"
+ score;
players.Add(id, player);
}
*******************************
GetList UpdateInfo 他们首先解析协议参数然后执行出来里
NetMgr.srvConn.Send(proto, GetList);
NetMgr.srvConn.msgDist.AddListener("UpdateInfo", UpdateInfo);
在脚本开始运行的时候注册进集合
//更新列表
public
void
GetList(
ProtocolBase
protocol)
{
ProtocolBytes
proto = (
ProtocolBytes
)protocol;
//获取头部数值
int
start = 0;
string
protoName = proto.GetString(start,
ref
start);
int
count = proto.GetInt(start,
ref
start);
//遍历
for
(
int
i = 0; i < count; i++)
{
string
id = proto.GetString(start,
ref
start);
float
x = proto.GetFloat(start,
ref
start);
float
y = proto.GetFloat(start,
ref
start);
float
z = proto.GetFloat(start,
ref
start);
int
score = proto.GetInt(start,
ref
start);
Vector3
pos =
new
Vector3
(x, y, z);
UpdateInfo(id, pos, score);
}
}
//更新信息
public
void
UpdateInfo(
ProtocolBase
protocol)
{
//获取数值
ProtocolBytes
proto = (
ProtocolBytes
)protocol;
int
start = 0;
string
protoName = proto.GetString(start,
ref
start);
string
id = proto.GetString(start,
ref
start);
float
x = proto.GetFloat(start,
ref
start);
float
y = proto.GetFloat(start,
ref
start);
float
z = proto.GetFloat(start,
ref
start);
int
score = proto.GetInt(start,
ref
start);
Vector3
pos =
new
Vector3
(x, y, z);
UpdateInfo(id, pos, score);
}
//更新信息
public
void
UpdateInfo(
string
id,
Vector3
pos,
int
score)
{
//只更新自己的分数
if
(id == playerID)
{
UpdateScore(id, score);
return;
}
//其他人
//已经初始化该玩家
if
(players.ContainsKey(id))
{
players[id].transform.position = pos;
UpdateScore(id, score);
}
//尚未初始化该玩家
else
{
AddPlayer(id, pos, score);
}
}
、
//玩家离开
public
void
PlayerLeave(
ProtocolBase
protocol)
{
ProtocolBytes
proto = (
ProtocolBytes
)protocol;
//获取数值
int
start = 0;
string
protoName = proto.GetString(start,
ref
start);
string
id = proto.GetString(start,
ref
start);
DelPlayer(id);
}