Unity C# 网络学习(三)

27 篇文章 0 订阅

Unity C# 网络学习(三)

一.区分消息类型

  • 之前介绍的发送消息已经可以正常发送了,但是客户端和服务器是如何知道我们发送的和接收的是哪一条消息呢,这个时候就需要我们为协议加上一个编号
  • MsgBase是继承之前的序列化基类的一个消息的发送类,里面有协议编号的虚属性,在子类继承这个类时,只需要重写这个属性就可以有不同的对应编号了
public class MsgBase : DataBase
{
    public override int GetLength()
    {
        return 0;
    }
    public override byte[] GetBytes()
    {
        return null;
    }
    public override void Reading(byte[] buffer, int startIndex = 0)
    {
        
    }
    public virtual int ProtoID => -1;
}
public class PlayerMsg : MsgBase
{
    public int playerID;
    public PlayerData playerData;
    public override int GetLength()
    {
        return 4 + 4 + playerData.GetLength();
    }

    public override byte[] GetBytes()
    {
        var index = 0;
        var buffer = new byte[GetLength()];
        WriteInt(buffer,ProtoID,ref index);
        WriteInt(buffer,playerID,ref index);
        WriteData(buffer,playerData,ref index);
        return buffer;
    }

    public override void Reading(byte[] buffer, int startIndex = 0)
    {
        this.playerID = ReadInt(buffer, ref startIndex);
        this.playerData = ReadData<PlayerData>(buffer, ref startIndex);
    }
    public override int ProtoID => 1001;
}
public class PlayerData : DataBase
{
    public string name;
    public int lev;
    public int atk;
    public override int GetLength()
    {
        return 4 +
               Encoding.UTF8.GetBytes(name).Length +
               4 +
               4;
    }

    public override byte[] GetBytes()
    {
        var index = 0;
        var buffer = new byte[GetLength()];
        WriteString(buffer,this.name,ref index);
        WriteInt(buffer,this.lev,ref index);
        WriteInt(buffer,this.atk,ref index);
        return buffer;
    }

    public override void Reading(byte[] buffer, int startIndex = 0)
    {
        this.name = ReadString(buffer, ref startIndex);
        this.lev = ReadInt(buffer, ref startIndex);
        this.atk = ReadInt(buffer, ref startIndex);
    }
}

当我们发送消息时只需要序列化后发送就好,双端共享一份协议

二.分包和黏包

  • 为了解决黏包和分包的问题我们需要在协议中添加一个关于纯包体长度的参数进行通讯,用于拆分包和结合包

1.黏包

当客户端向服务器发送10次数据时,服务器接收消息时会发生黏包
在这里插入图片描述
当服务器接收的包是玩玩全全的黏包时,如下解析

        private void HandleReceiveMsg(byte[] receiveBytes, int receiveLength)
        {
            int protoID = 0;
            int length = 0;
            int nowIndex = 0;
            while (nowIndex < receiveLength)
            {
                protoID = BitConverter.ToInt32(receiveBytes, nowIndex);
                nowIndex += 4;
                length = BitConverter.ToInt32(receiveBytes, nowIndex);
                nowIndex += 4;
                if (protoID == 1001)
                {
                    PlayerMsg playerMsg = new PlayerMsg();
                    playerMsg.Reading(receiveBytes, nowIndex);
                    Console.WriteLine("接收到协议:" + protoID);
                }
                nowIndex += length;
            }
        }

2.分包

当服务器接收的数据单纯为分包的数据时

        private void HandleReceiveMsg(byte[] receiveBytes, int receiveLength)
        {
            int protoID = 0;
            int length = 0;
            int nowIndex = 0;
            receiveBytes.CopyTo(cacheBuffer, cacheIndex );
            cacheIndex += receiveLength;
            while (true)
            {
                if (receiveLength >=8)
                {
                    protoID = BitConverter.ToInt32(cacheBuffer, nowIndex);
                    nowIndex += 4;
                    length = BitConverter.ToInt32(cacheBuffer, nowIndex);
                    nowIndex += 4;
                }
                if(receiveLength - nowIndex >= length)
                {
                    if (protoID == 1001)
                    {
                        PlayerMsg playerMsg = new PlayerMsg();
                        playerMsg.Reading(cacheBuffer, nowIndex);
                        Console.WriteLine("接收到协议:" + protoID);
                    }
                    nowIndex += length;
                    if (nowIndex == cacheIndex)
                        break;
                }
                else
                {
                    break;
                }
            }
        }

