修改

Change-Id: I18193ed65cb577ff03f82079a7c0a4d98ca3f236
diff --git a/src/main/java/com/pt/utils/BencodeUtils.java b/src/main/java/com/pt/utils/BencodeUtils.java
new file mode 100644
index 0000000..2d3850d
--- /dev/null
+++ b/src/main/java/com/pt/utils/BencodeUtils.java
@@ -0,0 +1,107 @@
+package com.pt.utils;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.*;
+
+public class BencodeUtils {
+
+    // 通用bencode编码接口
+    public static void encode(Object obj, OutputStream out) throws IOException {
+        if (obj instanceof String) {
+            encodeString((String) obj, out);
+        } else if (obj instanceof Number) {
+            encodeInteger(((Number) obj).longValue(), out);
+        } else if (obj instanceof byte[]) {
+            encodeBytes((byte[]) obj, out);
+        } else if (obj instanceof List) {
+            encodeList((List<?>) obj, out);
+        } else if (obj instanceof Map) {
+            encodeMap((Map<String, Object>) obj, out);
+        } else {
+            throw new IllegalArgumentException("Unsupported type: " + obj.getClass());
+        }
+    }
+
+    public static byte[] encode(Object obj) {
+        try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+            encode(obj, baos);
+            return baos.toByteArray();
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static void encodeString(String s, OutputStream out) throws IOException {
+        byte[] bytes = s.getBytes(StandardCharsets.UTF_8);
+        out.write(String.valueOf(bytes.length).getBytes(StandardCharsets.US_ASCII));
+        out.write(':');
+        out.write(bytes);
+    }
+
+    private static void encodeBytes(byte[] bytes, OutputStream out) throws IOException {
+        out.write(String.valueOf(bytes.length).getBytes(StandardCharsets.US_ASCII));
+        out.write(':');
+        out.write(bytes);
+    }
+
+    private static void encodeInteger(long value, OutputStream out) throws IOException {
+        out.write('i');
+        out.write(Long.toString(value).getBytes(StandardCharsets.US_ASCII));
+        out.write('e');
+    }
+
+    private static void encodeList(List<?> list, OutputStream out) throws IOException {
+        out.write('l');
+        for (Object item : list) {
+            encode(item, out);
+        }
+        out.write('e');
+    }
+
+    private static void encodeMap(Map<String, Object> map, OutputStream out) throws IOException {
+        out.write('d');
+        List<String> keys = new ArrayList<>(map.keySet());
+        Collections.sort(keys);  // bencode字典必须按key排序
+        for (String key : keys) {
+            encodeString(key, out);
+            encode(map.get(key), out);
+        }
+        out.write('e');
+    }
+
+    // 构造单个compact peer的二进制格式 (4字节IP + 2字节端口)
+    public static byte[] buildCompactPeer(String ip, int port) {
+        try {
+            InetAddress addr = InetAddress.getByName(ip);
+            ByteBuffer buffer = ByteBuffer.allocate(6);
+            buffer.put(addr.getAddress());
+            buffer.putShort((short) port);
+            return buffer.array();
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    // 构造多个compact peer的二进制拼接
+    public static byte[] buildCompactPeers(List<String> ips, List<Integer> ports) {
+        if (ips.size() != ports.size()) throw new IllegalArgumentException("IPs and ports list size mismatch");
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        for (int i = 0; i < ips.size(); i++) {
+            out.write(buildCompactPeer(ips.get(i), ports.get(i)), 0, 6);
+        }
+        return out.toByteArray();
+    }
+
+    // 构造tracker响应字典,至少包含interval和peers
+    public static byte[] buildTrackerResponse(int interval, byte[] peersCompact) {
+        Map<String, Object> dict = new LinkedHashMap<>();
+        dict.put("interval", interval);
+        dict.put("peers", peersCompact);
+        return encode(dict);
+    }
+}