package com.acgist.snail.net.torrent.peer;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.BitSet;
import com.acgist.snail.config.PeerConfig;
import com.acgist.snail.config.PeerConfig.Type;
import com.acgist.snail.config.SystemConfig;
import com.acgist.snail.logger.Logger;
import com.acgist.snail.logger.LoggerFactory;
import com.acgist.snail.net.NetException;
import com.acgist.snail.net.codec.IMessageDecoder;
import com.acgist.snail.net.torrent.IEncryptMessageSender;
import com.acgist.snail.net.torrent.IPeerConnect;
import com.acgist.snail.net.torrent.TorrentContext;
import com.acgist.snail.net.torrent.TorrentSession;
import com.acgist.snail.utils.ArrayUtils;
import com.acgist.snail.utils.BitfieldUtils;
import com.acgist.snail.utils.ByteUtils;
import com.acgist.snail.utils.NumberUtils;
import com.acgist.snail.utils.StringUtils;
/**
* Peer消息代理(TCP/UDP)
*
* The BitTorrent Protocol Specification
* 协议链接:http://www.bittorrent.org/beps/bep_0003.html
* Fast Extension
* 协议链接:http://www.bittorrent.org/beps/bep_0006.html
* Private Torrents
* 协议链接:http://www.bittorrent.org/beps/bep_0027.html
*
* Peer状态标识:https://baike.baidu.com/item/uTorrent/8195186
*
* @author acgist
*/
public final class PeerSubMessageHandler implements IMessageDecoder<ByteBuffer>, IPeerConnect {
private static final Logger LOGGER = LoggerFactory.getLogger(PeerSubMessageHandler.class);
/**
* 检查是否使用最大次数:{@value}
*/
private static final int MAX_USELESS_CHECK = 3;
/**
* 检查是否使用次数
*/
private int uselessCheck = 0;
/**
* 是否可用
*/
private volatile boolean available = false;
/**
* 是否已经发送握手
*/
private volatile boolean handshakeSend = false;
/**
* 是否已经处理握手
*/
private volatile boolean handshakeRecv = false;
/**
* 是否是服务端
*/
private final boolean server;
/**
* Peer连接
*
* @see PeerUploader
* @see PeerDownloader
*/
private PeerConnect peerConnect;
/**
* Peer信息
*/
private PeerSession peerSession;
/**
* BT任务信息
*/
private TorrentSession torrentSession;
/**
* Peer连接信息
*/
private PeerConnectSession peerConnectSession;
/**
* 加密消息代理
*/
private IEncryptMessageSender messageEncryptSender;
/**
* 扩展消息代理
*/
private ExtensionMessageHandler extensionMessageHandler;
/**
* DHT扩展消息代理
*/
private DhtExtensionMessageHandler dhtExtensionMessageHandler;
/**
* 服务端
*/
private PeerSubMessageHandler() {
this.server = true;
}
/**
* 客户端
*
* @param peerSession Peer信息
* @param torrentSession BT任务信息
*/
private PeerSubMessageHandler(PeerSession peerSession, TorrentSession torrentSession) {
this.server = false;
this.init(peerSession, torrentSession);
}
/**
* 服务端
*
* @return PeerSubMessageHandler
*/
public static final PeerSubMessageHandler newInstance() {
return new PeerSubMessageHandler();
}
/**
* 客户端
*
* @param peerSession Peer信息
* @param torrentSession BT任务信息
*
* @return PeerSubMessageHandler
*/
public static final PeerSubMessageHandler newInstance(PeerSession peerSession, TorrentSession torrentSession) {
return new PeerSubMessageHandler(peerSession, torrentSession);
}
/**
* 初始消息代理
*
* @param peerSession Peer信息
* @param torrentSession BT任务信息
*/
private void init(PeerSession peerSession, TorrentSession torrentSession) {
this.peerSession = peerSession;
this.torrentSession = torrentSession;
this.extensionMessageHandler = ExtensionMessageHandler.newInstance(this.peerSession, this.torrentSession, this);
this.dhtExtensionMessageHandler = DhtExtensionMessageHandler.newInstance(this.peerSession, this.torrentSession, this);
}
/**
* 初始化服务端
*
* @param infoHashHex InfoHashHex
* @param peerId PeerId
*
* @return 是否成功
*/
private boolean initServer(String infoHashHex, byte[] peerId) {
if(Arrays.equals(PeerConfig.getInstance().getPeerId(), peerId)) {
LOGGER.debug("Peer接入失败(PeerId一致)");
return false;
}
final TorrentSession torrentSession = TorrentContext.getInstance().torrentSession(infoHashHex);
if(torrentSession == null) {
LOGGER.debug("Peer接入失败(种子信息不存在):{}", infoHashHex);
return false;
}
if(!torrentSession.useable()) {
LOGGER.debug("Peer接入失败(任务没有准备完成):{}", infoHashHex);
return false;
}
final InetSocketAddress socketAddress = this.remoteSocketAddress();
if(socketAddress == null) {
LOGGER.debug("Peer接入失败(远程客户端获取失败):{}", infoHashHex);
return false;
}
// 禁止自动获取端口:通过PEX消息获取端口
final PeerSession peerSession = PeerContext.getInstance().newPeerSession(
infoHashHex,
torrentSession.statistics(),
socketAddress.getHostString(),
null,
PeerConfig.Source.CONNECT
);
final PeerUploader peerUploader = torrentSession.newPeerUploader(peerSession, this);
if(peerUploader == null) {
return false;
} else {
this.init(peerSession, torrentSession);
this.peerConnect = peerUploader;
this.peerConnectSession = peerUploader.peerConnectSession();
this.peerSession.peerUploader(peerUploader);
return true;
}
}
/**
* 初始化客户端
*
* @param peerDownloader Peer下载
*
* @return Peer消息代理
*/
public PeerSubMessageHandler initClient(PeerDownloader peerDownloader) {
this.peerConnect = peerDownloader;
this.peerConnectSession = peerDownloader.peerConnectSession();
this.peerSession.peerDownloader(peerDownloader);
return this;
}
/**
* 判断是否没有使用
*
* @return 是否没有使用
*/
public boolean useless() {
if(this.handshakeRecv) {
return false;
}
return ++this.uselessCheck > MAX_USELESS_CHECK;
}
/**
* 判断是否处理握手消息
*
* @return 是否处理握手消息
*/
public boolean handshakeRecv() {
return this.handshakeRecv;
}
/**
* @return BT任务信息
*/
public TorrentSession torrentSession() {
return this.torrentSession;
}
/**
* 判断是否需要加密
*
* @return 是否需要加密
*
* @see PeerSession#encrypt()
*/
public boolean needEncrypt() {
if(this.peerSession == null) {
// 默认使用明文
return false;
}
return this.peerSession.encrypt();
}
/**
* 设置实际消息代理:TCP、UDP(UTP)
*
* @param messageEncryptSender 实际消息代理
*
* @return Peer消息代理
*/
public PeerSubMessageHandler messageEncryptSender(IEncryptMessageSender messageEncryptSender) {
this.messageEncryptSender = messageEncryptSender;
return this;
}
@Override
public final IPeerConnect.ConnectType connectType() {
return this.messageEncryptSender.connectType();
}
@Override
public void onMessa