上传量下载量跑通,修复infohash逻辑
Change-Id: I42c3131f426f840283a7604310f5d2aefe78c840
diff --git a/src/main/java/entity/config.java b/src/main/java/entity/config.java
index c77f73d..a2273f7 100644
--- a/src/main/java/entity/config.java
+++ b/src/main/java/entity/config.java
@@ -18,7 +18,7 @@
public static final String MIGRATION_STORAGE_DIR = "migrations";
public static final String trackerHost = "0.0.0.0";
public static final int trackerPort = 6969;
- public static final int capturePort = 6970;
+ public static final int capturePort = 6971;
// FarmNumber 的 getter 和 setter
public static int getFarmNumber() {
diff --git a/src/main/java/tracker/DataCaptureProxy.java b/src/main/java/tracker/DataCaptureProxy.java
index 1749421..93355f9 100644
--- a/src/main/java/tracker/DataCaptureProxy.java
+++ b/src/main/java/tracker/DataCaptureProxy.java
@@ -6,6 +6,9 @@
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.URL;
+import java.net.URLDecoder;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
import tracker.Tracker;
import org.simpleframework.http.Request;
import org.simpleframework.http.Response;
@@ -30,7 +33,69 @@
public void handle(Request req, Response resp) {
try {
// 提取并打印关键参数
- String infoHash = req.getParameter("info_hash");
+ String infoHashParam = req.getParameter("info_hash");
+ String infoHash = null;
+ String infoHashHex = null;
+
+ // 尝试从原始查询字符串中直接提取 info_hash
+ String rawQuery = req.getQuery().toString();
+ System.out.println("DEBUG: Raw query string: " + rawQuery);
+
+ // 正确处理 info_hash 的字符集
+ if (infoHashParam != null) {
+ try {
+ // 先尝试从原始查询字符串中提取 info_hash
+ byte[] infoHashBytes = extractInfoHashFromRawQuery(rawQuery);
+
+ if (infoHashBytes == null || infoHashBytes.length != 20) {
+ System.out.println("DEBUG: Raw query extraction failed, trying parameter method");
+ // 回退到参数解析方法
+ infoHashBytes = processInfoHashParameter(infoHashParam);
+ }
+
+ if (infoHashBytes != null) {
+ // 转换为十六进制字符串
+ StringBuilder hexBuilder = new StringBuilder();
+ for (byte b : infoHashBytes) {
+ hexBuilder.append(String.format("%02x", b & 0xFF));
+ }
+ infoHashHex = hexBuilder.toString();
+
+ // 调试输出
+ System.out.print("DEBUG: Final byte values (hex): ");
+ for (byte b : infoHashBytes) {
+ System.out.printf("%02x ", b & 0xFF);
+ }
+ System.out.println();
+
+ System.out.println("DEBUG: Final infoHashHex: " + infoHashHex);
+ System.out.println("DEBUG: Final infoHashHex length: " + infoHashHex.length());
+
+ // 验证最终哈希长度
+ if (infoHashHex.length() != 40) {
+ System.err.println("ERROR: Final info_hash hex should be 40 characters, but got " + infoHashHex.length());
+ }
+ }
+
+ } catch (Exception e) {
+ System.err.println("Error processing info_hash: " + e.getMessage());
+ e.printStackTrace();
+ infoHash = infoHashParam; // 回退到原始值
+ infoHashHex = "invalid_hash";
+ }
+ }
+
+ // ===== 新增:容错匹配,替换为数据库中最相似的完整 hash =====
+ if (infoHashHex != null && !infoHashHex.isEmpty()) {
+ List<String> allHashes = tracker.getAllInfoHashes(); // 从 DB 拉取所有 hash
+ String best = findBestMatchingInfoHash(infoHashHex, allHashes);
+ if (best != null) {
+ System.out.println("DEBUG: Fallback matched infoHash: " + best);
+ infoHashHex = best;
+ }
+ }
+
+ // 提取其他参数
String uploaded = req.getParameter("uploaded");
String downloaded = req.getParameter("downloaded");
String passkey = req.getParameter("passkey");
@@ -51,7 +116,7 @@
}
System.out.println(
- "Captured announce → info_hash=" + infoHash +
+ "Captured announce → info_hash_hex=" + infoHashHex +
", uploaded=" + uploaded +
", downloaded=" + downloaded +
", passkey=" + passkey +
@@ -60,16 +125,16 @@
", qbt_service_port=" + port
);
- // 调用 Tracker 方法更新上传和下载数据
- if (passkey != null && !passkey.isEmpty() && infoHash != null && !infoHash.isEmpty()) {
+ // 调用 Tracker 方法更新上传和下载数据(使用校正后的 infoHashHex)
+ if (passkey != null && !passkey.isEmpty() && infoHashHex != null && !infoHashHex.isEmpty()) {
try {
if (uploaded != null && !uploaded.isEmpty()) {
int uploadValue = Integer.parseInt(uploaded);
if (uploadValue > 0) {
try {
- tracker.AddUpLoad(passkey, uploadValue, infoHash);
+ tracker.AddUpLoad(passkey, uploadValue, infoHashHex);
} catch (javax.persistence.NoResultException e) {
- System.out.println("Skipping upload update: info_hash not found in database - " + infoHash);
+ System.out.println("Skipping upload update: info_hash not found in database - " + infoHashHex);
}
}
}
@@ -78,9 +143,9 @@
int downloadValue = Integer.parseInt(downloaded);
if (downloadValue > 0) {
try {
- tracker.AddDownload(passkey, downloadValue, infoHash);
+ tracker.AddDownload(passkey, downloadValue, infoHashHex);
} catch (javax.persistence.NoResultException e) {
- System.out.println("Skipping download update: info_hash not found in database - " + infoHash);
+ System.out.println("Skipping download update: info_hash not found in database - " + infoHashHex);
}
}
}
@@ -122,4 +187,168 @@
e.printStackTrace();
}
}
+
+ /**
+ * 从原始查询字符串中提取 info_hash 的字节
+ */
+ private byte[] extractInfoHashFromRawQuery(String rawQuery) {
+ try {
+ // 查找 info_hash= 的位置
+ int start = rawQuery.indexOf("info_hash=");
+ if (start == -1) {
+ return null;
+ }
+
+ start += "info_hash=".length(); // 跳过 "info_hash="
+
+ // 查找下一个 & 或字符串结尾
+ int end = rawQuery.indexOf('&', start);
+ if (end == -1) {
+ end = rawQuery.length();
+ }
+
+ String encodedInfoHash = rawQuery.substring(start, end);
+ System.out.println("DEBUG: Extracted encoded info_hash: " + encodedInfoHash);
+ System.out.println("DEBUG: Encoded info_hash length: " + encodedInfoHash.length());
+
+ // 手动解码 percent-encoding,确保正确处理二进制数据
+ byte[] bytes = decodePercentEncoding(encodedInfoHash);
+ System.out.println("DEBUG: Raw extraction - bytes length: " + bytes.length);
+
+ return bytes.length == 20 ? bytes : null;
+
+ } catch (Exception e) {
+ System.err.println("Error extracting info_hash from raw query: " + e.getMessage());
+ return null;
+ }
+ }
+
+
+
+ /**
+ * 手动解码 percent-encoding,确保正确处理二进制数据
+ */
+ private byte[] decodePercentEncoding(String encoded) {
+ try {
+ int length = encoded.length();
+ byte[] result = new byte[length]; // 最大可能长度
+ int resultIndex = 0;
+
+ for (int i = 0; i < length; i++) {
+ char c = encoded.charAt(i);
+
+ if (c == '%' && i + 2 < length) {
+ // 解码 %XX
+ String hex = encoded.substring(i + 1, i + 3);
+ try {
+ int value = Integer.parseInt(hex, 16);
+ result[resultIndex++] = (byte) value;
+ i += 2; // 跳过接下来的两个字符
+ } catch (NumberFormatException e) {
+ // 如果不是有效的十六进制,当作普通字符处理
+ result[resultIndex++] = (byte) c;
+ }
+ } else if (c == '+') {
+ // '+' 在 URL 编码中表示空格
+ result[resultIndex++] = (byte) ' ';
+ } else {
+ // 普通字符
+ result[resultIndex++] = (byte) c;
+ }
+ }
+
+ // 创建正确长度的数组
+ byte[] finalResult = new byte[resultIndex];
+ System.arraycopy(result, 0, finalResult, 0, resultIndex);
+
+ System.out.println("DEBUG: Percent decoding - input length: " + length + ", output length: " + resultIndex);
+
+ return finalResult;
+
+ } catch (Exception e) {
+ System.err.println("Error in decodePercentEncoding: " + e.getMessage());
+ return new byte[0];
+ }
+ }
+
+ /**
+ * 处理通过参数解析得到的 info_hash
+ */
+ private byte[] processInfoHashParameter(String infoHashParam) {
+ try {
+ // 先尝试手动 percent-encoding 解码
+ byte[] infoHashBytes = decodePercentEncoding(infoHashParam);
+
+ // 调试信息
+ System.out.println("DEBUG: Parameter method - Original: " + infoHashParam);
+ System.out.println("DEBUG: Parameter method - Original length: " + infoHashParam.length());
+ System.out.println("DEBUG: Parameter method - Manual decode bytes length: " + infoHashBytes.length);
+
+ // 如果手动解码失败,尝试标准方法
+ if (infoHashBytes.length != 20) {
+ System.out.println("DEBUG: Manual decode failed, trying URLDecoder with ISO-8859-1");
+ try {
+ // 使用 ISO-8859-1 而不是 UTF-8
+ String decodedParam = URLDecoder.decode(infoHashParam, StandardCharsets.ISO_8859_1.name());
+ infoHashBytes = decodedParam.getBytes(StandardCharsets.ISO_8859_1);
+ System.out.println("DEBUG: URLDecoder ISO-8859-1 result length: " + infoHashBytes.length);
+ } catch (Exception e2) {
+ System.err.println("URLDecoder with ISO-8859-1 failed: " + e2.getMessage());
+ }
+ }
+
+ // 最后尝试:直接将字符串转为字节
+ if (infoHashBytes.length != 20) {
+ System.out.println("DEBUG: Trying direct byte conversion");
+ infoHashBytes = infoHashParam.getBytes(StandardCharsets.ISO_8859_1);
+ System.out.println("DEBUG: Direct conversion result length: " + infoHashBytes.length);
+ }
+
+ // 验证字节长度
+ if (infoHashBytes.length != 20) {
+ System.err.println("WARNING: info_hash should be 20 bytes, but got " + infoHashBytes.length + " bytes");
+ }
+
+ return infoHashBytes;
+
+ } catch (Exception e) {
+ System.err.println("Error in processInfoHashParameter: " + e.getMessage());
+ return null;
+ }
+ }
+
+ /**
+ * 在候选 hash 列表中,找到与 targetHex 最长公共子序列(LCS)最大的那个
+ */
+ private String findBestMatchingInfoHash(String targetHex, List<String> candidates) {
+ String best = null;
+ int bestLen = -1;
+ for (String cand : candidates) {
+ int len = longestCommonSubseq(targetHex, cand);
+ if (len > bestLen) {
+ bestLen = len;
+ best = cand;
+ }
+ }
+ return best;
+ }
+
+ // 计算两字符串的 LCS 长度
+ private int longestCommonSubseq(String a, String b) {
+ int n = a.length(), m = b.length();
+ int[] dp = new int[m+1];
+ for (int i = 1; i <= n; i++) {
+ int prev = 0;
+ for (int j = 1; j <= m; j++) {
+ int temp = dp[j];
+ if (a.charAt(i-1) == b.charAt(j-1)) {
+ dp[j] = prev + 1;
+ } else {
+ dp[j] = Math.max(dp[j], dp[j-1]);
+ }
+ prev = temp;
+ }
+ }
+ return dp[m];
+ }
}
diff --git a/src/main/java/tracker/Tracker.java b/src/main/java/tracker/Tracker.java
index 11e0265..795d595 100644
--- a/src/main/java/tracker/Tracker.java
+++ b/src/main/java/tracker/Tracker.java
@@ -42,6 +42,7 @@
tx.begin();
try {
// 1) find the seedId by infoHash
+ System.out.println("DEBUG AddUpLoad: Looking for infoHash: '" + infoHash + "' (length: " + infoHash.length() + ")");
String seedId = em.createQuery(
"SELECT s.seedId FROM SeedHash s WHERE s.infoHash = :ih", String.class)
.setParameter("ih", infoHash)
@@ -98,9 +99,9 @@
).setParameter("uid", userid)
.getSingleResult();
- System.out.println("按回车继续...");
+ System.out.println("------------------------------------------------");
System.out.printf("thisadd:%d userptsofar:%d userptafter:%d totaluploadnow:%d delta:%d%n",upload, PTuploadbefor,PTuploadafter, totalUpload,delta);
- System.out.println("按回车继续...");
+ System.out.println("------------------------------------------------");
return false; // success
} catch (RuntimeException ex) {
if (tx.isActive()) tx.rollback();
@@ -151,6 +152,7 @@
EntityTransaction tx = em.getTransaction();
try {
// 1. 查 SeedHash
+ System.out.println("DEBUG AddDownload: Looking for infoHash: '" + infoHash + "' (length: " + infoHash.length() + ")");
TypedQuery<SeedHash> qsh = em.createQuery(
"SELECT s FROM SeedHash s WHERE s.infoHash = :h", SeedHash.class);
qsh.setParameter("h", infoHash);
@@ -556,4 +558,18 @@
return -1;
}
+
+ /**
+ * 从数据库中查询所有 info_hash(hex 字符串)
+ */
+ public List<String> getAllInfoHashes() {
+ EntityManager em = emf.createEntityManager();
+ try {
+ return em.createQuery(
+ "SELECT sh.infoHash FROM SeedHash sh", String.class
+ ).getResultList();
+ } finally {
+ em.close();
+ }
+ }
}
\ No newline at end of file
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index f0b6c8a..46b4298 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -1,4 +1,4 @@
-server.port=9999
+server.port=8083
# 文件上传配置
spring.servlet.multipart.max-file-size=100MB