public PeerExchange(final SharingPeer peer, final SharedTorrent torrent,
			final Socket socket) throws SocketException {
		this.peer = peer;
		this.torrent = torrent;
		this.socket = socket;

		// Set the socket read timeout.
		this.socket.setSoTimeout(PeerExchange.KEEP_ALIVE_FOR_MINUTES*60*1000);
		this.socket.setTcpNoDelay(true); // random flush

		listeners = new HashSet<MessageListener>();
		sendQueue = new LinkedBlockingQueue<Message>();

		if (!this.peer.hasPeerId())
			throw new IllegalStateException("Peer does not have a " +
					"peer ID. Was the handshake made properly?");

		final String peerId = this.peer.getHexPeerId().substring(
				this.peer.getHexPeerId().length()-6)
				.toUpperCase();
		in = new IncomingThread();
		in.setName("bt-peer(.." + peerId + ")-recv");

		out = new OutgoingThread();
		out.setName("bt-peer(.." + peerId + ")-send");

		// Automatically start the exchange activity loops
		stop = false;
		in.start();
		out.start();

		logger.debug("Started peer exchange with " + this.peer + " for " +
				this.torrent + ".");

		// If we have pieces, start by sending a BITFIELD message to the peer.
		final BitSet pieces = this.torrent.getCompletedPieces();
		if (pieces.cardinality() > 0)
			send(Message.BitfieldMessage.craft(pieces));
	}