修改Tracker服务器相关bug
Change-Id: Id3ec61034575bd794724be98f832f8c065448b92
diff --git a/src/main/java/com/pt/service/ResourceService.java b/src/main/java/com/pt/service/ResourceService.java
index 7d68817..4eb7823 100644
--- a/src/main/java/com/pt/service/ResourceService.java
+++ b/src/main/java/com/pt/service/ResourceService.java
@@ -6,11 +6,14 @@
import com.pt.repository.DownloadRepository;
import com.pt.repository.ResourceRepository;
import com.pt.repository.TorrentMetaRepository;
+import com.pt.service.TrackerService; // 新增导入
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
@Service
public class ResourceService {
@@ -27,6 +30,9 @@
@Autowired
private TorrentMetaRepository torrentMetaRepository;
+ @Autowired
+ private TrackerService trackerService; // 新增注入
+
public List<Resource> getAllResources() {
return resourceRepository.findAll();
}
@@ -35,7 +41,7 @@
return resourceRepository.findByAuthor(username);
}
- public void publishResource(String name, String description, String author, double size, byte[] torrentBytes) throws Exception {
+ public void publishResource(String name, String description, String author, byte[] torrentBytes, String username) throws Exception {
// 解析并保存torrent元信息
TorrentMeta meta = torrentService.parseAndSaveTorrent(torrentBytes);
@@ -44,13 +50,19 @@
resource.setName(name);
resource.setDescription(description);
resource.setAuthor(author);
- resource.setSize(size);
resource.setPublishTime(LocalDateTime.now());
resource.setTorrentData(torrentBytes);
- resource.setInfo_hash(meta.getInfoHash());
// 这里可以保存torrent文件路径,或直接存数据库,依据你的设计
resourceRepository.save(resource);
+
+ // 在Tracker中注册相关信息
+// Map<String, String[]> params = new HashMap<>();
+// params.put("info_hash", new String[]{meta.getInfoHash()});
+// // 这里peer_id和port可以先使用默认值,实际应用中可以根据需求修改
+// params.put("peer_id", new String[]{username});
+// params.put("port", new String[]{"6881"});
+// trackerService.handleAnnounce(params, ip);
}
// 获取资源时,返回BLOB字段内容作为torrent文件
@@ -89,4 +101,4 @@
return resourceRepository.findByNameContainingIgnoreCase(query);
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/com/pt/service/TorrentMetaService.java b/src/main/java/com/pt/service/TorrentMetaService.java
new file mode 100644
index 0000000..185c321
--- /dev/null
+++ b/src/main/java/com/pt/service/TorrentMetaService.java
@@ -0,0 +1,30 @@
+package com.pt.service;
+
+import com.pt.entity.TorrentMeta;
+import com.pt.repository.TorrentMetaRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalDateTime;
+
+@Service
+public class TorrentMetaService {
+
+ @Autowired
+ private TorrentMetaRepository torrentMetaRepository;
+
+ public void save(TorrentMeta torrentMeta) {
+ // 这里可以添加保存逻辑,比如调用repository的save方法
+ // torrentMetaRepository.save(torrentMeta);
+ System.out.println("Saving TorrentMeta: " + torrentMeta);
+ }
+
+ public void save(String infoHash, String name, long size) {
+ TorrentMeta torrentMeta = new TorrentMeta();
+ torrentMeta.setInfoHash(infoHash);
+ torrentMeta.setFilename(name);
+ torrentMeta.setSize(size);
+ torrentMeta.setUploadTime(LocalDateTime.now());
+ torrentMetaRepository.save(torrentMeta);
+ }
+}
diff --git a/src/main/java/com/pt/service/TorrentService.java b/src/main/java/com/pt/service/TorrentService.java
index 3c9d28d..e61e874 100644
--- a/src/main/java/com/pt/service/TorrentService.java
+++ b/src/main/java/com/pt/service/TorrentService.java
@@ -8,6 +8,7 @@
import java.security.MessageDigest;
import java.time.LocalDateTime;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -43,6 +44,9 @@
}
length = totalLength;
}
+ // 添加Tracker地址到种子文件中
+ torrentDict.put("announce", "http://your-tracker-url/api/tracker/announce");
+
// 保存到数据库
Optional<TorrentMeta> existing = torrentMetaRepository.findByInfoHash(infoHash);
@@ -55,7 +59,7 @@
meta.setInfoHash(infoHash);
meta.setSize(length);
meta.setUploadTime(LocalDateTime.now());
- meta.setTorrentData(torrentBytes);
+ meta.setTorrentData(BencodeCodec.encode(torrentDict)); // 使用更新后的字典编码
return torrentMetaRepository.save(meta);
}
@@ -70,4 +74,4 @@
}
return sb.toString();
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/com/pt/service/TorrentStatsService.java b/src/main/java/com/pt/service/TorrentStatsService.java
index baa63e3..40b98ad 100644
--- a/src/main/java/com/pt/service/TorrentStatsService.java
+++ b/src/main/java/com/pt/service/TorrentStatsService.java
@@ -81,9 +81,9 @@
int completed = 0;
for (PeerInfoEntity peer : peers) {
- if ("seeding".equals(peer.getStatus()) && peer.isActive()) {
+ if ("seeding".equals(peer.getStatus()) && peer.isActive() == 1) {
seeders++;
- } else if ("downloading".equals(peer.getStatus()) && peer.isActive()) {
+ } else if ("downloading".equals(peer.getStatus()) && peer.isActive() == 1) {
leechers++;
}
diff --git a/src/main/java/com/pt/service/TrackerService.java b/src/main/java/com/pt/service/TrackerService.java
index ea840e1..29ac1ad 100644
--- a/src/main/java/com/pt/service/TrackerService.java
+++ b/src/main/java/com/pt/service/TrackerService.java
@@ -1,27 +1,40 @@
package com.pt.service;
+import com.pt.constant.Constants;
+import com.pt.entity.PeerInfoEntity;
import com.pt.entity.TorrentMeta;
+import com.pt.entity.User;
+import com.pt.repository.PeerInfoRepository;
import com.pt.repository.TorrentMetaRepository;
+import com.pt.repository.UserRepository;
import com.pt.utils.BencodeCodec;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
-import java.io.UnsupportedEncodingException;
-import java.net.URLDecoder;
-import java.nio.charset.StandardCharsets;
+import java.io.ByteArrayOutputStream;
import java.util.*;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.CopyOnWriteArrayList;
@Service
public class TrackerService {
- private final Map<String, List<PeerInfo>> torrentPeers = new ConcurrentHashMap<>();
-
@Autowired
private TorrentMetaRepository torrentMetaRepository;
- public byte[] handleAnnounce(Map<String, String[]> params, String ipAddress) {
+ @Autowired
+ private PeerInfoRepository peerInfoEntityRepository;
+
+ @Autowired
+ private TorrentMetaService torrentMetaService;
+
+ @Autowired
+ private UserRepository userRepository;
+
+ @Transactional
+ public byte[] handleAnnounce(Map<String, Object> params, String ipAddress, String event) {
+
+ System.out.println("TrackerService------------------------handle announce");
+
try {
if (!params.containsKey("info_hash") || !params.containsKey("peer_id") || !params.containsKey("port")) {
return BencodeCodec.encode(Map.of("failure reason", "Missing required parameters"));
@@ -30,71 +43,300 @@
System.out.println("Received announce params: " + params);
System.out.println("Client IP: " + ipAddress);
- // 用ISO-8859-1解码,确保二进制数据正确还原
- String infoHash = decodeParam(params.get("info_hash")[0]);
- System.out.println("Decoded info_hash (raw bytes as hex): " + bytesToHex(infoHash.getBytes("ISO-8859-1")));
-
- Optional<TorrentMeta> meta = torrentMetaRepository.findByInfoHash(infoHash);
- if (!meta.isPresent()) {
- System.out.println("Invalid info_hash: not found in DB");
- return BencodeCodec.encode(Map.of("failure reason", "Invalid info_hash"));
+ String username = (String) params.get("passkey");
+ User user = null;
+ if(username != null){
+ user = userRepository.findByUsername(username);
+ if (user == null) {
+ System.out.println("User not found: " + username);
+ return BencodeCodec.encode(Map.of("failure reason", "User not found"));
+ }
}
- String peerId = decodeParam(params.get("peer_id")[0]);
- System.out.println("Decoded peer_id: " + peerId);
+ int compact = params.containsKey("compact") ? (int) params.get("compact") : 1;
- int port = Integer.parseInt(params.get("port")[0]);
- System.out.println("Port: " + port);
+ System.out.println("left" + (long)params.get("left"));
+ String infoHash = (String)params.get("info_hash");
- PeerInfo peer = new PeerInfo(ipAddress, port, peerId);
+ switch(event){
+ case "started":
+ String status = (long)params.get("left") == (long)0 ? "seeding" : "downloading"; // 默认状态为下载中
+ System.out.println("Event: started");
+ Optional<TorrentMeta> meta = torrentMetaRepository.findByInfoHash(infoHash);
- torrentPeers.computeIfAbsent(infoHash, k -> new CopyOnWriteArrayList<>());
- List<PeerInfo> peers = torrentPeers.get(infoHash);
+ if(status.equals("seeding")) {
+ if(meta.isEmpty()){
+ torrentMetaService.save(infoHash, "", 123456789L);
+ }
- boolean exists = peers.stream().anyMatch(p -> p.peerId.equals(peerId));
- if (!exists) {
- peers.add(peer);
- System.out.println("Added new peer: " + peerId);
- } else {
- System.out.println("Peer already exists: " + peerId);
+ String peerId = (String) params.get("peer_id");
+ System.out.println("Decoded peer_id: " + peerId);
+ int port = (int) params.get("port");
+ System.out.println("Port: " + port);
+
+ if(peerInfoEntityRepository.findByInfoHashAndPeerId(infoHash, (String) params.get("peer_id")).isEmpty()) {
+
+ PeerInfoEntity peer = new PeerInfoEntity(ipAddress, port, peerId);
+ peer.setInfoHash(infoHash);
+ peer.setStatus(status);
+ peer.setIsActive(1); // 默认活跃状态
+ peer.setUploaded(0);
+ peer.setDownloaded(0);
+ peer.setPeerId((String) params.get("peer_id"));
+ peerInfoEntityRepository.save(peer);
+
+ }
+ else {
+ System.out.println("Peer already exists for info_hash: " + infoHash);
+ peerInfoEntityRepository.findByInfoHashAndPeerId(infoHash, (String) params.get("peer_id")).get(0).setStatus("seeding");
+ }
+
+ // 返回成功响应
+ Map<String, Object> response = new HashMap<>();
+ response.put("interval", 1800); // 客户端应该多久再次请求
+ response.put("min interval", 900); // 最小请求间隔
+ response.put("complete", peerInfoEntityRepository.findByInfoHash(infoHash).size()); // 种子数
+ response.put("incomplete", peerInfoEntityRepository.findDownloadingPeersByInfoHash(infoHash).size()); // 下载者数
+
+ // 紧凑格式 - 返回当前种子的信息
+ if(compact == 1) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try {
+ // 转换IP地址
+ String[] ipParts = ipAddress.split("\\.");
+ for(String part : ipParts) {
+ baos.write(Integer.parseInt(part));
+ }
+ // 转换端口(大端序)
+ baos.write((port >> 8) & 0xFF);
+ baos.write(port & 0xFF);
+ } catch(Exception e) {
+ System.out.println("Error encoding peer info: " + e.getMessage());
+ }
+ response.put("peers", baos.toByteArray());
+ } else {
+ // 非紧凑格式
+ List<Map<String, Object>> peerList = new ArrayList<>();
+ Map<String, Object> peerMap = new HashMap<>();
+ peerMap.put("peer id", peerId);
+ peerMap.put("ip", ipAddress);
+ peerMap.put("port", port);
+ peerList.add(peerMap);
+ response.put("peers", peerList);
+ }
+
+ return BencodeCodec.encode(response);
+
+ }
+
+ if(status.equals("downloading")) {
+ System.out.println("Torrent is being downloaded, checking for existing peers...");
+
+ if(user.getDownloaded() > Constants.DOWNLOAD_EXEMPTION_BYTES){
+ if(user.getShareRatio() < Constants.MIN_SHARE_RATIO_THRESHOLD) {
+ return BencodeCodec.encode(Map.of("failure reason", "Share ratio too low"));
+ }
+ }
+
+ List<PeerInfoEntity> peers = peerInfoEntityRepository.findSeedingPeersByInfoHash(infoHash);
+
+ for(PeerInfoEntity peer : peers) {
+ System.out.println("Peer: " + peer.getPeerId() + ", IP: " + peer.getIp() + ", Port: " + peer.getPort());
+ }
+
+ String peerId = (String)params.get("peer_id");
+ System.out.println("Decoded peer_id: " + peerId);
+ int port = (int) params.get("port");
+ System.out.println("Port: " + port);
+ if(peerInfoEntityRepository.findByInfoHashAndPeerId(infoHash, (String) params.get("peer_id")).isEmpty()) {
+ PeerInfoEntity peer = new PeerInfoEntity(ipAddress, port, peerId);
+ peer.setInfoHash(infoHash);
+ peer.setStatus(status);
+ peer.setIsActive(1); // 默认活跃状态
+ peer.setUploaded(0);
+ peer.setDownloaded(0);
+ peer.setPeerId((String) params.get("peer_id"));
+ peer.setLeft((long)params.get("left"));
+ peerInfoEntityRepository.save(peer);
+ }
+ else {
+ System.out.println("Peer already exists for info_hash: " + infoHash);
+ peerInfoEntityRepository.findByInfoHashAndPeerId(infoHash, (String) params.get("peer_id")).get(0).setStatus("seeding");
+ }
+
+ if (peers.isEmpty()) {
+ return BencodeCodec.encode(Map.of("failure reason", "Torrent is not being seeded yet"));
+ }
+
+ System.out.println("solve download, compact = " + compact);
+
+ // 构建正确的响应
+ Map<String, Object> response = new HashMap<>();
+ // 添加基本信息
+ response.put("interval", 1800);
+ // 客户端应该多久再次请求
+ response.put("min interval", 900);
+ // 最小请求间隔
+ response.put("complete", peers.size());
+ // 种子数response.put("incomplete", 0);
+ // 下载者数(这里只返回种子)
+ // 构建peers列表
+ if (compact == 1) {
+ // 紧凑格式 - 每个peer用6字节表示 (4字节IP + 2字节端口)
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ for (PeerInfoEntity tmpPeer : peers) {
+ try {
+ // 转换IP地址
+ String[] ipParts = tmpPeer.getIp().split("\\.");
+ for (String part : ipParts) {
+ baos.write(Integer.parseInt(part));
+ }
+ // 转换端口(大端序)
+ int tmpPeerPort = tmpPeer.getPort();
+ baos.write((tmpPeerPort >> 8) & 0xFF);
+ baos.write(tmpPeerPort & 0xFF);
+ } catch (Exception e) {
+ System.out.println(e.getMessage());
+ }
+ }
+ response.put("peers", baos.toByteArray());
+ System.out.println(response);
+ } else {
+ // 非紧凑格式 - 每个peer是一个字典
+ List<Map<String, Object>> peerList = new ArrayList<>();
+ for (PeerInfoEntity tmpPeer : peers) {
+ Map<String, Object> peerMap = new HashMap<>();
+ peerMap.put("peer id", tmpPeer.getPeerId());
+ peerMap.put("ip", tmpPeer.getIp());
+ peerMap.put("port", tmpPeer.getPort());
+ peerList.add(peerMap);
+ }
+ response.put("peers", peerList);
+ }
+ System.out.println(BencodeCodec.encode(response));
+
+ return BencodeCodec.encode(response);
+ }
+ break;
+ case "stopped":
+ System.out.println("Event: stopped");
+
+ String peerId = (String) params.get("peer_id");
+ PeerInfoEntity peer = peerInfoEntityRepository.findByInfoHashAndPeerId(infoHash, peerId).get(0);
+ peerInfoEntityRepository.delete(peer); // 删除该peer信息
+
+ if(peerInfoEntityRepository.findByInfoHash(infoHash).isEmpty()) {
+ torrentMetaRepository.deleteByInfoHash(infoHash); // 如果没有其他peer,删除种子信息
+ }
+
+ // 停止事件,通常不需要返回数据
+ Map<String, Object> response = new HashMap<>();
+ // 添加基本信息
+ response.put("interval", 1800);
+ // 客户端应该多久再次请求
+ response.put("min interval", 900);
+ System.out.println("solve stop");
+
+ return BencodeCodec.encode(response);
+ case "completed":
+ System.out.println("Event: completed");
+
+ PeerInfoEntity complete_peer = peerInfoEntityRepository.findByInfoHashAndPeerId(infoHash, (String) params.get("peer_id")).get(0);
+
+ Map<String, Object> complete_response = new HashMap<>();
+ // 添加基本信息
+ complete_response.put("interval", 1800);
+ // 客户端应该多久再次请求
+ complete_response.put("min interval", 900);
+
+ complete_response.put("complete", peerInfoEntityRepository.findByInfoHash(infoHash).size()); // 种子数
+ complete_response.put("incomplete", peerInfoEntityRepository.findDownloadingPeersByInfoHash(infoHash).size()); // 下载者数
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ for (PeerInfoEntity tmpPeer : peerInfoEntityRepository.findSeedingPeersByInfoHash(infoHash)) {
+ try {
+ // 转换IP地址
+ String[] ipParts = tmpPeer.getIp().split("\\.");
+ for (String part : ipParts) {
+ baos.write(Integer.parseInt(part));
+ }
+ // 转换端口(大端序)
+ int tmpPeerPort = tmpPeer.getPort();
+ baos.write((tmpPeerPort >> 8) & 0xFF);
+ baos.write(tmpPeerPort & 0xFF);
+ } catch (Exception e) {
+ System.out.println(e.getMessage());
+ }
+ }
+ complete_response.put("peers", baos.toByteArray());
+ System.out.println("solve complete");
+ if(complete_peer.getStatus().equals("downloading")) {
+ user.setDownloaded(user.getDownloaded() + (long)params.get("downloaded"));
+ complete_peer.setStatus("seeding");
+ peerInfoEntityRepository.save(complete_peer);
+ }
+ if(complete_peer.getStatus().equals("seeding")) {
+ user.setUploaded(user.getUploaded() + (long)params.get("uploaded"));
+ }
+
+ user.setShareRatio(user.getUploaded() / (double) Math.max(user.getDownloaded(), 1));
+
+ userRepository.save(user); // 保存用户信息
+
+ return BencodeCodec.encode(complete_response);
+ default:
+ System.out.println(event);
+ System.out.println("Event: unknown or not specified");
+
+ PeerInfoEntity other_peer = peerInfoEntityRepository.findByInfoHashAndPeerId(infoHash, (String) params.get("peer_id")).get(0);
+
+ Map<String, Object> other_response = new HashMap<>();
+
+ // 添加基本信息
+ other_response.put("interval", 1800);
+ // 客户端应该多久再次请求
+ other_response.put("min interval", 900);
+
+ other_response.put("complete", peerInfoEntityRepository.findByInfoHash(infoHash).size()); // 种子数
+ other_response.put("incomplete", peerInfoEntityRepository.findDownloadingPeersByInfoHash(infoHash).size()); // 下载者数
+
+ ByteArrayOutputStream other_baos = new ByteArrayOutputStream();
+ for (PeerInfoEntity tmpPeer : peerInfoEntityRepository.findSeedingPeersByInfoHash(infoHash)) {
+ try {
+ // 转换IP地址
+ String[] ipParts = tmpPeer.getIp().split("\\.");
+ for (String part : ipParts) {
+ other_baos.write(Integer.parseInt(part));
+ }
+ // 转换端口(大端序)
+ int tmpPeerPort = tmpPeer.getPort();
+ other_baos.write((tmpPeerPort >> 8) & 0xFF);
+ other_baos.write(tmpPeerPort & 0xFF);
+ } catch (Exception e) {
+ System.out.println(e.getMessage());
+ }
+ }
+ other_response.put("peers", other_baos.toByteArray());
+ if(other_peer.getStatus().equals("downloading")) {
+ user.setDownloaded(user.getDownloaded() + (long)params.get("downloaded"));
+ }
+ if(other_peer.getStatus().equals("seeding")) {
+ user.setUploaded(user.getUploaded() + (long)params.get("uploaded"));
+ }
+
+ user.setShareRatio(user.getUploaded() / (double) Math.max(user.getDownloaded(), 1));
+
+ userRepository.save(user); // 保存用户信息
+ return BencodeCodec.encode(other_response);
}
- List<String> ips = peers.stream().map(p -> p.ip).toList();
- List<Integer> ports = peers.stream().map(p -> p.port).toList();
- byte[] peerBytes = BencodeCodec.buildCompactPeers(ips, ports);
-
- return BencodeCodec.buildTrackerResponse(1800, peerBytes);
+ return BencodeCodec.encode(Map.of("failure reason", "Event not handled"));
} catch (Exception e) {
e.printStackTrace(); // 打印异常堆栈,方便定位错误
return BencodeCodec.encode(Map.of("failure reason", "Internal server error"));
}
}
- // 辅助函数:打印字节转成16进制字符串
- private String bytesToHex(byte[] bytes) {
- StringBuilder sb = new StringBuilder();
- for (byte b : bytes) {
- sb.append(String.format("%02x", b));
- }
- return sb.toString();
- }
-
- // decodeParam 建议用ISO-8859-1解码
- private String decodeParam(String param) throws UnsupportedEncodingException {
- return URLDecoder.decode(param, "ISO-8859-1");
- }
- private static class PeerInfo {
- String ip;
- int port;
- String peerId;
-
- public PeerInfo(String ip, int port, String peerId) {
- this.ip = ip;
- this.port = port;
- this.peerId = peerId;
- }
- }
}