添加bt与tracker交互

Change-Id: I1327c2ed89a76bee8e2abeb8cb3014b56357689a
diff --git a/src/main/java/com/pt/utils/BdecodeUtils.java b/src/main/java/com/pt/utils/BdecodeUtils.java
deleted file mode 100644
index c77f8b4..0000000
--- a/src/main/java/com/pt/utils/BdecodeUtils.java
+++ /dev/null
@@ -1,84 +0,0 @@
-package com.pt.utils;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.*;
-
-public class BdecodeUtils {
-    public static Object decode(byte[] data) throws IOException {
-        try (ByteArrayInputStream in = new ByteArrayInputStream(data)) {
-            return decodeNext(in);
-        }
-    }
-
-    private static Object decodeNext(InputStream in) throws IOException {
-        int prefix = in.read();
-        if (prefix == -1) {
-            throw new IOException("Unexpected end of stream");
-        }
-
-        if (prefix >= '0' && prefix <= '9') {
-            // 字符串,回退一个字节给parseString处理
-            in.reset();
-            return parseString(in, prefix);
-        } else if (prefix == 'i') {
-            return parseInteger(in);
-        } else if (prefix == 'l') {
-            return parseList(in);
-        } else if (prefix == 'd') {
-            return parseDict(in);
-        } else {
-            throw new IOException("Invalid bencode prefix: " + (char) prefix);
-        }
-    }
-
-    private static String parseString(InputStream in, int firstDigit) throws IOException {
-        // 读长度前缀
-        StringBuilder lenStr = new StringBuilder();
-        lenStr.append((char) firstDigit);
-        int b;
-        while ((b = in.read()) != -1 && b != ':') {
-            lenStr.append((char) b);
-        }
-        int length = Integer.parseInt(lenStr.toString());
-
-        // 读内容
-        byte[] buf = new byte[length];
-        int read = in.read(buf);
-        if (read < length) throw new IOException("Unexpected end of stream reading string");
-        return new String(buf);
-    }
-
-    private static long parseInteger(InputStream in) throws IOException {
-        StringBuilder intStr = new StringBuilder();
-        int b;
-        while ((b = in.read()) != -1 && b != 'e') {
-            intStr.append((char) b);
-        }
-        return Long.parseLong(intStr.toString());
-    }
-
-    private static List<Object> parseList(InputStream in) throws IOException {
-        List<Object> list = new ArrayList<>();
-        int b;
-        while ((b = in.read()) != 'e') {
-            in.reset();
-            list.add(decodeNext(in));
-        }
-        return list;
-    }
-
-    private static Map<String, Object> parseDict(InputStream in) throws IOException {
-        Map<String, Object> map = new LinkedHashMap<>();
-        int b;
-        while ((b = in.read()) != 'e') {
-            in.reset();
-            String key = (String) decodeNext(in);
-            Object value = decodeNext(in);
-            map.put(key, value);
-        }
-        return map;
-    }
-}
-
diff --git a/src/main/java/com/pt/utils/BencodeCodec.java b/src/main/java/com/pt/utils/BencodeCodec.java
new file mode 100644
index 0000000..2117d43
--- /dev/null
+++ b/src/main/java/com/pt/utils/BencodeCodec.java
@@ -0,0 +1,216 @@
+package com.pt.utils;
+
+import java.io.*;
+import java.net.InetAddress;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.*;
+
+public class BencodeCodec {
+
+    /* ------------- 编码部分 ------------- */
+
+    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');
+    }
+
+    /* ------------- 解码部分 ------------- */
+
+    public static Object decode(byte[] data) throws IOException {
+        try (ByteArrayInputStream in = new ByteArrayInputStream(data)) {
+            in.mark(data.length);
+            return decodeNext(in);
+        }
+    }
+
+    private static Object decodeNext(InputStream in) throws IOException {
+        int prefix = in.read();
+        if (prefix == -1) {
+            throw new IOException("Unexpected end of stream");
+        }
+
+        in.mark(1024);
+
+        if (prefix >= '0' && prefix <= '9') {
+            in.reset();
+            return parseString(in);
+        } else if (prefix == 'i') {
+            return parseInteger(in);
+        } else if (prefix == 'l') {
+            return parseList(in);
+        } else if (prefix == 'd') {
+            return parseDict(in);
+        } else {
+            throw new IOException("Invalid bencode prefix: " + (char) prefix);
+        }
+    }
+
+    private static String parseString(InputStream in) throws IOException {
+        StringBuilder lenStr = new StringBuilder();
+        int b;
+        while ((b = in.read()) != -1 && b != ':') {
+            if (b < '0' || b > '9') {
+                throw new IOException("Invalid string length character: " + (char) b);
+            }
+            lenStr.append((char) b);
+        }
+        if (b == -1) {
+            throw new IOException("Unexpected end of stream reading string length");
+        }
+        int length = Integer.parseInt(lenStr.toString());
+
+        byte[] buf = new byte[length];
+        int offset = 0;
+        while (offset < length) {
+            int read = in.read(buf, offset, length - offset);
+            if (read == -1) {
+                throw new IOException("Unexpected end of stream reading string data");
+            }
+            offset += read;
+        }
+
+        return new String(buf, StandardCharsets.UTF_8);
+    }
+
+    private static long parseInteger(InputStream in) throws IOException {
+        StringBuilder intStr = new StringBuilder();
+        int b;
+        while ((b = in.read()) != -1 && b != 'e') {
+            intStr.append((char) b);
+        }
+        if (b == -1) {
+            throw new IOException("Unexpected end of stream reading integer");
+        }
+        return Long.parseLong(intStr.toString());
+    }
+
+    private static List<Object> parseList(InputStream in) throws IOException {
+        List<Object> list = new ArrayList<>();
+        int b;
+        while (true) {
+            in.mark(1);
+            b = in.read();
+            if (b == -1) {
+                throw new IOException("Unexpected end of stream reading list");
+            }
+            if (b == 'e') {
+                break;
+            }
+            in.reset();
+            list.add(decodeNext(in));
+        }
+        return list;
+    }
+
+    private static Map<String, Object> parseDict(InputStream in) throws IOException {
+        Map<String, Object> map = new LinkedHashMap<>();
+        int b;
+        while (true) {
+            in.mark(1);
+            b = in.read();
+            if (b == -1) {
+                throw new IOException("Unexpected end of stream reading dictionary");
+            }
+            if (b == 'e') {
+                break;
+            }
+            in.reset();
+            String key = (String) decodeNext(in);
+            Object value = decodeNext(in);
+            map.put(key, value);
+        }
+        return map;
+    }
+
+    /* ------------- 其他辅助方法 ------------- */
+
+    // 构造单个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);
+    }
+}
diff --git a/src/main/java/com/pt/utils/BencodeUtils.java b/src/main/java/com/pt/utils/BencodeUtils.java
deleted file mode 100644
index 2d3850d..0000000
--- a/src/main/java/com/pt/utils/BencodeUtils.java
+++ /dev/null
@@ -1,107 +0,0 @@
-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);
-    }
-}