我们在之前写的NetManager
,属于消息发送的底层工具类,它提供各种消息的收发接口,协议收发需要另外一个单独的工具类。
ProtocolManager
在Unity工程的Scripts——Net文件夹里,新建ProtocolManager
我们先实现一下获取私钥的协议
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public class ProtocolManager { public static void SecretRequest() { MsgSecret msg = new MsgSecret(); NetManager.Instance.AddProtoListener(ProtocolEnum.MsgSecret, (secMsg) => { MsgSecret msgSecret = secMsg as MsgSecret; NetManager.Instance.SetKey(msgSecret.Secret); }); NetManager.Instance.SendMessage(msg);
} }
|
私钥是客户端一开始链接服务器就要请求的,我们把获取私钥的请求放在NetManager.ConnectCallback
中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
void ConnectCallback(IAsyncResult ar) { try { m_IsConnecting = false; ProtocolManager.SecretRequest(); Debug.Log("Socket Connect Success"); catch (SocketException se) { } }
|
NetTest
新建一个NetTest
的Mono脚本并挂载,用来测试网络
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| using UnityEngine;
public class NetTest : MonoBehaviour { void Start() { NetManager.Instance.Connect("127.0.0.1", 8011); }
void Update() { NetManager.Instance.Update(); } private void OnApplicationQuit() { NetManager.Instance.CloseConnection(); } }
|
心跳包协议
客户端:
- 每隔一段时间发送
MsgPing
- 在
Msg
处理线程内,每收到一个服务端MsgPing
,更新客户端的lastPongTime
- 在
Ping
线程内,长期没有收到服务端的消息就断开连接。
服务端:
- 每收到一个客户端的消息,更新此客户端的
lastPingTime
- 在while循环内每次比较所有客户端没有Ping过的时长。
- 长期没有Ping的客户端断开连接
在NetManager
中添加PingThread
方法,并在连接成功的回调中开启这个线程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| void ConnectCallback(IAsyncResult ar) { try { m_IsConnecting = false; m_HeartThread = new Thread(PingThread); m_HeartThread.IsBackground = true; m_HeartThread.Start(); ProtocolManager.SecretRequest(); Debug.Log("Socket Connect Success"); } catch (SocketException se) { } }
void PingThread() { while(m_Socket != null && m_Socket.Connected) { long timeNow = GetTimeStamp(); if (timeNow - lastPingTime > m_PingInterval) { MsgPing msgPing = new MsgPing(); SendMessage(msgPing); lastPingTime = GetTimeStamp(); } if(timeNow - lastPongTime > m_PingInterval * 4) { CloseConnection(false); } } }
|
服务器发送心跳包
修改服务器的SysMsg
(就是之前命名为MsgSecret的文件,如果没改请改名),添加MsgPing
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| [ProtoBuf.ProtoContract()] public class MsgSecret : MsgBase { public MsgSecret() { ProtoType = ProtocolEnum.MsgSecret; } [ProtoBuf.ProtoMember(1)] public override ProtocolEnum ProtoType { get; set; } [ProtoBuf.ProtoMember(2)] public string? Secret; } [ProtoBuf.ProtoContract()] public class MsgPing : MsgBase { public MsgPing() { ProtoType = ProtocolEnum.MsgPing; } [ProtoBuf.ProtoMember(1)] public override ProtocolEnum ProtoType { get; set; } }
|
修改服务器的MsgHandler
,添加MsgPing
方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| namespace SimpleServer.Net { public partial class MsgHandler { public static void MsgSecret(ClientSocket client, MsgBase msg) { if (msg == null) { Debug.LogError(client.Socket?.RemoteEndPoint?.ToString() + "MsgSecret协议接收错误!"); } else { MsgSecret msgSecret = (MsgSecret)msg; msgSecret.Secret = ServerSocket.SecretKey; ServerSocket.Send(client, msgSecret); } } public static void MsgPing(ClientSocket client, MsgBase msg) { if (msg == null) { Debug.LogError(client.Socket?.RemoteEndPoint?.ToString() + "MsgPing协议接收错误!"); } client.LastPingTime = ServerSocket.GetTimeStamp(); MsgPing msgPong = new MsgPing(); ServerSocket.Send(client, msgPong); } } }
|
服务器维护的client的LastPingTime
字段,只有接收到client的心跳包时才会被更新。