/* * 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.task; import java.io.DataOutputStream; import java.io.IOException; import java.util.Collection; import java.util.LinkedList; import java.util.Queue; import java.util.logging.Level; import org.bitlet.wetorrent.Event; import org.bitlet.wetorrent.Torrent; import org.bitlet.wetorrent.peer.message.Message; import org.bitlet.wetorrent.peer.TorrentPeer; import org.bitlet.wetorrent.peer.message.Piece; import org.bitlet.wetorrent.util.stream.OutputStreamLimiter; import org.bitlet.wetorrent.util.thread.ThreadTask; public class MessageSender implements ThreadTask { private Queue<Message> messagesToBeSent = new LinkedList<Message>(); private boolean interrupted = false; private TorrentPeer peer; private long sentBytes; private long lastSentMessageMillis; private long uploaded = 0; public MessageSender(TorrentPeer peer) { this.peer = peer; } public synchronized void addSentBytes(Integer byteNumber) { sentBytes += byteNumber; } public synchronized long getSentBytes() { return sentBytes; } public boolean execute() throws Exception { try { Message message = getMessage(); if (message != null) { DataOutputStream os = new DataOutputStream(new OutputStreamLimiter(peer.getSocket().getOutputStream(), peer.getPeersManager().getTorrent().getUploadBandwidthLimiter())); if (message.getType() != Message.KEEP_ALIVE) { byte[] payload = message.getPayload(); if (payload != null) { os.writeInt(payload.length + 1); os.writeByte(message.getType()); addSentBytes(4 + 1); int payloadOffset = 0; while (payloadOffset < payload.length) { int payloadMissing = payload.length - payloadOffset; int payloadChunk = payloadMissing > 1 << 10 ? 1 << 10 : payloadMissing; os.write(payload, payloadOffset, payloadChunk); addSentBytes(payloadChunk); if (message.getType() == message.PIECE) { uploaded += payloadChunk - (payloadOffset == 0 ? 13 : 0); } payloadOffset += payloadChunk; } } else { os.writeInt(1); os.writeByte(message.getType()); addSentBytes(4 + 1); } } else { /* keep alive */ os.writeInt(0); addSentBytes(4); } setLastSentMessageMillis(System.currentTimeMillis()); if (Torrent.verbose) { peer.getPeersManager().getTorrent().addEvent(new Event(this, "Message sent " + message.getType(), Level.FINEST)); } } } catch (Exception e) { if (Torrent.verbose) { peer.getPeersManager().getTorrent().addEvent(new Event(this, "Problem sending message", Level.WARNING)); } throw e; } return true; } public synchronized void addMessage(Message message) { messagesToBeSent.add(message); notify(); } private synchronized Message getMessage() { while (messagesToBeSent.size() == 0 && !interrupted) { try { wait(2000); } catch (InterruptedException ex) { } } return messagesToBeSent.poll(); } public synchronized void interrupt() { try { peer.getSocket().close(); } catch (IOException ex) { } messagesToBeSent.clear(); interrupted = true; notify(); } public void exceptionCought(Exception e) { peer.interrupt(); e.printStackTrace(); } public synchronized void cancel(int index, int begin, int length) { Collection<Message> messagesToCancel = new LinkedList<Message>(); for (Message elem : messagesToBeSent) { if (elem.getType() == Message.PIECE) { Piece block = (Piece) elem; if (block.getIndex() == index && block.getBegin() == begin && block.getLength() == length) { messagesToCancel.add(elem); } } } messagesToBeSent.removeAll(messagesToCancel); } public synchronized void cancelAll() { Collection<Message> messagesToCancel = new LinkedList<Message>(); for (Message elem : messagesToBeSent) { if (elem.getType() == Message.PIECE) { messagesToCancel.add(elem); } } messagesToBeSent.removeAll(messagesToCancel); } public long getUploaded() { return uploaded; } public synchronized long getLastSentMessageMillis() { return lastSentMessageMillis; } public synchronized void setLastSentMessageMillis(long lastSentMessageMillis) { this.lastSentMessageMillis = lastSentMessageMillis; } }