You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

发送Encryption Response后Minecraft服务器返回异常数据包问题排查

Minecraft客户端登录失败:未收到Login Success包,出现随机数据包ID

我正在开发Java程序模拟Minecraft客户端登录服务器,已经完成Handshake、Login Start、Encryption Request和Encryption Response数据包的处理,但始终收不到预期的Login Success(包ID 0x02),反而每次读取到随机的数据包ID。本地测试时服务器日志提示用户名会话无效,直接断开连接。

读取数据包代码

// S->C Login Success
int loginPacketSize = readVarInt(input); // packet size
int loginPacketId = readVarInt(input); // packet id

if(loginPacketId != 0x02) { // We want login success
    System.out.println("Bad packet id: " + loginPacketId);
    
    if(loginPacketId == 0x00) { // If it's a disconnect packet
        disconnected(input);
    }
}

readVarInt方法

public static int readVarInt(DataInputStream in) throws IOException {
    int i = 0;
    int j = 0;
    while (true) {
        int k = in.readByte();
        i |= (k & 0x7F) << j++ * 7;
        if (j > 5) throw new RuntimeException("VarInt too big");
        if ((k & 0x80) != 128) break;
    }
    return i;
}

错误现象

每次运行输出随机数据包ID:

Bad packet id: 134
Bad packet id: 3757
Bad packet id: 9673

本地服务器日志:

[14:41:32 ERROR]: Username 'EnderPoint_07' tried to join with an invalid session
[14:41:32 INFO]: /127.0.0.1:54157 lost connection: Failed to verify username!


问题根源与修复方案

1. 加密数据流未处理(核心问题)

在发送Encryption Response之后,服务器和客户端之间的所有通信都会使用协商好的共享密钥加密。你现在直接用原始的DataInputStream读取,拿到的是加密后的乱码,自然解析出随机的VarInt值(也就是你看到的随机数据包ID)。

修复:发送完Encryption Response后,立即将输入输出流包装成加密流:

// 假设已生成sharedSecret和服务器公钥
Cipher decryptCipher = Cipher.getInstance("AES/CFB8/NoPadding");
decryptCipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(sharedSecret, "AES"), new IvParameterSpec(sharedSecret));
Cipher encryptCipher = Cipher.getInstance("AES/CFB8/NoPadding");
encryptCipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(sharedSecret, "AES"), new IvParameterSpec(sharedSecret));

// 替换原流为加密流
input = new DataInputStream(new CipherInputStream(input, decryptCipher));
output = new DataOutputStream(new CipherOutputStream(output, encryptCipher));

之后所有数据包的读写都要通过这个加密后的流进行。

2. 在线模式验证失败

本地服务器默认开启在线模式,会向Mojang验证用户会话。你的客户端没有完成Mojang的会话认证流程,所以服务器直接拒绝登录,发送Disconnect包(但因为你没解密,读出来的ID是乱的)。

两种解决方式:

  • 离线测试:修改服务器server.properties中的online-mode=false,关闭在线验证,即可正常收到Login Success包。
  • 支持在线模式:需要完成Mojang会话认证——用服务器发送的serverId、共享密钥、公钥生成哈希,向Mojang认证服务器请求会话令牌,服务器验证通过后才会发送Login Success。

3. 修正Disconnect包处理

现在的代码判断loginPacketId == 0x00永远不会触发,因为数据包是加密的。必须先启用加密流,才能正确读取Disconnect包的内容,获取具体的拒绝原因。


内容的提问来源于stack exchange,提问作者EnderPoint

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 16:00:27