/*
* bitlet - Simple bittorrent library
* Copyright (C) 2008 Alessandro Bahgat Shehata, Daniele Castagna
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.bitlet.wetorrent.peer;
import java.net.InetAddress;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;
import org.bitlet.wetorrent.Event;
import org.bitlet.wetorrent.Torrent;
import org.bitlet.wetorrent.peer.message.Have;
import org.bitlet.wetorrent.peer.message.Message;
import org.bitlet.wetorrent.util.Utils;
public class PeersManager {
private List<Peer> connectingPeers = new LinkedList<Peer>();
private List<Peer> activePeers = new LinkedList<Peer>();
/**
* Creates a new instance of PeersManager
*/
private int connectionCreationTreshold = 35;
private int maxConnection = 45;
private Torrent torrent;
private long disconnectedClientDownloaded = 0l;
private long disconnectedClientUploaded = 0l;
public PeersManager(Torrent torrent) {
this.torrent = torrent;
}
/*
* This is called when we would like to start a connection to
*/
public synchronized TorrentPeer offer(byte[] peerId, InetAddress ip, int port) {
// TODO: this could be optimized with a proper indexing
for (Peer peer : connectingPeers) {
if (peer.getPort() == port && peer.getIp().equals(ip)) {
return null;
}
}
for (Peer peer : activePeers) {
if (peer.getPort() == port && peer.getIp().equals(ip)) {
return null;
}
}
if (activePeers.size() < connectionCreationTreshold) {
TorrentPeer peer = new TorrentPeer(peerId, ip, port, this);
if (Torrent.verbose) {
torrent.addEvent(new Event(this, "Starting connection to new peer: " + ip, Level.FINE));
}
peer.start();
connectingPeers.add(peer);
return peer;
}
if (Torrent.verbose) {
torrent.addEvent(new Event(this, "Too many connection, peer refused: " + ip, Level.FINER));
}
return null;
}
public synchronized TorrentPeer offer(TorrentPeer peer) {
if (activePeers.size() > maxConnection) {
if (Torrent.verbose) {
torrent.addEvent(new Event(this, "Refusing incoming connection: too many connection", Level.FINER));
}
peer.interrupt();
return null;
}
if (Torrent.verbose) {
torrent.addEvent(new Event(this, "Accpeting incoming peer connection ", Level.FINER));
}
peer.setPeersManager(this);
connectingPeers.add(peer);
return peer;
}
public Torrent getTorrent() {
return torrent;
}
public synchronized long getUploaded() {
long uploaded = disconnectedClientUploaded;
for (Peer p : activePeers) {
uploaded += p.getUploaded();
}
return uploaded;
}
public synchronized void interrupted(Peer peer) {
connectingPeers.remove(peer);
if (activePeers.remove(peer)) {
torrent.interrupted(peer);
disconnectedClientDownloaded += peer.getDownloaded();
disconnectedClientUploaded += peer.getUploaded();
}
if (Torrent.verbose) {
torrent.addEvent(new Event(this, activePeers.size() + " active peers, " + connectingPeers.size() + "connecting peer", Level.INFO));
}
}
public synchronized int[] getPiecesFrequencies() {
int[] frequencies = new int[torrent.getMetafile().getPieces().size()];
for (int i = 0; i < frequencies.length; i++) {
for (Peer p : activePeers) {
if (p.hasPiece(i)) {
frequencies[i]++;
}
}
}
return frequencies;
}
public synchronized long getDownloaded() {
long downloaded = disconnectedClientDownloaded;
for (Peer p : activePeers) {
downloaded += p.getDownloaded();
}
return downloaded;
}
public synchronized void tick() {
long now = System.currentTimeMillis();
List<Peer> peersTimedOut = new LinkedList<Peer>();
// TODO: Remove hardcoded millis
for (Peer p : activePeers) {
if (now - p.getLastReceivedMessageMillis() > 120000) {
peersTimedOut.add(p);
} else if (now - p.getLastReceivedMessageMillis() > 110000) {
p.sendMessage(new Message(Message.KEEP_ALIVE, null));
}
}
for (Peer p : peersTimedOut) {
p.interrupt();
interrupted(p);
}
}
public synchronized void interrupt() {
while (connectingPeers.size() > 0) {
connectingPeers.get(0).interrupt();
}
while (activePeers.size() > 0) {
activePeers.get(0).interrupt();
}
}
public synchronized void sendHave(Have have) {
for (Peer p : activePeers) {
if (!p.hasPiece(have.getIndex())) {
p.sendMessage(have);
}
}
}
public synchronized int getActivePeersNumber() {
return activePeers.size();
}
public synchronized int getSeedersNumber() {
int acc = 0;
for (Peer peer : activePeers)
acc += peer.isSeeder() ? 1 : 0;
return acc;
}
public synchronized void connected(Peer peer) {
if (!connectingPeers.remove(peer)) {
if (Torrent.verbose) {
torrent.addEvent(new Event(peer, "Peer connected", Level.WARNING));
}
}
for (Peer p : activePeers) {
if (Utils.bytesCompare(peer.getPeerId(), p.getPeerId())) {
peer.interrupt();
torrent.addEvent(new Event(this, "Peer already connected", Level.FINE));
return;
}
}
activePeers.add(peer);
}
}