欢迎光临散文网 会员登陆 & 注册

MineCraft协议——握手篇(讲解+实现)

2019-12-28 17:29 作者:NewtonCY  | 我要投稿

前几天开服和朋友联机。配置端口转发。不知道什么毛病,好像没有起作用的样子。为了确认到底有没有收到客户端发过来的包,抄了一段代码来监听发过来的TCP报文。

看着收到的数据陷入沉思,不禁好奇MC是怎么握手的。于是去了解了一下

mc客户端一次握手传来的数据

上图是客户端握手时传来的数据。这篇文章的最后将以此为案例分析其中的内容。

参考资料:

[1]http://mcprotocol.orecraft.cn/Server_List_Ping

[2]http://mcprotocol.orecraft.cn/Protocol

一、与服务器会话的状态

mc客户端与服务器的会话,可能有这么几种状态。

  • Handshaking 正在握手

  • Status 查询服务器状态

  • Login 正在登入

  • Play 正在进行游戏

和服务器建立连接后,总是进入握手状态,握手过程中,可以选择接下来进入Status状态或者Login状态,登录成功后,将进入Play状态。

二、常用的字段及和编码格式

1、定长整数

定长整数:有 Byte,Sort,Int,Long 和 UnsignedByte,UnsignedSort,UnsignedInt,UnsignedLong。

Byte用一字节编码,sort两字节,int四字节,long八字节,均采用大端方式排列,即表示高有效位的字节排在前面,第一位是符号位,负数用补码

UnsignedByte,UnsignedSort,UnsignedInt,UnsignedLong。为无符号整数,没有符号位。

想必学过几门编程语言的人对上述的数据类型不会陌生

2、变长整数

变长整数:VarInt,VarLong。

VarInt用于编码int类型,占1~5个字节,每个字节用低七位来编码数字。最高一位如果为1,表示还有下一字节。最高位为0,表示没有下一字节了。表示低有效位的字节排在前面,表示高有效位的字节排在后面。

符号位无需单独处理,不把符号位看成符号位就行

举例:

-1 = 0x FF FF FF FF 转成 VarInt: 0xFF FF FF FF 0F

1 = 0x 00 00 00 01 转成VarInt: 0x 01

127 = 0x00 00 00 7F 转成VarInt:0x7F

128 = 0x00 00 00 80 转成VarInt: 0x80 01

VarInt表示比较小的整数时很节约字节,对于比较大的整数也能表示。但是对于任何负数,它始终会占用5个字节

VarLong用于编码long类型,转化方法同理

在参考资料[1]中有例程可以参考

3、字符串

字符串由两部分组成,一个VarInt前缀,表示字符串按UTF-8编码之后的长度,接下来是用UTF-8编码的字符串本身

举例:

“FML” 按utf-8编码->0x46 4D 4C->长度为三字节,3按VarInt编码:0x03->完整的字符串:0x03 46 4D 4C

三、包格式

mc将要发送的数据封装成包(packet)再发送给服务器或客户端

Packet format

包序号(Packet ID),或者更准确的叫包ID,表示这个包的类型,这个包是干什么的,有哪些字段。注意,包可分为发往客户端的包和发往服务器的包两类。这两类包的id是分开编的,也就是说发往服务器的id=0的包与发往客户端id=0的包意义完全不一样。

数据,由字段依次连接而成,具体包含哪些字段取决于包ID。

四、mc握手和SLP

通过Server List Ping (SLP)可以得到服务器的有关信息(有几个玩家在线,服务器的名字版本号,装了哪些mod)和连接的时延

具体过程如下:

1、客户端发送Handshake包


Handshake包

例子:

00 

D4 02

0E 31 32 37 2E 30 2E 30 2E 31 00 46 4D 4C 00

27 66 

02

分行写只是为了看着清楚

包ID=0,对应第一行

字段

协议号VarInt:本文章基于mc 1.12.2 协议号340,对应第二行D4 02

服务器地址String:字符串,ip或者域名,比如127.0.0.1,对应第三行。测试用的forge客户端,实际发送的是“127.0.0.1\0FML\0” \0表示00,ASCII码空字符。FML显然是forge mod loader的缩写。

端口号UnsignedSort:默认25565,对应第四行 27 66。

下一个状态VarInt:只能是1或者2。1表示进入Status状态,2表示进入Login状态,例子中是02,要获取服务器信息则应该填01

2、客户端发Request包

Request包

没啥好说的

对应的二进制应该是0x0100。第一个字节是包长度,第二个字节包id,数据部分没有

3、服务器回应Response包

Response包

一个字段,string,json格式的服务器信息

例如:

{   

 "version": {        "name": "1.8.7",        "protocol": 47    },    

"players": {        "max": 100,        "online": 5,        "sample": [            {                "name": "thinkofdeath",                "id": "4566e69f-c907-48ee-8d71-d7ba5aa00d20"            }        ]    },   

 "description": {        "text": "Hello world"    },   

 "favicon": "data:image/png;base64,<data>" 

}


4、此时可以发送ping包用来测试服务器时延

ping包和pong包

五、协议实现

我对mc的一部分协议做了非常好的封装,提供了简洁的API可以调用,并且写了一个SLP的demo

这部分代码可以从我的gitHub上克隆

SLP的demo

如图,提供了很好用的API,欢迎大家跟我一起hacking MC的协议。

先定一个小目标,做出可以登上服务器挖矿的工具人!

GitHub仓库:

https://github.com/newtoncy/mcProtocol

MineCraft协议——握手篇(讲解+实现)的评论 (共 条)

分享到微博请遵守国家法律