3.同时是黏包和分包数据

        private void HandleReceiveMsg(byte[] receiveBytes, int receiveLength)
        {
            int protoID = 0;
            int length = 0;
            int nowIndex = 0;
            receiveBytes.CopyTo(cacheBuffer, cacheIndex );
            cacheIndex += receiveLength;
            while (true)
            {
                length = -1;
                if (cacheIndex - nowIndex >= 8)
                {
                    protoID = BitConverter.ToInt32(cacheBuffer, nowIndex);
                    nowIndex += 4;
                    length = BitConverter.ToInt32(cacheBuffer, nowIndex);
                    nowIndex += 4;
                }
                if (cacheIndex - nowIndex >= length && length != -1)
                {
                    if (protoID == 1001)
                    {
                        PlayerMsg playerMsg = new PlayerMsg();
                        playerMsg.Reading(cacheBuffer, nowIndex);
                        Console.WriteLine("接收到协议:" + protoID);
                    }
                    nowIndex += length;
                    if (nowIndex == cacheIndex)
                    {
                        cacheIndex = 0;
                        break;
                    }
                }
                else
                {
                    if (length != -1)
                    {
                        nowIndex -= 8;
                        Array.Copy(cacheBuffer,nowIndex,cacheBuffer,0,cacheIndex - nowIndex);
                        cacheIndex -= nowIndex;
                    }
                    break;
                }
            }
        }

4.黏包和分包测试

public class Lesson09 : MonoBehaviour
{
    [SerializeField] private Button _button1;
    [SerializeField] private Button _button2;
    [SerializeField] private Button _button3;

    private void Awake()
    {
        gameObject.AddComponent<NetMgr>();
        NetMgr.Instance.Connect("127.0.0.1",8080);
    }

    private void Start()
    {
        _button1.onClick.AddListener(() =>
        {
            //黏包
            var playerMsg1 = new PlayerMsg
            {
                playerID = 1,
                playerData = new PlayerData
                {
                    name = "zzs",
                    atk = 10,
                    lev = 1
                }
            };
            var playerMsg2 = new PlayerMsg
            {
                playerID = 2,
                playerData = new PlayerData
                {
                    name = "ywj",
                    atk = 10,
                    lev = 1
                }
            };
            var byte1 = playerMsg1.GetBytes();
            var byte2 = playerMsg2.GetBytes();
            var buffer = new byte[byte1.Length+byte2.Length];
            byte1.CopyTo(buffer,0);
            byte2.CopyTo(buffer,byte1.Length);
            NetMgr.Instance.SendTest(buffer);
        });
        _button2.onClick.AddListener(async () =>
        {
            //分包
            var playerMsg1 = new PlayerMsg
            {
                playerID = 1,
                playerData = new PlayerData
                {
                    name = "zzs",
                    atk = 10,
                    lev = 1
                }
            };
            var byte1 = playerMsg1.GetBytes();
            var buffer1 = new byte[10];
            var buffer2 = new byte[byte1.Length - 10];
            Array.Copy(byte1,0,buffer1,0,10);
            Array.Copy(byte1,10,buffer2,0,buffer2.Length);
            NetMgr.Instance.SendTest(buffer1);
            await Task.Delay(500);
            NetMgr.Instance.SendTest(buffer2);
        });
        _button3.onClick.AddListener(async () =>
        {
            //分包黏包
            var playerMsg1 = new PlayerMsg
            {
                playerID = 1,
                playerData = new PlayerData
                {
                    name = "zzs",
                    atk = 10,
                    lev = 1
                }
            };
            var playerMsg2 = new PlayerMsg
            {
                playerID = 2,
                playerData = new PlayerData
                {
                    name = "ywj",
                    atk = 10,
                    lev = 1
                }
            };
            var byte1 = playerMsg1.GetBytes();
            var byte2 = playerMsg2.GetBytes();
            var byte2_1 = new byte[10];
            var byte2_2 = new byte[byte2.Length - 10];
            Array.Copy(byte2,0,byte2_1,0,10);
            Array.Copy(byte2,10,byte2_2,0,byte2_2.Length);

            var buffer1 = new byte[byte1.Length + byte2_1.Length];
            byte1.CopyTo(buffer1,0);
            byte2_1.CopyTo(buffer1,byte1.Length);
            NetMgr.Instance.SendTest(buffer1);
            await Task.Delay(500);
            NetMgr.Instance.SendTest(byte2_2);
        });
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值