/** * Runs the connection to the other peer. This method does not return until the connection is * terminated. * * <p>When the connection is correctly started the connected() method of the given PeerListener is * called. If the connection ends or the connection could not be setup correctly the * disconnected() method is called. * * <p>If the given BitField is non-null it is send to the peer as first message. */ public void runConnection( I2PSnarkUtil util, PeerListener listener, BitField bitfield, MagnetState mState) { if (state != null) throw new IllegalStateException("Peer already started"); if (_log.shouldLog(Log.DEBUG)) _log.debug("Running connection to " + peerID.toString(), new Exception("connecting")); try { // Do we need to handshake? if (din == null) { // Outgoing connection sock = util.connect(peerID); if (_log.shouldLog(Log.DEBUG)) _log.debug("Connected to " + peerID + ": " + sock); if ((sock == null) || (sock.isClosed())) { throw new IOException("Unable to reach " + peerID); } InputStream in = sock.getInputStream(); OutputStream out = sock.getOutputStream(); byte[] id = handshake(in, out); byte[] expected_id = peerID.getID(); if (expected_id == null) { peerID.setID(id); } else if (Arrays.equals(expected_id, id)) { if (_log.shouldLog(Log.DEBUG)) _log.debug("Handshake got matching IDs with " + toString()); } else { throw new IOException( "Unexpected peerID '" + PeerID.idencode(id) + "' expected '" + PeerID.idencode(expected_id) + "'"); } } else { // Incoming connection if (_log.shouldLog(Log.DEBUG)) _log.debug("Already have din [" + sock + "] with " + toString()); } // bad idea? if (metainfo == null && (options & OPTION_EXTENSION) == 0) { if (_log.shouldLog(Log.INFO)) _log.info("Peer does not support extensions and we need metainfo, dropping"); throw new IOException("Peer does not support extensions and we need metainfo, dropping"); } PeerConnectionIn in = new PeerConnectionIn(this, din); PeerConnectionOut out = new PeerConnectionOut(this, dout); PeerState s = new PeerState(this, listener, metainfo, in, out); if ((options & OPTION_EXTENSION) != 0) { if (_log.shouldLog(Log.DEBUG)) _log.debug("Peer supports extensions, sending reply message"); int metasize = metainfo != null ? metainfo.getInfoBytes().length : -1; out.sendExtension(0, ExtensionHandler.getHandshake(metasize)); } if ((options & OPTION_I2P_DHT) != 0 && util.getDHT() != null) { if (_log.shouldLog(Log.DEBUG)) _log.debug("Peer supports DHT, sending PORT message"); int port = util.getDHT().getPort(); out.sendPort(port); } // Send our bitmap if (bitfield != null) s.out.sendBitfield(bitfield); // We are up and running! state = s; magnetState = mState; listener.connected(this); if (_log.shouldLog(Log.DEBUG)) _log.debug("Start running the reader with " + toString()); // Use this thread for running the incomming connection. // The outgoing connection creates its own Thread. out.startup(); Thread.currentThread().setName("Snark reader from " + peerID); s.in.run(); } catch (IOException eofe) { // Ignore, probably just the other side closing the connection. // Or refusing the connection, timing out, etc. if (_log.shouldLog(Log.DEBUG)) _log.debug(this.toString(), eofe); } catch (Throwable t) { _log.error(this + ": " + t.getMessage(), t); if (t instanceof OutOfMemoryError) throw (OutOfMemoryError) t; } finally { if (deregister) listener.disconnected(this); disconnect(); } }