com.acgist.snail.net.torrent.tracker.bootstrap.TrackerClient Maven / Gradle / Ivy
package com.acgist.snail.net.torrent.tracker.bootstrap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.acgist.snail.pojo.session.TorrentSession;
import com.acgist.snail.protocol.Protocol;
import com.acgist.snail.system.config.TrackerConfig;
import com.acgist.snail.system.exception.NetException;
import com.acgist.snail.utils.NumberUtils;
import com.acgist.snail.utils.ObjectUtils;
import com.acgist.snail.utils.StringUtils;
/**
* Tracker客户端
* 基本协议:TCP(HTTP)、UDP、WS(WebSocket)
* sid:每一个Torrent和Tracker服务器对应的id。
*
* @author acgist
* @since 1.0.0
*/
public abstract class TrackerClient implements Comparable {
private static final Logger LOGGER = LoggerFactory.getLogger(TrackerClient.class);
/**
* 想要获取的Peer数量
*/
public static final int WANT_PEER_SIZE = 50;
/**
* 权重
* 查询成功会使权重增加,查询失败会使权重减少。
*/
protected int weight;
/**
* ID
* UDP获取连接ID时使用
*/
protected final Integer id;
/**
* 协议类型
*/
protected final Protocol.Type type;
/**
* 刮檫地址
*/
protected final String scrapeUrl;
/**
* 声明地址
*/
protected final String announceUrl;
/**
* 失败次数,成功后清零,超过一定次数#{@link TrackerConfig#MAX_FAIL_TIMES}设置为不可用。
*/
private int failTimes = 0;
/**
* 失败原因
*/
private String failMessage;
/**
* 是否可用
*/
private boolean available = true;
public TrackerClient(String scrapeUrl, String announceUrl, Protocol.Type type) throws NetException {
if(StringUtils.isEmpty(announceUrl)) {
throw new NetException("不支持的Tracker声明地址:" + announceUrl);
}
this.id = NumberUtils.build();
this.type = type;
this.weight = 0;
this.scrapeUrl = scrapeUrl;
this.announceUrl = announceUrl;
}
/**
* 是否可用
*/
public boolean available() {
return this.available;
}
/**
* 查找Peer:查找到的结果放入Peer列表
*/
public void findPeers(Integer sid, TorrentSession torrentSession) {
if(!available()) {
return;
}
try {
announce(sid, torrentSession);
this.failTimes = 0;
this.weight++;
} catch (Exception e) {
LOGGER.error("查找Peer异常,失败次数:{},地址:{}", this.failTimes, this.announceUrl, e);
this.weight--;
if(++this.failTimes >= TrackerConfig.MAX_FAIL_TIMES) {
LOGGER.warn("TrackerClient停用,失败次数:{},地址:{}", this.failTimes, this.announceUrl, e);
this.available = false;
this.failMessage = e.getMessage();
}
}
}
/**
* 跟踪(声明)
*
* @param sid sid
* @param torrentSession BT任务信息
*
* @throws NetException 网络异常
*/
public abstract void announce(Integer sid, TorrentSession torrentSession) throws NetException;
/**
* 完成:下载完成时推送,如果一开始时就已经完成不需要推送。
*
* @param sid sid
* @param torrentSession BT任务信息
*
* @throws NetException 网络异常
*/
public abstract void complete(Integer sid, TorrentSession torrentSession) throws NetException;
/**
* 停止
*
* @param sid sid
* @param torrentSession BT任务信息
*
* @throws NetException 网络异常
*/
public abstract void stop(Integer sid, TorrentSession torrentSession) throws NetException;
/**
* 刮檫
*
* @param sid sid
* @param torrentSession BT任务信息
*
* @throws NetException 网络异常
*/
public abstract void scrape(Integer sid, TorrentSession torrentSession) throws NetException;
/**
* 创建声明消息
*
* @param sid sid
* @param torrentSession BT任务信息
* @param event 事件
*
* @return 声明消息
*/
protected Object buildAnnounceMessage(Integer sid, TorrentSession torrentSession, TrackerConfig.Event event) {
long download = 0L, left = 0L, upload = 0L;
final var taskSession = torrentSession.taskSession();
if(taskSession != null) {
final var statistics = taskSession.statistics();
download = statistics.downloadSize(); // 已下载
left = taskSession.getSize() - download; // 剩余下载
upload = statistics.uploadSize(); // 已上传
}
return this.buildAnnounceMessageEx(sid, torrentSession, event, download, left, upload);
}
/**
* 创建声明消息
*
* @param sid sid
* @param torrentSession BT任务信息
* @param event 事件
* @param download 已下载大小
* @param left 剩余大小
* @param upload 已上传大小
*
* @return 声明消息
*/
protected abstract Object buildAnnounceMessageEx(Integer sid, TorrentSession torrentSession, TrackerConfig.Event event, long download, long left, long upload);
public Integer id() {
return this.id;
}
public Protocol.Type type() {
return this.type;
}
public String announceUrl() {
return this.announceUrl;
}
public String failMessage() {
return this.failMessage;
}
/**
* 判断当前TrackerClient的声明URL和声明URL是否一致。
*/
public boolean equals(String announceUrl) {
return this.announceUrl.equals(announceUrl);
}
@Override
public int compareTo(TrackerClient client) {
return this.weight == client.weight ? 0 : this.weight > client.weight ? 1 : -1;
}
@Override
public int hashCode() {
return ObjectUtils.hashCode(this.announceUrl);
}
/**
* 如果声明URL一致即为相等
*/
@Override
public boolean equals(Object object) {
if(ObjectUtils.equals(this, object)) {
return true;
}
if(object instanceof TrackerClient) {
final TrackerClient client = (TrackerClient) object;
return StringUtils.equals(this.announceUrl, client.announceUrl);
}
return false;
}
@Override
public String toString() {
return ObjectUtils.toString(this, this.id, this.type, this.failTimes, this.announceUrl);
}
}