Bunny~ 发表于 2020-5-13 09:07

申请会员ID:Bunny_i【申请通过】


申 请 I D:Bunny_i
个人邮箱:1160706050@qq.com
原创技术文章:转自个人GitHub C#JSON解析
https://github.com/1160706050/ParseJSON


这是我个人学习C#过程中的JSON解析







Hmily 发表于 2020-5-14 10:58

抱歉,未能达到申请要求,申请不通过,可以关注论坛官方微信(吾爱破解论坛),等待开放注册通知。

ps:这个同类的内容太多了,提供更多技术分析文章这类的来申请吧。

发表于 2020-5-15 10:01

那我在加个Minecraft 服务器信息获取,配合JSON
根据https://wiki.vg/Server_List_Ping公开的wiki
已知Minecraft 服务器信息获取的数据类型


可得知需要以下信息
Protocol Version :VarInt类型;协议版本,这个不重要,不知道协议版本时可以设置为-1
Server Address : String类型;服务器地址
Server Port : UShort类型;服务器端口
Next state : VarInt类型;下一个状态,状态为1,登录为2。

首先我们要先准备好这些数据,根据他所示的数据类型进行组合
首先是VarInt;

这里的VarInt与一般的VarInt相似,而Minecraft的VarInt只使用了7个位。
我们需要先实现VarInt

      public static byte[] GetVarInt(int value)
      {
            List<byte> bytes = new List<byte>();
            while ((value & -128) != 0)
            {
                // value & 127
                // 因为VarInt需要msb所以一个字节只能储存7bit数据
                // 127二进制是0111 1111和value做位与,取7bit的有效数据
                // value & 128
                // 7bit的有效数据做位或 128二进制是1111 1111,添加msb信息
                bytes.Add((byte)(value & 127 | 128));

                // 向右移7bit,处理剩余高位的数据
                value = (int)((uint)value >> 7);

            }
            bytes.Add((byte)value);
            return bytes.ToArray();
      }

实现完VarInt之后,就是String类型,根据Minecraft wiki文档中可得知

可能不太好理解,简单来说就是 字符串数据的长度(VarInt类型)+字符串数据
GetVarInt(Encoding.UTF8.GetBytes(字符串数据))+Encoding.UTF8.GetBytes(字符串数据)
当完成这些数据类型之后,就可以进行组合了。

注意,还要查看一下Minecraft 发包格式



这里使用的是未经Zlib压缩的数据,格式也很好理解,完整的包=Packet ID+Data的长度信息(VarInt)+packet id(VarInt) +data,
好了,知道这些信息之后 就可以开始写TCP通讯了
首先C# 创建TCP客户端对象
            TcpClient tcpClient = new TcpClient(host, port);
顺便设置一下缓存接收区
            // 设置TCP接收缓冲区大小
            tcpClient.ReceiveBufferSize = 1024 * 1024;
准备一下握手的数据包
// 握手数据包
byte[] packetId = GetVarInt(0);
            byte[] protocolVersion = GetVarInt(-1);
            byte[] serverAddress = Encoding.UTF8.GetBytes(host);
            serverAddress = ConcatBytes(GetVarInt(serverAddress.Length), serverAddress);
            byte[] serverPort = BitConverter.GetBytes((ushort)port); Array.Reverse(serverPort);
            byte[] nextState = GetVarInt(1);
// 请求数据包跟进
byte[] statusRequest = GetVarInt(0);

这里的ConcatBytes作用是将多个数组添加在一起
      public static byte[] ConcatBytes(params byte[][] bytes)
      {
            List<byte> result = new List<byte>();
            foreach (byte[] array in bytes)
                result.AddRange(array);
            return result.ToArray();
      }
然后进行发送TPC数据
            // 发送数据包
            tcpClient.Client.Send(packet);
            tcpClient.Client.Send(statusRequest);
