问题描述
我一直在开发一个基本上类似于 Minechat 的 Java 程序(基于文本的应用程序,仅用于查看聊天.)我从来没有真正使用过网络太多,所以问题是弄清楚如何正确发送数据包.我目前正处于与服务器创建握手的位置.经过数小时的研究,我想出了以下代码,但它总是遇到失败!(例外)"信息.对我来说,一切看起来都是正确的,但据我所知,这可能是 100% 错误的.如果有人能指出我在这里做错了什么,我将不胜感激.
I have been working on a java program that basically acts like Minechat(text-based app to just view chat.) I have never really worked with networking too much, so the issue is figuring out how to send packets correctly. I am currently at the position of creating the handshake with the server. After hours of research, I have come up with the following code, but it always runs into the "Failed! (Exception)" message. To me, everything looks correct, but for all I know it could be 100% wrong. If someone could point out what I'm doing wrong here, I'd really appreciate it.
作为参考,请随意使用 this 和 this.
For reference, feel free to use this and this.
public static void main(String[] args) throws IOException { host = new InetSocketAddress("162.244.165.111", 48040); socket = new Socket(); System.out.println("Connecting..."); socket.connect(host, 3000); System.out.println("Done!"); System.out.println("Making streams..."); output = new DataOutputStream(socket.getOutputStream()); input = new DataInputStream(socket.getInputStream()); System.out.println("Done!"); System.out.println("Attempting handshake... "+host.getAddress().toString().substring(1)); byte[] msg = ("47;"+host.getAddress().toString().substring(1)+";"+host.getPort()+";2;").getBytes(Charset.forName("UTF-16")); output.writeInt(msg.length+Integer.valueOf(0x00)); output.writeByte(0x00); output.write(msg); output.flush(); try { if (input.readByte() != 0x02) System.out.println("Failed!"); else System.out.println("Done!"); } catch (EOFException e) { System.out.println("Failed! (Exception)"); } }
更多研究表明我使用 Byte 数组,但这让我对如何表示字符串以及需要使用字符串感到困惑?
More research suggests I use a Byte array, but this confuses me on how to represent a string and using strings is required?
推荐答案
看这个页面 http://wiki.vg/协议 看起来您没有写入足够的数据,也没有按正确的顺序.您还需要使用 varint 这是一种特殊类型的数据表示整数.
Looking at this page http://www.51sjk.com/Upload/Articles/1/0/338/338405_20221206105725019.jpg it looks like your not writing enough data nor in the right order. You also need to be using varint which is a special type of data representation of an integer.
此问题的相关链接:
- 握手协议
- 数据包格式
- Server Ping 说明及示例(其中涉及握手)
- Handshake Protocol
- Packet format
- Server Ping Explanation and Example (which involves handshake)
<小时>状态 ping 的工作原理如下:
The status ping works as follows:
C->S : Handshake State=1 C->S : Request S->C : Response C->S : Ping S->C : Pong
C 是客户端,S 是服务器
使用 wiki 和提供的代码示例,我修改了您的代码以遵循整个状态请求.
Using the wiki and the provided code samples I modified your code to follow the entire status request.
public static void main(String [] args) throws IOException { String address = "162.244.165.111"; int port = 48040; InetSocketAddress host = new InetSocketAddress(address, port); Socket socket = new Socket(); System.out.println("Connecting..."); socket.connect(host, 3000); System.out.println("Done!"); System.out.println("Making streams..."); DataOutputStream output = new DataOutputStream(socket.getOutputStream()); DataInputStream input = new DataInputStream(socket.getInputStream()); System.out.println("Done!"); System.out.println("Attempting handshake... "+host.getAddress().toString()); byte [] handshakeMessage = createHandshakeMessage(address, port); // C->S : Handshake State=1 // send packet length and packet writeVarInt(output, handshakeMessage.length); output.write(handshakeMessage); // C->S : Request output.writeByte(0x01); //size is only 1 output.writeByte(0x00); //packet id for ping // S->C : Response int size = readVarInt(input); int packetId = readVarInt(input); if (packetId == -1) { throw new IOException("Premature end of stream."); } if (packetId != 0x00) { //we want a status response throw new IOException("Invalid packetID"); } int length = readVarInt(input); //length of json string if (length == -1) { throw new IOException("Premature end of stream."); } if (length == 0) { throw new IOException("Invalid string length."); } byte[] in = new byte[length]; input.readFully(in); //read json string String json = new String(in); // C->S : Ping long now = System.currentTimeMillis(); output.writeByte(0x09); //size of packet output.writeByte(0x01); //0x01 for ping output.writeLong(now); //time!? // S->C : Pong readVarInt(input); packetId = readVarInt(input); if (packetId == -1) { throw new IOException("Premature end of stream."); } if (packetId != 0x01) { throw new IOException("Invalid packetID"); } long pingtime = input.readLong(); //read response // print out server info System.out.println(json); System.out.println("Done!"); } public static byte [] createHandshakeMessage(String host, int port) throws IOException { ByteArrayOutputStream buffer = new ByteArrayOutputStream(); DataOutputStream handshake = new DataOutputStream(buffer); handshake.writeByte(0x00); //packet id for handshake writeVarInt(handshake, 4); //protocol version writeString(handshake, host, StandardCharsets.UTF_8); handshake.writeShort(port); //port writeVarInt(handshake, 1); //state (1 for handshake) return buffer.toByteArray(); } public static void writeString(DataOutputStream out, String string, Charset charset) throws IOException { byte [] bytes = string.getBytes(charset); writeVarInt(out, bytes.length); out.write(bytes); } public static void writeVarInt(DataOutputStream out, int paramInt) throws IOException { while (true) { if ((paramInt & 0xFFFFFF80) == 0) { out.writeByte(paramInt); return; } out.writeByte(paramInt & 0x7F | 0x80); paramInt >>>= 7; } } 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; }