三种作弊检测方法:1,上传下载速度异常检测,2,异常客户端检测,3,上传下载量一致检测

Change-Id: I9949b20c4982ef1a5c8974f9f58928462759b5a6
diff --git a/src/main/java/com/pt5/pthouduan/entity/TrackeredTorrentWithStats.java b/src/main/java/com/pt5/pthouduan/entity/TrackeredTorrentWithStats.java
index 6f015d2..21cc304 100644
--- a/src/main/java/com/pt5/pthouduan/entity/TrackeredTorrentWithStats.java
+++ b/src/main/java/com/pt5/pthouduan/entity/TrackeredTorrentWithStats.java
@@ -49,6 +49,20 @@
         long deltaDownloaded = Math.max(0,downloaded - last[1]);
         lastStats.put(peerUID,new long[]{uploaded,downloaded});
         //updatePeerStatsInDB(this.getHexInfoHash(),ip,port,hexPeerId,uploaded,downloaded,deltaUpload,deltaDownloaded,passkey);
+        //检测异常流量
+        final long ABNORMAL_THRESHOLD = 1073741824L; // 1GB 阈值
+        if (deltaUpload > ABNORMAL_THRESHOLD || deltaDownloaded > ABNORMAL_THRESHOLD) {
+            // 调用服务层记录异常
+            statsService.recordTrafficAnomaly(
+                    this.getHexInfoHash(),  // info_hash
+                    ip,                     // peer IP
+                    port,                   // peer 端口
+                    deltaUpload,            // 上传增量
+                    deltaDownloaded         // 下载增量
+            );
+        }
+
+
         try {
             // 调用服务层更新统计
             statsService.updatePeerStatsInDB(
diff --git a/src/main/java/com/pt5/pthouduan/service/impl/updatePeerStatsService.java b/src/main/java/com/pt5/pthouduan/service/impl/updatePeerStatsService.java
index bdcdc65..9548da6 100644
--- a/src/main/java/com/pt5/pthouduan/service/impl/updatePeerStatsService.java
+++ b/src/main/java/com/pt5/pthouduan/service/impl/updatePeerStatsService.java
@@ -9,6 +9,7 @@
 import java.nio.charset.StandardCharsets;
 import java.sql.Connection;
 import java.sql.PreparedStatement;
+import java.sql.ResultSet;
 import java.sql.SQLException;
 
 import static com.pt5.pthouduan.util.TorrentUtils.hexStringToByteArray;
@@ -33,6 +34,11 @@
         int intervalSeconds = 100;
         String username = userMapper.getUsernameByPasskey(passkey); // ← 获取用户名
         String client = parseClientFromPeerId(peerId);              // ← 解析客户端
+        // 记录可疑客户端
+        if (client.startsWith("Suspicious_") || client.startsWith("Blacklisted_")||client.startsWith("Other")) {
+            recordSuspiciousClient(infoHash, ip, port, peerId, client);
+        }
+
         System.out.println("剩下的:" + left);
         boolean isCompleted = left == 0;
         double uploadSpeed = intervalSeconds > 0 ? (double) deltaUpload / intervalSeconds : 0.0;
@@ -134,8 +140,97 @@
         if (decodedPeerId.startsWith("-AZ")) return "Azureus";
         if (decodedPeerId.startsWith("-LT")) return "libtorrent";
         if (decodedPeerId.startsWith("-qB")) return "qBittorrent";
+        if (decodedPeerId.startsWith("-DE")) return "Deluge";      // 新增:Deluge客户端
+        if (decodedPeerId.startsWith("-BT")) return "BitTorrent";  // 新增:BitTorrent客户端
+
+        // 黑名单客户端识别
+        if (decodedPeerId.startsWith("-FAKE")) return "Blacklisted_FakeClient";  // 伪造客户端
+        if (decodedPeerId.startsWith("-HACK")) return "Blacklisted_HackedClient"; // 破解客户端
+        // 未识别的非常见客户端(长度不足或无匹配前缀)
+        if (decodedPeerId.length() < 6) return "Suspicious_TooShort";  // 长度异常
+
+
+
 
         return "Other";
     }
+    //记录流量异常
+    public void recordTrafficAnomaly(String infoHash, String ip, int port, long uploadDelta, long downloadDelta) {
+        String sql = """
+            INSERT INTO traffic_anomaly (info_hash, ip, port, upload_delta, download_delta)
+            VALUES (?, ?, ?, ?, ?)
+        """;
+        try (Connection conn = dataSource.getConnection();
+             PreparedStatement stmt = conn.prepareStatement(sql)) {
+            stmt.setString(1, infoHash);
+            stmt.setString(2, ip);
+            stmt.setInt(3, port);
+            stmt.setLong(4, uploadDelta);
+            stmt.setLong(5, downloadDelta);
+            stmt.executeUpdate();
+        } catch (SQLException e) {
+            //logger.error("记录流量异常失败,info_hash={}, ip={}:{}", infoHash, ip, port, e);
+        }
+    }
+    //记录可疑客户端
+    public void recordSuspiciousClient(String infoHash, String ip, int port, String peerId, String clientType) {
+        String sql = """
+            INSERT INTO client_anomaly (info_hash, ip, port, peer_id, client_type)
+            VALUES (?, ?, ?, ?, ?)
+        """;
+        try (Connection conn = dataSource.getConnection();
+             PreparedStatement stmt = conn.prepareStatement(sql)) {
+            stmt.setString(1, infoHash);
+            stmt.setString(2, ip);
+            stmt.setInt(3, port);
+            stmt.setString(4, peerId);
+            stmt.setString(5, clientType);
+            stmt.executeUpdate();
+        } catch (SQLException e) {
+            //logger.error("记录可疑客户端失败,info_hash={}, ip={}:{}", infoHash, ip, port, e);
+        }
+    }
+    // 验证指定种子的上传总量与下载总量是否一致
+    public void verifyFileTrafficConsistency(String infoHash) {
+        String querySql = """
+            SELECT 
+                SUM(uploaded) AS total_upload, 
+                SUM(downloaded) AS total_download,
+                MIN(created_at) AS first_seen_time  -- 获取种子首次出现时间
+            FROM peer_stats 
+            WHERE info_hash = ?
+        """;
+        String insertAnomalySql = "INSERT INTO file_traffic_anomaly (info_hash, total_upload, total_download, create_time) VALUES (?, ?, ?, NOW())";
+
+        try (Connection conn = dataSource.getConnection();
+             PreparedStatement queryStmt = conn.prepareStatement(querySql);
+             PreparedStatement anomalyStmt = conn.prepareStatement(insertAnomalySql)) {
+
+            queryStmt.setString(1, infoHash);
+            ResultSet rs = queryStmt.executeQuery();
+            if (rs.next()) {
+                long totalUpload = rs.getLong("total_upload");
+                long totalDownload = rs.getLong("total_download");
+                java.sql.Timestamp firstSeen = rs.getTimestamp("first_seen_time");
+
+                // 忽略种子创建后1小时内的不平衡
+                if (firstSeen != null && System.currentTimeMillis() - firstSeen.getTime() < 3600_000) {
+                    return;
+                }
+                // 上传总量应等于下载总量(允许±1B误差避免计算精度问题)
+                if (Math.abs(totalUpload - totalDownload) > 1) {
+                    // logger.warn("检测到异常种子,info_hash={},总上传={},总下载={}", infoHash, totalUpload, totalDownload);
+
+                    // 记录异常到数据库
+                    anomalyStmt.setString(1, infoHash);
+                    anomalyStmt.setLong(2, totalUpload);
+                    anomalyStmt.setLong(3, totalDownload);
+                    anomalyStmt.executeUpdate();
+                }
+            }
+        } catch (SQLException e) {
+            //logger.error("验证种子流量一致性失败,info_hash={}", infoHash, e);
+        }
+    }
 }