blob: 29ac1ad5de3548040dc8d9a1295025feaca14570 [file] [log] [blame]
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +08001package com.pt.service;
2
Edwardsamaxlcba512d2025-06-09 21:17:29 +08003import com.pt.constant.Constants;
4import com.pt.entity.PeerInfoEntity;
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +08005import com.pt.entity.TorrentMeta;
Edwardsamaxlcba512d2025-06-09 21:17:29 +08006import com.pt.entity.User;
7import com.pt.repository.PeerInfoRepository;
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +08008import com.pt.repository.TorrentMetaRepository;
Edwardsamaxlcba512d2025-06-09 21:17:29 +08009import com.pt.repository.UserRepository;
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +080010import com.pt.utils.BencodeCodec;
11import org.springframework.beans.factory.annotation.Autowired;
12import org.springframework.stereotype.Service;
Edwardsamaxlcba512d2025-06-09 21:17:29 +080013import org.springframework.transaction.annotation.Transactional;
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +080014
Edwardsamaxlcba512d2025-06-09 21:17:29 +080015import java.io.ByteArrayOutputStream;
22301102ca0fb2f2025-06-09 18:40:42 +080016import java.util.*;
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +080017
18@Service
19public class TrackerService {
20
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +080021 @Autowired
22 private TorrentMetaRepository torrentMetaRepository;
23
Edwardsamaxlcba512d2025-06-09 21:17:29 +080024 @Autowired
25 private PeerInfoRepository peerInfoEntityRepository;
26
27 @Autowired
28 private TorrentMetaService torrentMetaService;
29
30 @Autowired
31 private UserRepository userRepository;
32
33 @Transactional
34 public byte[] handleAnnounce(Map<String, Object> params, String ipAddress, String event) {
35
36 System.out.println("TrackerService------------------------handle announce");
37
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +080038 try {
22301102ca0fb2f2025-06-09 18:40:42 +080039 if (!params.containsKey("info_hash") || !params.containsKey("peer_id") || !params.containsKey("port")) {
40 return BencodeCodec.encode(Map.of("failure reason", "Missing required parameters"));
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +080041 }
42
22301102ca0fb2f2025-06-09 18:40:42 +080043 System.out.println("Received announce params: " + params);
44 System.out.println("Client IP: " + ipAddress);
45
Edwardsamaxlcba512d2025-06-09 21:17:29 +080046 String username = (String) params.get("passkey");
47 User user = null;
48 if(username != null){
49 user = userRepository.findByUsername(username);
50 if (user == null) {
51 System.out.println("User not found: " + username);
52 return BencodeCodec.encode(Map.of("failure reason", "User not found"));
53 }
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +080054 }
55
Edwardsamaxlcba512d2025-06-09 21:17:29 +080056 int compact = params.containsKey("compact") ? (int) params.get("compact") : 1;
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +080057
Edwardsamaxlcba512d2025-06-09 21:17:29 +080058 System.out.println("left" + (long)params.get("left"));
59 String infoHash = (String)params.get("info_hash");
ystxdfd42b32025-06-07 13:26:52 +080060
Edwardsamaxlcba512d2025-06-09 21:17:29 +080061 switch(event){
62 case "started":
63 String status = (long)params.get("left") == (long)0 ? "seeding" : "downloading"; // 默认状态为下载中
64 System.out.println("Event: started");
65 Optional<TorrentMeta> meta = torrentMetaRepository.findByInfoHash(infoHash);
ystxdfd42b32025-06-07 13:26:52 +080066
Edwardsamaxlcba512d2025-06-09 21:17:29 +080067 if(status.equals("seeding")) {
68 if(meta.isEmpty()){
69 torrentMetaService.save(infoHash, "", 123456789L);
70 }
ystxdfd42b32025-06-07 13:26:52 +080071
Edwardsamaxlcba512d2025-06-09 21:17:29 +080072 String peerId = (String) params.get("peer_id");
73 System.out.println("Decoded peer_id: " + peerId);
74 int port = (int) params.get("port");
75 System.out.println("Port: " + port);
76
77 if(peerInfoEntityRepository.findByInfoHashAndPeerId(infoHash, (String) params.get("peer_id")).isEmpty()) {
78
79 PeerInfoEntity peer = new PeerInfoEntity(ipAddress, port, peerId);
80 peer.setInfoHash(infoHash);
81 peer.setStatus(status);
82 peer.setIsActive(1); // 默认活跃状态
83 peer.setUploaded(0);
84 peer.setDownloaded(0);
85 peer.setPeerId((String) params.get("peer_id"));
86 peerInfoEntityRepository.save(peer);
87
88 }
89 else {
90 System.out.println("Peer already exists for info_hash: " + infoHash);
91 peerInfoEntityRepository.findByInfoHashAndPeerId(infoHash, (String) params.get("peer_id")).get(0).setStatus("seeding");
92 }
93
94 // 返回成功响应
95 Map<String, Object> response = new HashMap<>();
96 response.put("interval", 1800); // 客户端应该多久再次请求
97 response.put("min interval", 900); // 最小请求间隔
98 response.put("complete", peerInfoEntityRepository.findByInfoHash(infoHash).size()); // 种子数
99 response.put("incomplete", peerInfoEntityRepository.findDownloadingPeersByInfoHash(infoHash).size()); // 下载者数
100
101 // 紧凑格式 - 返回当前种子的信息
102 if(compact == 1) {
103 ByteArrayOutputStream baos = new ByteArrayOutputStream();
104 try {
105 // 转换IP地址
106 String[] ipParts = ipAddress.split("\\.");
107 for(String part : ipParts) {
108 baos.write(Integer.parseInt(part));
109 }
110 // 转换端口(大端序)
111 baos.write((port >> 8) & 0xFF);
112 baos.write(port & 0xFF);
113 } catch(Exception e) {
114 System.out.println("Error encoding peer info: " + e.getMessage());
115 }
116 response.put("peers", baos.toByteArray());
117 } else {
118 // 非紧凑格式
119 List<Map<String, Object>> peerList = new ArrayList<>();
120 Map<String, Object> peerMap = new HashMap<>();
121 peerMap.put("peer id", peerId);
122 peerMap.put("ip", ipAddress);
123 peerMap.put("port", port);
124 peerList.add(peerMap);
125 response.put("peers", peerList);
126 }
127
128 return BencodeCodec.encode(response);
129
130 }
131
132 if(status.equals("downloading")) {
133 System.out.println("Torrent is being downloaded, checking for existing peers...");
134
135 if(user.getDownloaded() > Constants.DOWNLOAD_EXEMPTION_BYTES){
136 if(user.getShareRatio() < Constants.MIN_SHARE_RATIO_THRESHOLD) {
137 return BencodeCodec.encode(Map.of("failure reason", "Share ratio too low"));
138 }
139 }
140
141 List<PeerInfoEntity> peers = peerInfoEntityRepository.findSeedingPeersByInfoHash(infoHash);
142
143 for(PeerInfoEntity peer : peers) {
144 System.out.println("Peer: " + peer.getPeerId() + ", IP: " + peer.getIp() + ", Port: " + peer.getPort());
145 }
146
147 String peerId = (String)params.get("peer_id");
148 System.out.println("Decoded peer_id: " + peerId);
149 int port = (int) params.get("port");
150 System.out.println("Port: " + port);
151 if(peerInfoEntityRepository.findByInfoHashAndPeerId(infoHash, (String) params.get("peer_id")).isEmpty()) {
152 PeerInfoEntity peer = new PeerInfoEntity(ipAddress, port, peerId);
153 peer.setInfoHash(infoHash);
154 peer.setStatus(status);
155 peer.setIsActive(1); // 默认活跃状态
156 peer.setUploaded(0);
157 peer.setDownloaded(0);
158 peer.setPeerId((String) params.get("peer_id"));
159 peer.setLeft((long)params.get("left"));
160 peerInfoEntityRepository.save(peer);
161 }
162 else {
163 System.out.println("Peer already exists for info_hash: " + infoHash);
164 peerInfoEntityRepository.findByInfoHashAndPeerId(infoHash, (String) params.get("peer_id")).get(0).setStatus("seeding");
165 }
166
167 if (peers.isEmpty()) {
168 return BencodeCodec.encode(Map.of("failure reason", "Torrent is not being seeded yet"));
169 }
170
171 System.out.println("solve download, compact = " + compact);
172
173 // 构建正确的响应
174 Map<String, Object> response = new HashMap<>();
175 // 添加基本信息
176 response.put("interval", 1800);
177 // 客户端应该多久再次请求
178 response.put("min interval", 900);
179 // 最小请求间隔
180 response.put("complete", peers.size());
181 // 种子数response.put("incomplete", 0);
182 // 下载者数(这里只返回种子)
183 // 构建peers列表
184 if (compact == 1) {
185 // 紧凑格式 - 每个peer用6字节表示 (4字节IP + 2字节端口)
186 ByteArrayOutputStream baos = new ByteArrayOutputStream();
187 for (PeerInfoEntity tmpPeer : peers) {
188 try {
189 // 转换IP地址
190 String[] ipParts = tmpPeer.getIp().split("\\.");
191 for (String part : ipParts) {
192 baos.write(Integer.parseInt(part));
193 }
194 // 转换端口(大端序)
195 int tmpPeerPort = tmpPeer.getPort();
196 baos.write((tmpPeerPort >> 8) & 0xFF);
197 baos.write(tmpPeerPort & 0xFF);
198 } catch (Exception e) {
199 System.out.println(e.getMessage());
200 }
201 }
202 response.put("peers", baos.toByteArray());
203 System.out.println(response);
204 } else {
205 // 非紧凑格式 - 每个peer是一个字典
206 List<Map<String, Object>> peerList = new ArrayList<>();
207 for (PeerInfoEntity tmpPeer : peers) {
208 Map<String, Object> peerMap = new HashMap<>();
209 peerMap.put("peer id", tmpPeer.getPeerId());
210 peerMap.put("ip", tmpPeer.getIp());
211 peerMap.put("port", tmpPeer.getPort());
212 peerList.add(peerMap);
213 }
214 response.put("peers", peerList);
215 }
216 System.out.println(BencodeCodec.encode(response));
217
218 return BencodeCodec.encode(response);
219 }
220 break;
221 case "stopped":
222 System.out.println("Event: stopped");
223
224 String peerId = (String) params.get("peer_id");
225 PeerInfoEntity peer = peerInfoEntityRepository.findByInfoHashAndPeerId(infoHash, peerId).get(0);
226 peerInfoEntityRepository.delete(peer); // 删除该peer信息
227
228 if(peerInfoEntityRepository.findByInfoHash(infoHash).isEmpty()) {
229 torrentMetaRepository.deleteByInfoHash(infoHash); // 如果没有其他peer,删除种子信息
230 }
231
232 // 停止事件,通常不需要返回数据
233 Map<String, Object> response = new HashMap<>();
234 // 添加基本信息
235 response.put("interval", 1800);
236 // 客户端应该多久再次请求
237 response.put("min interval", 900);
238 System.out.println("solve stop");
239
240 return BencodeCodec.encode(response);
241 case "completed":
242 System.out.println("Event: completed");
243
244 PeerInfoEntity complete_peer = peerInfoEntityRepository.findByInfoHashAndPeerId(infoHash, (String) params.get("peer_id")).get(0);
245
246 Map<String, Object> complete_response = new HashMap<>();
247 // 添加基本信息
248 complete_response.put("interval", 1800);
249 // 客户端应该多久再次请求
250 complete_response.put("min interval", 900);
251
252 complete_response.put("complete", peerInfoEntityRepository.findByInfoHash(infoHash).size()); // 种子数
253 complete_response.put("incomplete", peerInfoEntityRepository.findDownloadingPeersByInfoHash(infoHash).size()); // 下载者数
254 ByteArrayOutputStream baos = new ByteArrayOutputStream();
255 for (PeerInfoEntity tmpPeer : peerInfoEntityRepository.findSeedingPeersByInfoHash(infoHash)) {
256 try {
257 // 转换IP地址
258 String[] ipParts = tmpPeer.getIp().split("\\.");
259 for (String part : ipParts) {
260 baos.write(Integer.parseInt(part));
261 }
262 // 转换端口(大端序)
263 int tmpPeerPort = tmpPeer.getPort();
264 baos.write((tmpPeerPort >> 8) & 0xFF);
265 baos.write(tmpPeerPort & 0xFF);
266 } catch (Exception e) {
267 System.out.println(e.getMessage());
268 }
269 }
270 complete_response.put("peers", baos.toByteArray());
271 System.out.println("solve complete");
272 if(complete_peer.getStatus().equals("downloading")) {
273 user.setDownloaded(user.getDownloaded() + (long)params.get("downloaded"));
274 complete_peer.setStatus("seeding");
275 peerInfoEntityRepository.save(complete_peer);
276 }
277 if(complete_peer.getStatus().equals("seeding")) {
278 user.setUploaded(user.getUploaded() + (long)params.get("uploaded"));
279 }
280
281 user.setShareRatio(user.getUploaded() / (double) Math.max(user.getDownloaded(), 1));
282
283 userRepository.save(user); // 保存用户信息
284
285 return BencodeCodec.encode(complete_response);
286 default:
287 System.out.println(event);
288 System.out.println("Event: unknown or not specified");
289
290 PeerInfoEntity other_peer = peerInfoEntityRepository.findByInfoHashAndPeerId(infoHash, (String) params.get("peer_id")).get(0);
291
292 Map<String, Object> other_response = new HashMap<>();
293
294 // 添加基本信息
295 other_response.put("interval", 1800);
296 // 客户端应该多久再次请求
297 other_response.put("min interval", 900);
298
299 other_response.put("complete", peerInfoEntityRepository.findByInfoHash(infoHash).size()); // 种子数
300 other_response.put("incomplete", peerInfoEntityRepository.findDownloadingPeersByInfoHash(infoHash).size()); // 下载者数
301
302 ByteArrayOutputStream other_baos = new ByteArrayOutputStream();
303 for (PeerInfoEntity tmpPeer : peerInfoEntityRepository.findSeedingPeersByInfoHash(infoHash)) {
304 try {
305 // 转换IP地址
306 String[] ipParts = tmpPeer.getIp().split("\\.");
307 for (String part : ipParts) {
308 other_baos.write(Integer.parseInt(part));
309 }
310 // 转换端口(大端序)
311 int tmpPeerPort = tmpPeer.getPort();
312 other_baos.write((tmpPeerPort >> 8) & 0xFF);
313 other_baos.write(tmpPeerPort & 0xFF);
314 } catch (Exception e) {
315 System.out.println(e.getMessage());
316 }
317 }
318 other_response.put("peers", other_baos.toByteArray());
319 if(other_peer.getStatus().equals("downloading")) {
320 user.setDownloaded(user.getDownloaded() + (long)params.get("downloaded"));
321 }
322 if(other_peer.getStatus().equals("seeding")) {
323 user.setUploaded(user.getUploaded() + (long)params.get("uploaded"));
324 }
325
326 user.setShareRatio(user.getUploaded() / (double) Math.max(user.getDownloaded(), 1));
327
328 userRepository.save(user); // 保存用户信息
329 return BencodeCodec.encode(other_response);
22301102ca0fb2f2025-06-09 18:40:42 +0800330 }
ystxdfd42b32025-06-07 13:26:52 +0800331
Edwardsamaxlcba512d2025-06-09 21:17:29 +0800332 return BencodeCodec.encode(Map.of("failure reason", "Event not handled"));
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +0800333 } catch (Exception e) {
22301102ca0fb2f2025-06-09 18:40:42 +0800334 e.printStackTrace(); // 打印异常堆栈,方便定位错误
335 return BencodeCodec.encode(Map.of("failure reason", "Internal server error"));
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +0800336 }
337 }
338
ystxdfd42b32025-06-07 13:26:52 +0800339
ystxdfd42b32025-06-07 13:26:52 +0800340
22301102ca0fb2f2025-06-09 18:40:42 +0800341}
ystxdfd42b32025-06-07 13:26:52 +0800342