在上一节我们将消息都添加到了列表当中,这一节我们将处理列表中的消息。
一般情况下,列表中的消息分为两类,一类是需要游戏前台处理的消息,一类是需要后台处理的。后台处理的消息包括心跳包等,因为这类消息当玩家不玩游戏转到别的软件时需要持续处理。
我们在NetManager
里面开启两个线程,一个是消息处理线程,一个是心跳包线程,消息处理线程负责区分前台消息和后台消息。
1 2
| private Thread m_MsgThread; private Thread m_HeartThread;
|
消息处理线程
在客户端成功连接服务器时,初始化m_MsgThread
,所以在ConnectCallback
中添加:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
void ConnectCallback(IAsyncResult ar) { try { Socket socket = (Socket)ar.AsyncState; socket.EndConnect(ar); FirstEvent(NetEvent.ConnectSucc, ""); m_MsgThread = new Thread(MsgThread); m_MsgThread.IsBackground = true; m_MsgThread.Start(); m_IsConnecting = false; } catch (SocketException se) { } }
|
将客户端和服务器Proto文件夹的MsgSecret.cs的名称修改为SysMsg.cs,它是一个总的消息文件。然后在里面添加客户端心跳包的协议。心跳包内部不包含任何信息,服务器和客户端都是自己记录时间。
1 2 3 4 5 6 7 8 9 10
| [ProtoBuf.ProtoContract()] public class MsgPing : MsgBase { public MsgPing() { ProtoType = ProtocolEnum.MsgPing; } [ProtoBuf.ProtoMember(1)] public override ProtocolEnum ProtoType { get ; set; } }
|
客户端也要记录自己的时间戳,先在NetManager
里面添加lastPongTime
(最后一次从服务器接收的消息的时间)和lastPingTime
(最后一次从服务器发送消息的时间)
1 2
| static long lastPingTime; static long lastPongTime;
|
然后和服务器一样在客户端里面添加获取时间戳的函数
1 2 3 4 5
| public static long GetTimeStamp() { TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0); return Convert.ToInt64(ts.TotalSeconds); }
|
消息处理线程分拣出来的前台消息需要Unity处理,所以我们还需要在NetManager
添加前台消息的列表
1
| private List<MsgBase> m_UnityMsgList;
|
在InitState
中都初始化好
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
void InitState() { m_Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); m_ReadBuff = new ByteArray(); m_IsConnecting = false; m_IsClosing = false; m_MsgList = new List<MsgBase>(); m_UnityMsgList = new List<MsgBase>(); m_MsgCount = 0; lastPingTime = GetTimeStamp(); lastPongTime = GetTimeStamp(); }
|
终于,我们在NetManager
添加MsgThread
回调方法,在异步线程处理消息,注意先处理后台,再处理前台
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
| void MsgThread() { while (m_Socket != null && m_Socket.Connected) { if (m_MsgList.Count <= 0) continue; MsgBase msgBase = null; lock (m_MsgList) { if (m_MsgList.Count > 0) { msgBase = m_MsgList[0]; m_MsgList.RemoveAt(0); } } if(msgBase != null) { if (msgBase is MsgPing) { lastPongTime = GetTimeStamp(); m_MsgCount--; } else { lock (m_UnityMsgList) { m_UnityMsgList.Add(msgBase); } } } else { break; } } }
|
Unity前台处理消息
我们在NetManager
里面添加Update
函数来交给Unity的生命周期,并且声明新的委托来发布消息。
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 42 43
| public delegate void ProtoListener(MsgBase msg); private Dictionary<ProtocolEnum, ProtoListener> m_ProtoListenerDic = new Dictionary<ProtocolEnum, ProtoListener>(); public void AddProtoListener(ProtocolEnum protocolEnum, ProtoListener listener) { m_ProtoListenerDic[protocolEnum] = listener; } void FirstProto(ProtocolEnum protocolEnum, MsgBase msg) { if(m_ProtoListenerDic.ContainsKey(protocolEnum)) { m_ProtoListenerDic[protocolEnum].Invoke(msg); } } public void Update() { MsgUpdate(); } void MsgUpdate() { if (m_Socket != null && m_Socket.Connected) { if (m_MsgCount == 0) return; MsgBase msgBase = null; lock(m_UnityMsgList) { if(m_UnityMsgList.Count > 0) { msgBase = m_UnityMsgList[0]; m_UnityMsgList.RemoveAt(0); m_MsgCount--; } } if(msgBase != null) { FirstProto(msgBase.ProtoType, msgBase); } } }
|