吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 3559|回复: 6
收起左侧

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

[复制链接]
吾爱游客  发表于 2020-5-13 09:07
申 请 I D:Bunny_i
个人邮箱:1160706050@qq.com
原创技术文章:转自个人GitHub C#JSON解析
https://github.com/1160706050/ParseJSON

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

6.png
7.png
1.png
2.png
3.png
5.png

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

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

ps:这个同类的内容太多了,提供更多技术分析文章这类的来申请吧。
吾爱游客  发表于 2020-5-15 10:01
那我在加个Minecraft 服务器信息获取,配合JSON
根据https://wiki.vg/Server_List_Ping公开的wiki
已知Minecraft 服务器信息获取的数据类型
002.png
006.png
可得知需要以下信息
Protocol Version :VarInt类型;协议版本,这个不重要,不知道协议版本时可以设置为-1
Server Address : String类型;服务器地址
Server Port : UShort类型;服务器端口
Next state : VarInt类型;下一个状态,状态为1,登录为2。

首先我们要先准备好这些数据,根据他所示的数据类型进行组合
首先是VarInt;
003.png
这里的VarInt与一般的VarInt相似,而Minecraft的VarInt只使用了7个位。
我们需要先实现VarInt

[C#] 纯文本查看 复制代码
        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文档中可得知
004.png
可能不太好理解,简单来说就是 字符串数据的长度(VarInt类型)+字符串数据
GetVarInt(Encoding.UTF8.GetBytes(字符串数据))+Encoding.UTF8.GetBytes(字符串数据)
当完成这些数据类型之后,就可以进行组合了。

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

005.png

这里使用的是未经Zlib压缩的数据,格式也很好理解,完整的包=  Packet ID+Data的长度信息(VarInt)+packet id(VarInt) +data,
好了,知道这些信息之后 就可以开始写TCP通讯了
首先C# 创建TCP客户端对象
[C#] 纯文本查看 复制代码
            TcpClient tcpClient = new TcpClient(host, port);

顺便设置一下缓存接收区
[C#] 纯文本查看 复制代码
            // 设置TCP接收缓冲区大小
            tcpClient.ReceiveBufferSize = 1024 * 1024;

准备一下握手的数据包
[C#] 纯文本查看 复制代码
 // 握手数据包
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作用是将多个数组添加在一起
[C#] 纯文本查看 复制代码
        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数据
[C#] 纯文本查看 复制代码
            // 发送数据包
            tcpClient.Client.Send(packet);
            tcpClient.Client.Send(statusRequest);

发送之后肯定要接收,接收的数据格式和我们发送时一样。
VarInt类型的数据包总长度信息+packet ID+数据
这里我单独吧读取写了方法以便调用
[C#] 纯文本查看 复制代码
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)[0];
                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[length];
                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);
            }
        }


我们先读取总长度信息
[C#] 纯文本查看 复制代码
int dataLength = ReadVarInt(tcpClient);

在读取长度信息长度的数据
[Asm] 纯文本查看 复制代码
                //读取数据包总长度的数据,使用List以便后期数据处理
                List<byte> packetData = new List<byte>(ReadData(tcpClient, dataLength));

这时候已经吧数据全取回来了
接着我们读Packet ID,一样的他是VarInt数据类型 我们也要读取一下,这里我又建了一些方法,供list数据处理,与上面不同的是,他是已经读取出来的数据待处理,而上面的是读取TCP缓冲区的数据
[C#] 纯文本查看 复制代码
/// <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[0];
            // 删除已读字节
            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所提供的例子
007.png
我们读取JSON数据

[C#] 纯文本查看 复制代码
                    // 响应的内容应该是呈JSON字符串数据,字符串=字符串数据的长度信息(VarInt)+字符串数据
                    // 先读取JSON长度
                    int JsonLength = ReadVarInt(packetData);
                    // 读取数据内容,并将字节数组转换成UTF8字符串
                    string result = Encoding.UTF8.GetString(ReadData(packetData, JsonLength));

就获取到一个Minecraft 服务器响应的JSON编码的字符串
008.png

这时就能使用到之前的JSON解析
009.png
我们调试一下,成功取出
010.png

这里只取了version里的信息,其他同理。这个还可以根据WIkI文档里实现各种各样的功能,可模拟Minecraft 网络通讯协议实现
吾爱游客  发表于 2020-5-15 10:04
Hmily 发表于 2020-5-14 10:58
抱歉,未能达到申请要求,申请不通过,可以关注论坛官方微信(吾爱破解论坛),等待开放注册通知。

ps: ...

我又回复补充了一部分Minecraft wiki里的网络通讯协议内容
吾爱游客  发表于 2020-5-15 10:11
001.png
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
邮箱:

已登录,特此报道。
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-24 12:46

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表