Moba项目开发(一)打造网络通信系统-任务10:10.接收到的报文的处理逻辑

1.处理报文的逻辑()

原理:按照报文的序号,进行顺序处理,如果是收到超过当前顺序加1的报文,先进行缓存,等待下一个报文处理后再处理后面的报文
public void Handle(BufferEntity buffer)

2.在USocket里面增加一个接口,在Updata里面进行调用,服务器可以开线程循环调用

原理:从消息队列的字典中取消息,将其反序列化,打印日志,传给业务处理逻辑的函数(local.Handle(bufferEntity);)
public void Handle()

3.业务逻辑处理函数

原理:先判断报文的类型,
1)如果是ACK的报文,将发送报文队列中的报文包去掉(按照序号去掉,sendPackage.TryRemove(buffer.sn,out bufferEntity)),不再进行超时重传的检测;
2)如果是业务报文,先回复服务器,表示已经收到这个报文,传给HandleLogincPackage(BufferEntity buffer)这个接口,这个函数就是检测这个报文的序号,如果这个序号大于1就缓存起来到awaitHandle这个线程安全的队列,处理完这个数据后,再从缓冲区取数据包,如果缓冲区没这下一个数据包,如果有的化再调用本函数

4.代码

//UClient.cs
using System.Net;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Threading;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Game.Net
{
    //客户端的代理
    public class UClient
    {
        //参数
        public IPEndPoint endPoint;
        USocket uSocket;                          //USocket内部封装了发送的接口
        public int sessionID;                     //会话ID,客户端只需要接受报文的会话ID赋值就行了
                                                  //服务端要拿到不同的会话ID区分不同的客户端
        public int sendSN = 0;                    //发送序号
        public int handleSN = 0;                  //处理的序号,为了保证报文的顺序性
        //这里为什么要当作形参来传递呢?
        //为了应对以后的业务扩展

        Action<BufferEntity> handleAction;        //定义处理报文的函数,实际就是分发报文给各个游戏模块

        //先来个构造函数->完成初始化
        //sendSN                                  发送的序号
        //handlSN                                 处理的序号
        //sessionID                               会话ID
        //Action<BufferEntity> dispatchNetEvent   派发报文的事件传递进来,也就是待处理的消息
        public UClient(USocket uSocket,IPEndPoint endPoint,int sendSN,int handlSN,int sessionID, Action<BufferEntity> dispatchNetEvent)
        {
            this.uSocket = uSocket;
            this.endPoint = endPoint;
            this.sendSN = sendSN;
            this.handleSN = handleSN;
            this.sessionID = sessionID;
            handleAction = dispatchNetEvent;

            //初始化就开始调用时间统计
            CheckOutTime();
        }

        //处理消息
        //按照报文的序号,进行顺序处理,如果是收到超过当前顺序加1的报文,先进行缓存,等待下一个报文处理后再处理后面的报文
        public void Handle(BufferEntity buffer)
        {
            //服务器给我们客户端已经分配了一个会话ID
            if (0 == this.sessionID && buffer.session != 0)
            {
                //把会话ID缓存起来
                this.sessionID = buffer.session;
            }
            //判断消息的类型
            switch(buffer.messageType)
            {
                case 0:      //0是ACK确认报文
                    BufferEntity bufferEntity;
                    if(sendPackage.TryRemove(buffer.sn,out bufferEntity))
                    {
                        //移除成功打印日志
                        Debug.Log($"收到ACK确认报文,序号是:{buffer.sn}");
                    }
                    break;
                case 1:      //1是业务报文
                    BufferEntity ackPacka = new BufferEntity(buffer);
                    uSocket.SendACK(ackPacka); //先告诉服务器,我客户端已经收到这个报文

                    //再来处理业务报文
                    HandleLogincPackage(buffer);

                    break;
                defaultbreak;
            }
        }

        //定义字典存缓存,int是序号,值是BufferEntity,使用线程安全的字典
        ConcurrentDictionary<int, BufferEntity> awaitHandle = new ConcurrentDictionary<int, BufferEntiy>();
        //处理业务报文
        void HandleLogincPackage(BufferEntity buffer)
        {
            //若过处理的需要小于当前处理的序号,直接返回
            if (buffer.sn <= handleSN)
            {
                return;
            }

            //压入缓冲区
            if (buffer.sn - handleSN > 1)
            {
                if (WaitHandle.TryAdd(buffer.sn, buffer))
                {
                    Debug.Log($"收到错序的报文:{buffer.sn}");
                }
                //如果不是错序的报文,直接返回
                return;
            }
            //如果不是错序的报文
            //更新一下已经处理的报文
            //涉及到委托相关的知识
            handleSN = buffer.sn;
            if (handleAction != null)
            {
                handleAction(buffer);
            }

            //判断缓冲区里面有没有下一条要处理的数据
            //如果有就remove移除
            BufferEntity nextBuffer;
            if(WaitHandle.Remove(handleSN+1,out nextBuffer))
            {
                //移除放到nextBuffer
                HandleLogincPackage(nextBuffer);
            }
        }

        //定义字典存缓存,int是序号,值是BufferEntity,使用线程安全的字典
        ConcurrentDictionary<int, BufferEntity> sendPackage = new ConcurrentDictionary<int, BufferEntiy>();
        //发送报文的接口
        //这里不需要在void前加async,因为在下面的send已经实现了
        public void Send(BufferEntity package)
        {
            //更新报文发送的时间
            package.time = TimeHelper.Now();         //暂时先等于0
            sendSN += 1;
            package.sn = sendSN;

            package.Encoder(false);                  //对package编码,传递false表示不是ACK的报文
            if (sessionID != 0)                      //等于0表示还没跟服务器建立连接
            {
                //缓存起来,因为可能需要重发(缓存需要定义一个字典)
                sendPackage.TrtAdd(sendSN, package);
            }
            else
            {
                //还没跟服务器建立连接,所以不需要j进行缓存
            }
            uSocket.Send(package.buffer, endPoint);   //客户端的endPoint没啥用,还可以再优化下 


        }

        //定义超时的时间
        int overtime = 150;
        //超时重传检测的接口
        //需要定期从字典里面取出报文,然后判断是否超时就可以了
        public async void CheckOutTime()
        {
            await Tash.Delay(overtime);
            foreach (var package in sendPackage.Values)
            {
                //确定是不是超过最大发送次数,考虑关闭socket
                if (TimeHelper.Now() - package.time >= overtime * 10)
                {
                    OnDisconnect();
                    return;
                }
                //如果没超过10次
                if(TimeHelper.Now()-package.time>=(package.recurCount+1)*overtime)
                {
                    package.recurCount += 1; //超过150秒就重传
                    uSocket.Send(package.buffer, endPoint);//重传,内部已经实现了(await)
                }

            }
            CheckOutTime();   //继续等待150毫秒后进行检测
        }
        //关闭socket的函数
        public void OnDisconnect()
        {
            if (local != null)
            {
                local = null;
            }
            if (udpClient != null)
            {
                uSocket.Close();
                handleAction = null;
            }
        }


    }

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值