添加bt与tracker交互
Change-Id: I1327c2ed89a76bee8e2abeb8cb3014b56357689a
diff --git a/src/main/java/com/pt/service/ResourceService.java b/src/main/java/com/pt/service/ResourceService.java
index 1d0e797..d85315e 100644
--- a/src/main/java/com/pt/service/ResourceService.java
+++ b/src/main/java/com/pt/service/ResourceService.java
@@ -6,16 +6,11 @@
import com.pt.repository.DownloadRepository;
import com.pt.repository.ResourceRepository;
import com.pt.repository.TorrentMetaRepository;
-import com.pt.utils.BencodeUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.Files;
import java.time.LocalDateTime;
import java.util.List;
-import java.util.Map;
@Service
public class ResourceService {
@@ -40,9 +35,11 @@
return resourceRepository.findByAuthor(username);
}
- // 发布资源时,将种子文件二进制数据保存到数据库
- public void publishResource(String name, String description, String author, double size) throws Exception {
- // 保存资源信息到数据库,包括种子文件的二进制数据
+ public void publishResource(String name, String description, String author, double size, byte[] torrentBytes) throws Exception {
+ // 解析并保存torrent元信息
+ TorrentMeta meta = torrentService.parseAndSaveTorrent(torrentBytes);
+
+ // 保存资源信息,并关联torrent信息
Resource resource = new Resource();
resource.setName(name);
resource.setDescription(description);
@@ -50,10 +47,8 @@
resource.setSize(size);
resource.setPublishTime(LocalDateTime.now());
- // 生成种子数据 byte[]
- byte[] torrentData = torrentService.generateTorrentBytes("uploads/" + name);
- resource.setTorrentData(torrentData);
-
+ resource.setTorrentData(torrentBytes);
+ // 这里可以保存torrent文件路径,或直接存数据库,依据你的设计
resourceRepository.save(resource);
}
diff --git a/src/main/java/com/pt/service/TorrentService.java b/src/main/java/com/pt/service/TorrentService.java
index dcbee69..1fdb700 100644
--- a/src/main/java/com/pt/service/TorrentService.java
+++ b/src/main/java/com/pt/service/TorrentService.java
@@ -2,77 +2,62 @@
import com.pt.entity.TorrentMeta;
import com.pt.repository.TorrentMetaRepository;
-import com.pt.utils.BencodeUtils;
+import com.pt.utils.BencodeCodec;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
-import java.io.*;
import java.security.MessageDigest;
import java.time.LocalDateTime;
-import java.util.LinkedHashMap;
+import java.util.List;
import java.util.Map;
@Service
public class TorrentService {
- // Tracker 服务器的 announce 地址
- private static final String ANNOUNCE_URL = "http://localhost:8080/announce";
- private static final int PIECE_LENGTH = 256 * 1024;
-
@Autowired
private TorrentMetaRepository torrentMetaRepository;
- public byte[] generateTorrentBytes(String sourceFilePath) throws Exception {
- File sourceFile = new File(sourceFilePath);
-
- // 构造 info 字典
- Map<String, Object> infoDict = new LinkedHashMap<>();
- infoDict.put("name", sourceFile.getName());
- infoDict.put("length", sourceFile.length());
- infoDict.put("piece length", PIECE_LENGTH);
- infoDict.put("pieces", calcPiecesHashes(sourceFile));
-
- // 构造完整 torrent 字典
- Map<String, Object> torrentDict = new LinkedHashMap<>();
- torrentDict.put("announce", ANNOUNCE_URL);
- torrentDict.put("info", infoDict);
-
- // 编码成种子数据字节数组
- byte[] bencodedTorrent = BencodeUtils.encode(torrentDict);
-
- // 计算 info_hash 并保存到数据库(如果需要的话)
- MessageDigest sha1 = MessageDigest.getInstance("SHA-1");
- byte[] infoEncoded = BencodeUtils.encode(infoDict);
- sha1.update(infoEncoded);
- String infoHash = bytesToHex(sha1.digest());
-
- TorrentMeta meta = new TorrentMeta();
- meta.setFilename(sourceFile.getName());
- meta.setInfoHash(infoHash);
- meta.setSize(sourceFile.length());
- meta.setUploadTime(LocalDateTime.now());
- torrentMetaRepository.save(meta);
-
- return bencodedTorrent;
- }
-
-
- private byte[] calcPiecesHashes(File file) throws Exception {
- MessageDigest sha1 = MessageDigest.getInstance("SHA-1");
- try (InputStream fis = new FileInputStream(file)) {
- byte[] buffer = new byte[PIECE_LENGTH];
- ByteArrayOutputStream piecesBuffer = new ByteArrayOutputStream();
-
- int read;
- while ((read = fis.read(buffer)) > 0) {
- sha1.reset();
- sha1.update(buffer, 0, read);
- piecesBuffer.write(sha1.digest());
- }
- return piecesBuffer.toByteArray();
+ public TorrentMeta parseAndSaveTorrent(byte[] torrentBytes) throws Exception {
+ Map<String, Object> torrentDict = (Map<String, Object>) BencodeCodec.decode(torrentBytes);
+ if (!torrentDict.containsKey("info")) {
+ throw new IllegalArgumentException("Invalid torrent file: missing 'info' dictionary");
}
+
+ Map<String, Object> infoDict = (Map<String, Object>) torrentDict.get("info");
+
+ // 计算 info_hash
+ byte[] infoEncoded = BencodeCodec.encode(infoDict);
+ MessageDigest sha1 = MessageDigest.getInstance("SHA-1");
+ byte[] infoHashBytes = sha1.digest(infoEncoded);
+ String infoHash = bytesToHex(infoHashBytes);
+
+ // 获取文件名,大小等
+ String name = (String) infoDict.get("name");
+ long length = 0;
+ if (infoDict.containsKey("length")) {
+ length = ((Number) infoDict.get("length")).longValue();
+ } else if (infoDict.containsKey("files")) {
+ long totalLength = 0;
+ List<Map<String, Object>> files = (List<Map<String, Object>>) infoDict.get("files");
+ for (Map<String, Object> file : files) {
+ totalLength += ((Number) file.get("length")).longValue();
+ }
+ length = totalLength;
+ }
+
+ // 保存到数据库
+ TorrentMeta meta = new TorrentMeta();
+ meta.setFilename(name);
+ meta.setInfoHash(infoHash);
+ meta.setSize(length);
+ meta.setUploadTime(LocalDateTime.now());
+ meta.setTorrentData(torrentBytes);
+
+ torrentMetaRepository.save(meta);
+ return meta;
}
+
private String bytesToHex(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
@@ -80,4 +65,4 @@
}
return sb.toString();
}
-}
\ No newline at end of file
+}
diff --git a/src/main/java/com/pt/service/TrackerService.java b/src/main/java/com/pt/service/TrackerService.java
new file mode 100644
index 0000000..7167ff3
--- /dev/null
+++ b/src/main/java/com/pt/service/TrackerService.java
@@ -0,0 +1,76 @@
+package com.pt.service;
+
+import com.pt.entity.TorrentMeta;
+import com.pt.repository.TorrentMetaRepository;
+import com.pt.utils.BencodeCodec;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+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) {
+ try {
+ if (!params.containsKey("info_hash") || !params.containsKey("peer_id") || !params.containsKey("port")) {
+ return BencodeCodec.encode(Map.of("failure reason", "Missing required parameters"));
+ }
+
+ String infoHash = decodeParam(params.get("info_hash")[0]);
+ TorrentMeta meta = torrentMetaRepository.findByInfoHash(infoHash);
+ if (meta == null) {
+ return BencodeCodec.encode(Map.of("failure reason", "Invalid info_hash"));
+ }
+
+ String peerId = decodeParam(params.get("peer_id")[0]);
+ int port = Integer.parseInt(params.get("port")[0]);
+
+ PeerInfo peer = new PeerInfo(ipAddress, port, peerId);
+
+ torrentPeers.computeIfAbsent(infoHash, k -> new CopyOnWriteArrayList<>());
+ List<PeerInfo> peers = torrentPeers.get(infoHash);
+
+ boolean exists = peers.stream().anyMatch(p -> p.peerId.equals(peerId));
+ if (!exists) {
+ peers.add(peer);
+ }
+
+ 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);
+ } catch (Exception e) {
+ return BencodeCodec.encode(Map.of("failure reason", "Internal server error"));
+ }
+ }
+
+ private String decodeParam(String raw) {
+ return new String(raw.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
+ }
+
+ 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;
+ }
+ }
+}
+