All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.acgist.snail.net.torrent.peer.bootstrap.PeerManager Maven / Gradle / Ivy

package com.acgist.snail.net.torrent.peer.bootstrap;

import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.acgist.snail.net.torrent.peer.bootstrap.ltep.PeerExchangeMessageHandler;
import com.acgist.snail.pojo.session.PeerSession;
import com.acgist.snail.pojo.session.StatisticsSession;
import com.acgist.snail.system.config.PeerConfig;
import com.acgist.snail.system.evaluation.PeerEvaluator;
import com.acgist.snail.utils.CollectionUtils;

/**
 * 

Peer管理器

*

Peer加入放入两个队列,一个队列负责下载时使用:{@link #peers},一个负责存档:{@link #storagePeers}。

* TODO:将IP分段,然后每次下载有连接分析时进行评分,插入IP时按照分段插入。 * * @author acgist * @since 1.0.0 */ public class PeerManager { private static final Logger LOGGER = LoggerFactory.getLogger(PeerManager.class); private static final PeerManager INSTANCE = new PeerManager(); /** *

使用的Peer,下载时Peer从中间剔除,选为劣质Peer时放回到列表中。

*

key=infoHashHex

*

value=Peers:双端队列,新加入插入队尾,剔除的Peer插入对头。

*/ private final Map> peers; /** * Peer存档队列。 */ private final Map> storagePeers; private PeerManager() { this.peers = new ConcurrentHashMap<>(); this.storagePeers = new ConcurrentHashMap<>(); } public static final PeerManager getInstance() { return INSTANCE; } /** * 获取所有Peer信息。 */ public Map> peers() { synchronized (this.storagePeers) { return this.storagePeers.entrySet().stream() .collect(Collectors.toMap(Map.Entry::getKey, entry -> { return new ArrayList<>(entry.getValue()); })); } } /** * 删除任务对应的Peer列表 */ public void remove(String infoHashHex) { synchronized (this.peers) { this.peers.remove(infoHashHex); } synchronized (this.storagePeers) { this.storagePeers.remove(infoHashHex); } } /** * 新增Peer,插入尾部。 * * @param infoHashHex 下载文件infoHashHex * @param parent torrent下载统计 * @param host 地址 * @param port 端口 * * @return PeerSession,如果是本机IP返回null。 */ public PeerSession newPeerSession(String infoHashHex, StatisticsSession parent, String host, Integer port, byte source) { var deque = deque(infoHashHex); synchronized (deque) { PeerSession peerSession = findPeerSession(infoHashHex, host); if(peerSession == null) { if(LOGGER.isDebugEnabled()) { LOGGER.debug("添加PeerSession,{}-{},来源:{}", host, port, PeerConfig.source(source)); } peerSession = PeerSession.newInstance(parent, host, port); if(source == PeerConfig.SOURCE_CONNECT || PeerEvaluator.getInstance().eval(peerSession)) { // 计算插入位置 deque.offerLast(peerSession); // 插入尾部:优先级高 } else { deque.offerFirst(peerSession); // 插入头部:优先级低 } list(infoHashHex).add(peerSession); // 存档 } peerSession.source(source); // 设置来源 return peerSession; } } /** * 放入一个劣质的Peer,插入头部。 */ public void inferior(String infoHashHex, PeerSession peerSession) { var deque = deque(infoHashHex); synchronized (deque) { deque.offerFirst(peerSession); } } /** * 放入一个优质的Peer,插入尾部。 */ public void preference(String infoHashHex, PeerSession peerSession) { var deque = deque(infoHashHex); synchronized (deque) { deque.offerLast(peerSession); } } /** * 从尾部选择一个Peer下载,选择可用状态的Peer。 */ public PeerSession pick(String infoHashHex) { var deque = deque(infoHashHex); synchronized (deque) { int index = 0; final int size = deque.size(); PeerSession peerSession = null; while(true) { if(++index > size) { break; } peerSession = deque.pollLast(); if(peerSession.available()) { // 可用 return peerSession; } else { deque.offerFirst(peerSession); } } return null; } } /** *

发送have消息

*

只发送给当前上传和下载的Peer。

*/ public void have(String infoHashHex, int index) { final var list = list(infoHashHex); if(CollectionUtils.isEmpty(list)) { return; } final AtomicInteger count = new AtomicInteger(0); list.stream() .filter(session -> session.uploading() || session.downloading()) .forEach(session -> { var peerConnect = session.peerConnect(); var peerLauncher = session.peerLauncher(); if(peerConnect != null && peerConnect.available()) { count.incrementAndGet(); peerConnect.have(index); } else if(peerLauncher != null && peerLauncher.available()) { count.incrementAndGet(); peerLauncher.have(index); } }); LOGGER.debug("发送Have消息,通知Peer数量:{}", count.get()); } /** *

发送PEX消息

*

只发送给当前上传和下载的Peer。

*/ public void exchange(String infoHashHex, List optimize) { final byte[] bytes = PeerExchangeMessageHandler.buildMessage(optimize); if(bytes == null) { return; } final var list = list(infoHashHex); if(CollectionUtils.isEmpty(list)) { return; } final AtomicInteger count = new AtomicInteger(0); list.stream() .filter(session -> session.uploading() || session.downloading()) .forEach(session -> { var peerConnect = session.peerConnect(); var peerLauncher = session.peerLauncher(); if(peerConnect != null && peerConnect.available()) { count.incrementAndGet(); peerConnect.exchange(bytes); } else if(peerLauncher != null && peerLauncher.available()) { count.incrementAndGet(); peerLauncher.exchange(bytes); } }); LOGGER.debug("发送PEX消息,Peer数量:{},通知Peer数量:{}", optimize.size(), count.get()); } /** * 获取对应的Peer列表 */ private Deque deque(String infoHashHex) { synchronized (this.peers) { Deque deque = this.peers.get(infoHashHex); if(deque == null) { deque = new LinkedBlockingDeque<>(); this.peers.put(infoHashHex, deque); } return deque; } } /** * 获取对应的Peer列表(存储) */ public List list(String infoHashHex) { synchronized (this.storagePeers) { List list = this.storagePeers.get(infoHashHex); if(list == null) { list = new ArrayList<>(); this.storagePeers.put(infoHashHex, list); } return list; } } /** * 是否已经添加过 */ private PeerSession findPeerSession(String infoHashHex, String host) { final var list = list(infoHashHex); final Optional optional = list.stream().filter(peer -> { return peer.equals(host); }).findFirst(); if(optional.isPresent()) { return optional.get(); } return null; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy