发送自定义类的2进制信息

        1.进程BaseData类

        2.实现其中的序列化、反序列化、获取字节数等

        3.发送自定义类数据时,序列化

        4.接受自定义类数据时,反序列化

区分消息

        为发送的消息添加标识,比如添加消息ID(类似于网络传输过程中的头文件)

        在所有发送的消息的头部加上消息ID

        举例说明:
        消息构成
        如果选用int类型作为消息ID的类型
        前4个字节为消息ID
        后面的字节为数据类的内容
        ####***************************
        这样每次收到消息时,先把前4个字节取出来解析为消息ID
        再根据ID进行消息反序列化即可

1.创建消息基类,基类继承BaseData,基类添加获取消息ID的方法或者属性

using System.Collections;
using System.Collections.Generic;
using System.Text;
using UnityEngine;

/// <summary>
/// 玩家数据类
/// </summary>
public class PlayerData : BaseData
{
    public string name;
    public int atk;
    public int lev;

    /// <summary>
    /// 获取玩家类的二进制的字节长度
    /// </summary>
    /// <returns>返回的是字节长度</returns>
    public override int GetBytesNum()
    {
        return 4+4+4+Encoding.UTF8.GetBytes(name).Length;
    }

    /// <summary>
    /// 读取玩家类的字节长度
    /// </summary>
    /// <param name="bytes">字节</param>
    /// <param name="beginIndex">开始的位置</param>
    /// <returns>占了多长的字节</returns>
    public override int Reading(byte[] bytes, int beginIndex = 0)
    {
        int index = beginIndex;
        name = ReadString(bytes, ref index);
        atk = ReadInt(bytes, ref index);
        lev = ReadInt(bytes, ref index);
        return index - beginIndex;
    }

    /// <summary>
    /// 写入所要存进去的数据
    /// </summary>
    /// <returns>所转换好的二进制的字符串</returns>
    public override byte[] Writeing()
    {
        int index = 0;
        byte[] bytes = new byte[GetBytesNum()];
        Writestring(bytes, name, ref index);
        WriteInt(bytes, atk, ref index);
        WriteInt(bytes, lev, ref index);
        return bytes;
    }
}

2.让想要被发送的消息继承该类,实现序列化反序列化方法

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BaseMsg : BaseData
{
    public override int GetBytesNum()
    {
        throw new System.NotImplementedException();
    }

    public override int Reading(byte[] bytes, int beginIndex = 0)
    {
        throw new System.NotImplementedException();
    }

    public override byte[] Writeing()
    {
        throw new System.NotImplementedException();
    }

    /// <summary>
    /// 头文件编号
    /// </summary>
    /// <returns>头文件的编号,每个编号对应的一个数据类型,这个编号对应的数据类型可以自己来决定</returns>
    public virtual int GetID()
    {
        return 0;
    }
}

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 给要传输的文件加入头文件
/// </summary>
public class PlayerMsg : BaseMsg
{
    public int playerID;
    public PlayerData playerData;
    
    public override byte[] Writeing()
    {
        int index = 0;
        byte[] bytes = new byte[GetBytesNum()];

        //先写消息ID
        WriteInt(bytes, GetID(), ref index);

        //再写这个消息的成员变量
        WriteInt(bytes, playerID, ref index);
        WriteData(bytes, playerData, ref index);

        return bytes;
    }

    public override int Reading(byte[] bytes, int beginIndex = 0)
    {
        //反序列化不需要解析ID,应该在反序列化之前就将ID反序列化出来
        //用来判断到底使用哪一个自定义类来反序列化
        int index = beginIndex;
        playerID = ReadInt(bytes, ref index);
        playerData = ReadData<PlayerData>(bytes, ref index);
        return index - beginIndex;
    }

    public override int GetBytesNum()
    {
        return 4 + 4 + playerData.GetBytesNum();
    }

