| 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.ByteArrayOutputStream; |
| import java.util.*; |
| |
| @Service |
| public class TrackerService { |
| |
| @Autowired |
| private TorrentMetaRepository torrentMetaRepository; |
| |
| @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")); |
| } |
| |
| System.out.println("Received announce params: " + params); |
| System.out.println("Client IP: " + ipAddress); |
| |
| 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")); |
| } |
| } |
| |
| int compact = params.containsKey("compact") ? (int) params.get("compact") : 1; |
| |
| System.out.println("left" + (long)params.get("left")); |
| String infoHash = (String)params.get("info_hash"); |
| |
| 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); |
| |
| if(status.equals("seeding")) { |
| if(meta.isEmpty()){ |
| torrentMetaService.save(infoHash, "", 123456789L); |
| } |
| |
| 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); |
| } |
| |
| 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")); |
| } |
| } |
| |
| |
| |
| } |
| |