发送之后肯定要接收,接收的数据格式和我们发送时一样。
VarInt类型的数据包总长度信息+packet ID+数据
这里我单独吧读取写了方法以便调用
public static byte[] GetVarInt(int value)
      {
            List<byte> bytes = new List<byte>();
            while ((value & -128) != 0)
            {
                // value & 127
                // 因为VarInt需要msb所以一个字节只能储存7bit数据
                // 127二进制是0111 1111和value做位与,取7bit的有效数据
                // value & 128
                // 7bit的有效数据做位或 128二进制是1111 1111,添加msb信息
                bytes.Add((byte)(value & 127 | 128));

                // 向右移7bit,处理剩余高位的数据
                value = (int)((uint)value >> 7);

            }
            bytes.Add((byte)value);
            return bytes.ToArray();
      }

      /// <summary>
      /// 读VarInt类型的整数
      /// </summary>
      /// <param name="tcpClient"></param>
      /// <returns></returns>
      public static int ReadVarInt(TcpClient tcpClient)
      {
            int numRead = 0;
            int result = 0;
            byte read;
            do
            {
                read = ReadData(tcpClient, 1);
                int value = (read & 0b01111111);
                result |= (value << (7 * numRead));

                numRead++;
                if (numRead > 5)
                {
                  throw new Exception("VarInt is too big");
                }
            } while ((read & 0b10000000) != 0);

            return result;
      }


      /// <summary>
      /// 读取TCP接收数据
      /// </summary>
      /// <param name="tcpClient"></param>
      /// <param name="length"></param>
      /// <returns></returns>
      public static byte[] ReadData(TcpClient tcpClient, int length)
      {
            if (length > 0)
            {
                byte[] cache = new byte;
                Receive(tcpClient, cache, 0, length);
                return cache;
            }
            return new byte[] { };
      }

      /// <summary>
      /// 读取TCP接收数据
      /// </summary>
      private static void Receive(TcpClient tcpClient, byte[] buffer, int offset, int size)
      {
            int read = 0;

            // read=实际读取量,如果小于要取的数据大小,则继续读取TCP数据
            while (read < size)
            {
                read += tcpClient.Client.Receive(buffer, offset + read, size - read, SocketFlags.None);
            }
      }

我们先读取总长度信息
int dataLength = ReadVarInt(tcpClient);
在读取长度信息长度的数据
                //读取数据包总长度的数据,使用List以便后期数据处理
                List<byte> packetData = new List<byte>(ReadData(tcpClient, dataLength));
这时候已经吧数据全取回来了
接着我们读Packet ID,一样的他是VarInt数据类型 我们也要读取一下,这里我又建了一些方法,供list数据处理,与上面不同的是,他是已经读取出来的数据待处理,而上面的是读取TCP缓冲区的数据
/// <summary>
      /// 读VarInt类型的整数
      /// </summary>
      /// <param name="tcpClient"></param>
      /// <returns></returns>
      public static int ReadVarInt(List<byte> lists)
      {
            int numRead = 0;
            int result = 0;
            byte read;
            do
            {
                read = ReadByte(lists);
                int value = (read & 0b01111111);
                result |= (value << (7 * numRead));

                numRead++;
                if (numRead > 5)
                {
                  throw new Exception("VarInt is too big");
                }
            } while ((read & 0b10000000) != 0);

            return result;
      }

      public static byte ReadByte(List<byte> lists)
      {
            // 读取一个字节
            byte result = lists;
            // 删除已读字节
            lists.RemoveAt(0);
            return result;
      }

      public static byte[] ReadData(List<byte> lists, int size)
      {
            // 连续读取字节
            byte[] result = lists.Take(size).ToArray();
            // 删除已读字节
            lists.RemoveRange(0, size);
            return result;
      }

然后判断他是否为0x00,这里是根据Minecraft wiki得知的,是0x00包头ID数据的话后面紧跟这JSON数据字符串
如wiki所提供的例子

我们读取JSON数据

                  // 响应的内容应该是呈JSON字符串数据,字符串=字符串数据的长度信息(VarInt)+字符串数据
                  // 先读取JSON长度
                  int JsonLength = ReadVarInt(packetData);
                  // 读取数据内容,并将字节数组转换成UTF8字符串
                  string result = Encoding.UTF8.GetString(ReadData(packetData, JsonLength));
就获取到一个Minecraft 服务器响应的JSON编码的字符串


这时就能使用到之前的JSON解析

我们调试一下,成功取出


这里只取了version里的信息,其他同理。这个还可以根据WIkI文档里实现各种各样的功能,可模拟Minecraft 网络通讯协议实现

发表于 2020-5-15 10:04

Hmily 发表于 2020-5-14 10:58
抱歉,未能达到申请要求,申请不通过,可以关注论坛官方微信(吾爱破解论坛),等待开放注册通知。

ps: ...

我又回复补充了一部分Minecraft wiki里的网络通讯协议内容

发表于 2020-5-15 10:11

Hmily 发表于 2020-5-15 16:33

I D:Bunny_i
邮箱:1160706050@qq.com

申请通过,欢迎光临吾爱破解论坛,期待吾爱破解有你更加精彩,ID和密码自己通过邮件密码找回功能修改,请即时登陆并修改密码!
登陆后请在一周内在此帖报道,否则将删除ID信息。

Bunny_i 发表于 2020-5-15 18:11

Hmily 发表于 2020-5-15 16:33
I D:Bunny_i
邮箱:



已登录,特此报道。
页: [1]
查看完整版本: 申请会员ID:Bunny_i【申请通过】