    /// <summary>
    /// 自定义的消息ID 主要用于区分是哪一个消息类
    /// </summary>
    /// <returns>头文件的编号,每个编号对应的一个数据类型,这个编号对应的数据类型可以自己来决定</returns>
    public override int GetID()
    {
        return 1001;
    }
}

3.修改客户端和服务端收发消息的逻辑

        对客户端的修改

using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using UnityEngine;

public class Lesson6 : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        //1.创建套接字Socket
        Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

        //2.用Connect方法与服务端相连
        IPEndPoint ipPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080);  //这里面的IP地址和端口号指的是服务端的而不是客户端的
        try
        {
            socket.Connect(ipPoint);
        }
        catch(SocketException e)
        {
            if (e.ErrorCode == 10061)
                print("服务器拒绝连接");
            else
                print("连接服务器失败" + e.ErrorCode);
            return;
        }

        //3.用Send和Receive相关方法收发数据

        //接收数据
        byte[] receiveBytes = new byte[1024];
        int receiveNum = socket.Receive(receiveBytes);

        //首先解析消息的ID
        //使用字节数组中的前四个字节 得到ID
        int msgID = BitConverter.ToInt32(receiveBytes, 0);

        //如果id与之匹配客户端就执行下面的命令
        switch (msgID)
        {
            case 1001:
                PlayerMsg msg = new PlayerMsg();
                msg.Reading(receiveBytes, 4);

                print(msg.playerID);
                print(msg.playerData.name);
                print(msg.playerData.atk);
                print(msg.playerData.lev);

                break;
        }


        print("收到服务端发来的消息:" + Encoding.UTF8.GetString(receiveBytes, 0, receiveNum));

        //发送数据
        socket.Send(Encoding.UTF8.GetBytes("你好,我是客户端"));

        //4.用Shutdown方法释放连接
        socket.Shutdown(SocketShutdown.Both);

        //5.关闭套接字
        socket.Close();
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

对服务端的修改

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace AllData
{
    class Program
    {
        static void Main(string[] args)
        {
            //1.创建套接字Socket
            Socket socketTcp = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            //2.用Bind方法将套接字与本地地址绑定(注:这里用try、catch是为了防止端口号已经被使用了
            try
            {
                //指定服务器的IP地址和端口号
                IPEndPoint ipPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080);
                socketTcp.Bind(ipPoint);
            }
            catch (Exception e)
            {
                Console.WriteLine("绑定报错" + e.Message);
                return;
            }

            //3.用Listen方法监听,参数代表可以接入客户端最大的数量
            socketTcp.Listen(1024);
            Console.WriteLine("服务端绑定监听结束,等待客户端连入");

            //4.用Accept方法等待客户端连接
            //5.建立连接,Accept返回新套接字
            Socket socketClient = socketTcp.Accept();  //该函数的执行,会一直坚持到有客户端加入才会继续到下一步
            Console.WriteLine("有客户端连入了");

            //6.用Send和Receive相关方法收发数据
            //发送
            //socketClient.Send(Encoding.UTF8.GetBytes("欢迎加入服务端"));

            //写入数据
            PlayerMsg msg = new PlayerMsg();
            msg.playerID = 196;
            msg.playerData = new PlayerData();
            msg.playerData.name = "缘笙箫";
            msg.playerData.atk = 100;
            msg.playerData.lev = 20;

            socketClient.Send(msg.Writeing());
            
            //接收
            byte[] result = new byte[1024];
            //返回值为接受到的字节数
            int receiveNum = socketClient.Receive(result);
            Console.WriteLine("接受到了{0}发来的消息:{1}",
                               socketClient.RemoteEndPoint.ToString(),
                               Encoding.UTF8.GetString(result, 0, receiveNum));

            //7.用Shutdown方法释放连接
            socketClient.Shutdown(SocketShutdown.Both);

            //8.关闭套接字
            socketClient.Close();


            Console.WriteLine("按任意键退出");
            Console.ReadKey();
        }
    }
}

unity消息机制 unity 消息_tcp/ip