zoukankan      html  css  js  c++  java
  • ET5.0-添加心跳功能

    ET5.0 demo中添加心跳功能

    一、服务端

    1:添加文件夹,在Model/Module文件夹下新建文件夹 Hearbeat

    2:添加心跳配置文件   SessionHeartbeatComponentAwakeSystem

    namespace ETModel
    {
        [ObjectSystem]
        public class SessionHeartbeatComponentAwakeSystem : AwakeSystem<SessionHeartbeatComponent>
        {
            public override void Awake(SessionHeartbeatComponent self)
            {
                HeartbeatMgrComponent.Ins.AddSessionHeartbeat(self);
            }
        }
    
        public class SessionHeartbeatComponent: Component
        {
            public const int DestroySeesiontTime = 40;//多长 时间 没收到客户端消息 就直接销毁
            public const int DestroySeesiontSecondTotalNum = 10;//一秒内收到多少条消息 就直接销毁
    
            public int SecondTotalMessageNum = 0;//一秒内 累计收到的消息条数
            public int UpReceiveMessageDistance = 0;//距离上次收到消息 有多长时间
    
            //销毁Session
            public void DisposeSession()
            {
                Entity.Dispose();
            }
            public override void Dispose()
            {
                HeartbeatMgrComponent.Ins.RemoveSessionHeartbeat(InstanceId);//必须在 base.Dispose(); 前面调 因为 Dispose会吧id置为0
                base.Dispose();
                SecondTotalMessageNum = 0;//一秒内 累计收到的消息条数
                UpReceiveMessageDistance = 0;//距离上次收到消息 有多长时间
            }
        }
    }
    View Code

    3、添加心跳检测组件  HeartbeatMgrComponentAwakeSystem

    using System.Collections.Generic;
    
    namespace ETModel
    {
        [ObjectSystem]
        public class HeartbeatMgrComponentAwakeSystem: AwakeSystem<HeartbeatMgrComponent>
        {
            // public static List<SessionHeartbeatComponent> _DestroyHeartbeatComponents = new List<SessionHeartbeatComponent>();
            public override void Awake(HeartbeatMgrComponent self)
            {
                HeartbeatMgrComponent.Ins = self;
                self.Awake();
            }
        }
    
        public class HeartbeatMgrComponent: Component
        {
            public List<long> SessionHeartbeatIds = new List<long>();
            public Dictionary<long, SessionHeartbeatComponent> SessionHeartbeatDic = new Dictionary<long, SessionHeartbeatComponent>();
            public static HeartbeatMgrComponent Ins { get; set; }
            
            public async void Awake()
            {
                while (true)
                {
                    await Game.Scene.GetComponent<TimerComponent>().WaitAsync(1000);
                    for (int i = 0; i < SessionHeartbeatIds.Count; i++)
                    {
                        //一秒内收到消息的数量
                        SessionHeartbeatDic[SessionHeartbeatIds[i]].SecondTotalMessageNum = 0;
                        SessionHeartbeatDic[SessionHeartbeatIds[i]].UpReceiveMessageDistance++;
    
                        if (SessionHeartbeatDic[SessionHeartbeatIds[i]].UpReceiveMessageDistance>=
                            SessionHeartbeatComponent.DestroySeesiontTime)
                        {
                            SessionHeartbeatDic[SessionHeartbeatIds[i]].DisposeSession();//如果上次收到时间 过长 就直接销毁DisposeSession
                        }
                    }
                }
            }
            
            /// <summary>
            /// 添加SessionHeartbeat 组件
            /// </summary>
            /// <param name="sessionHeartbeat"></param>
            public void AddSessionHeartbeat(SessionHeartbeatComponent sessionHeartbeat)
            {
                SessionHeartbeatDic[sessionHeartbeat.InstanceId] = sessionHeartbeat;
                SessionHeartbeatIds.Add(sessionHeartbeat.InstanceId);
            }
    
            /// <summary>
            /// 移除组件
            /// </summary>
            /// <param name="id"></param>
            public void RemoveSessionHeartbeat(long id)
            {
                if (SessionHeartbeatDic.ContainsKey(id))
                {
                    SessionHeartbeatDic.Remove(id);
                    SessionHeartbeatIds.Remove(id);
                }
            }
            
        }
    
    }
    View Code

    4、添加Session挂载心跳组件 SessionHeartbeatAwakeSystem

    namespace ETModel
    {
        [ObjectSystem]
        public class SessionHeartbeatAwakeSystem:AwakeSystem<Session, AChannel>
        {
            public override void Awake(Session self, AChannel a)
            {
                if (self.Network.AppType==AppType.None)//不是任何APPType 就是客户端
                {
                    self.AddComponent<SessionHeartbeatComponent>();
                }
            }
        }
    }
    View Code

    5、Program.cs 文件在 AppType.AllServer中挂载心跳组件

    //与客户端有连接都要加
    Game.Scene.AddComponent<HeartbeatMgrComponent>();//心跳管理组件 验证服 也是要加的

    6、处理心跳消息,在OuterMessage.proto 添加

    //心跳消息
    message C2G_Heartbeat // IMessage
    {
        int32 RpcId = 90;
    }

    在 OuterMessageDispatcher 文件中添加心跳逻辑处理

    using ETModel;
    
    namespace ETHotfix
    {
        public class OuterMessageDispatcher: IMessageDispatcher
        {
            public void Dispatch(Session session, ushort opcode, object message)
            {
                DispatchAsync(session, opcode, message).Coroutine();
            }
            
            public async ETVoid DispatchAsync(Session session, ushort opcode, object message)
            {
                /********* 添加心跳修改 **************/
                SessionHeartbeatComponent sessionHeartbeatComponent = session.GetComponent<SessionHeartbeatComponent>();
                if (sessionHeartbeatComponent == null)
                {
                    Log.Info("---not heartbeat compinent---");
                    session.Dispose();//心跳组件 没有 直接销毁
                    return;
                }
                sessionHeartbeatComponent.UpReceiveMessageDistance = 0;//重置上次收到消息的时间
                //如果收到 一秒收到的消息 大于规定的消息 就认定是DOSS攻击 直接销毁
                if (++sessionHeartbeatComponent.SecondTotalMessageNum >=
                    SessionHeartbeatComponent.DestroySeesiontSecondTotalNum)
                {
                    //断开连接
                    sessionHeartbeatComponent.DisposeSession();
                    //直接封号
                    //  UserHelp.StopSealOrRelieve(sessionUserComponent.UserId,true,"DOSS攻击封号"); //不要封号容易误封
                    return;
                }
                /********* 添加心跳修改 **************/
                
                
                // 根据消息接口判断是不是Actor消息,不同的接口做不同的处理
                switch (message)
                {
                    case IActorLocationRequest actorLocationRequest: // gate session收到actor rpc消息,先向actor 发送rpc请求,再将请求结果返回客户端
                    {
                        long unitId = session.GetComponent<SessionPlayerComponent>().Player.UnitId;
                        ActorLocationSender actorLocationSender = await Game.Scene.GetComponent<ActorLocationSenderComponent>().Get(unitId);
    
                        int rpcId = actorLocationRequest.RpcId; // 这里要保存客户端的rpcId
                        long instanceId = session.InstanceId;
                        IResponse response = await actorLocationSender.Call(actorLocationRequest);
                        response.RpcId = rpcId;
    
                        // session可能已经断开了,所以这里需要判断
                        if (session.InstanceId == instanceId)
                        {
                            session.Reply(response);
                        }
                        
                        break;
                    }
                    case IActorLocationMessage actorLocationMessage:
                    {
                        long unitId = session.GetComponent<SessionPlayerComponent>().Player.UnitId;
                        ActorLocationSender actorLocationSender = await Game.Scene.GetComponent<ActorLocationSenderComponent>().Get(unitId);
                        actorLocationSender.Send(actorLocationMessage).Coroutine();
                        break;
                    }
                    case IActorRequest actorRequest:  // 分发IActorRequest消息,目前没有用到,需要的自己添加
                    {
                        break;
                    }
                    case IActorMessage actorMessage:  // 分发IActorMessage消息,目前没有用到,需要的自己添加
                    {
                        break;
                    }
                    case C2G_Heartbeat c2GHeartbeat: //单独处理心跳
                    {   
                        //心跳不做处理
                        Log.Info("--收到心跳--");
                        break;
                    }
                    default:
                    {
                        // 非Actor消息
                        Game.Scene.GetComponent<MessageDispatcherComponent>().Handle(session, new MessageInfo(opcode, message));
                        break;
                    }
                }
            }
        }
    }
    View Code

    7、重新编译

    二、客户端

    1、在客户端 Assets/Hotfix/Module/Demo新建文件  HeartbeatComponent

    using ETModel;
    
    namespace ETHotfix
    {
        [ObjectSystem]
        public class HeartbeatComponentAwakeSystem: AwakeSystem<HeartbeatComponent>
        {
            public override void Awake(HeartbeatComponent self)
            {
               self.Awake();
            }
            
            public async void DetectionNetworkType(Session session)
            {
                // while (!session.IsDisposed)
                // {
                //     await ETModel.Game.Scene.GetComponent<TimerComponent>().WaitAsync(1000);
                //     //如果当前是无网络状态 发送连接失败的消息
                //     if (NetworkType.None == SdkMgr.Ins.GetNetworkInfo())
                //     {
                //         session.session.Error = (int) SocketError.NetworkDown;
                //         session.Dispose();
                //     }
                // }
            }
        }
        
        /// <summary>
        /// 心跳组件
        /// </summary>
        public class HeartbeatComponent: Component
        {
            public static C2G_Heartbeat heartBeat = new C2G_Heartbeat();
    
            public async void Awake()
            {
                //每间隔10秒发一条 心跳消息
                // DetectionNetworkType(session);//检测网络连接状态
                Log.Debug("--status:" + !IsDisposed);
                while (!IsDisposed)
                {
                    await ETModel.Game.Scene.GetComponent<TimerComponent>().WaitAsync(10000);
                    
                    Log.Debug("--ping---" + ETModel.SessionComponent.Instance.Session.IsDisposed);
                    
                    if (ETModel.SessionComponent.Instance.Session.IsDisposed)
                    {
                        Log.Error("--服务器连接断开-");
                    }
                    
                    ETModel.SessionComponent.Instance.Session.Send(heartBeat);
                }
            }
        }
    }
    View Code

    运行:

    1、重新编译服务器,并运行。不要移动小人,停止40秒(服务端配置的是40秒)左右,服务器会断开客户端。

    2、修改客户端,挂载心跳组件发送心跳

    我是在登录gate服务器成功时添加的组件,文件LoginHelper.cs中添加组件

       //挂载心跳组建
                    Game.Scene.GetComponent<SessionComponent>().Session.AddComponent<HeartbeatComponent>();

    3、再次运行,不会断开客户端。服务器每隔10秒收到心跳消息。

    注意:这里只处理了服务端检测心跳,去掉无心跳的链接。没有处理客户端的连接状态。可以将心跳消息修改为  IRequest,通过Call方式发生心跳。

    心跳组件参考: 五星麻将的心跳组件。

  • 相关阅读:
    docker 容器卷及提交
    docker 容器命令及解析
    docker镜像常用命令及解析
    drf 中集成swgger api功能文档
    drf 二次封装Response
    drf 中 自定义 异常处理方法
    drf 中自定义登录 以及token验证
    drf_vue对接极验验证
    django 信号的使用
    element plut tree renderContent
  • 原文地址:https://www.cnblogs.com/cj8988/p/14451409.html
Copyright © 2011-2022 走看看