Servent.java

Index Score
xnap.plugin.gnutella.net
XNap 2

View: Reasons, Metrics, Source Code

These are the metrics that contribute to the Enerjy Score for this file, ranked by impact. So the metrics listed at the top influence the score to a greater extent that the metrics listed at the bottom.

MetricDescription
/* * 03/31/2001 * * GnutellaConnectionHandler.java * Copyright (C) 2001 Frederik Zimmer * tristian@users.sourceforge.net * http://sourceforge.net/projects/ziga/ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package xnap.plugin.gnutella.net; import xnap.net.*; import xnap.util.*; import xnap.plugin.gnutella.util.*; import xnap.plugin.gnutella.io.*; import java.net.Socket; import java.net.SocketException; import java.net.InetAddress; import java.io.IOException; import java.io.EOFException; import java.io.InterruptedIOException; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.util.*; public class Servent extends AbstractCommunication implements Runnable { //--- Constant(s) --- public static final int STATUS_NOT_CONNECTED = 0; public static final int STATUS_CONNECTING = 1; public static final int STATUS_CONNECTED = 2; public static final int STATUS_DISCONNECTED = 3; public static final int STATUS_NEW_STATS = 4; public static final String[] STATUS_MSGS = { "", "connecting...", "connected", "disconnected", "error" }; public static final int PRIORITY_BROADCAST = 0; public static final int PRIORITY_ROUTE = 1; public static final int PRIORITY_NORMAL = 2; protected static final long ALIVE_PING_INTERVAL = 30 * 1000; protected static final int CONNECT_SOCKET_TIMEOUT = 5 * 1000; protected static final int DEFAULT_SOCKET_TIMEOUT = 30 * 1000; protected static final String CONNECT = " CONNECT/"; protected static final String LOGINMSG_04 = " CONNECT/0.4"; protected static final String LOGIN_OK_04 = " OK"; protected static final String NETWORK_NAME = "GNUTELLA"; protected static final String LOGIN_FORBIDDEN = " FORBIDDEN\n\n"; protected static final String USER_AGENT = "User-Agent"; protected static final String VENDORCODE = "XNAP"; protected static final String X_TRY = "X-Try"; protected static final String X_YOU_ARE = "X-YouAre"; protected static final String VERSION = "0.6"; //--- Data field(s) --- private GnuPreferences gnuPrefs = GnuPreferences.getInstance(); protected RoutingTable routingTable = RoutingTable.getInstance(); protected Socket socket; protected DataInputStream in; protected DataOutputStream out; protected Thread thread; protected WriterThread writerThread; protected boolean alive; protected boolean incomingConnection = false; protected String ip; protected int port; protected String hostString; protected String firstLine; protected long startTime; protected long connectTime; protected long lastAlivePing; protected int badMessageCount; private int messagesReceived; private int numOfKBs; private int numOfFiles; private SendQueue sendQueue; /** * Constructs a new servent to accept the connection coming in from socket. */ public Servent(String request, Socket sock) { sendQueue = new SendQueue(); writerThread = new WriterThread(this); socket = sock; firstLine = request; ip = socket.getInetAddress().getHostAddress(); port = socket.getPort(); hostString = ip + ":" + port; incomingConnection = true; } /** * Creates a new Servent with given ip and port. */ public Servent(String ip, int port) { sendQueue = new SendQueue(); writerThread = new WriterThread(this); this.ip = ip; this.port = port; hostString = ip + ":" + port; } /** * Called from run when connection is an accepting one, handles * handshaking. */ public boolean accept() { try { in = new DataInputStream(new BufferedInputStream(socket.getInputStream())); out = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream())); writerThread.setOutputStream(out); } catch (IOException ie) { Debug.log(ie); setStatus(STATUS_DISCONNECTED, ie.getMessage()); return false; } StringTokenizer t = new StringTokenizer(firstLine, "/"); if (t.countTokens() < 2) { sendInvalidLogin(out, "Wrong number of args"); return false; } /* skip "GNUTELLA CONNECT/" */ t.nextToken(); setStatus(STATUS_CONNECTING, "Handshake"); String version = t.nextToken(); // checking for '1' as a return value is not a good idea if (VersionParser.compare(version, "0.4") == 1) { try { out.write(NETWORK_NAME.getBytes()); out.write(LOGIN_OK_04.getBytes()); out.write(10); out.write(10); out.flush(); } catch (IOException ie) { return false; } return true; } else if (VersionParser.compare(version, "0.4") > 0) { StringBuffer loginOk = new StringBuffer(); loginOk.append(NETWORK_NAME); loginOk.append('/'); loginOk.append(VERSION); loginOk.append(" 200 OK"); try { out.write(loginOk.toString().getBytes()); out.write(13); out.write(10); /* send some cached servers */ sendServerHeaders(out); out.flush(); } catch (IOException ie) { return false; } ReadLineReader rlr = new ReadLineReader(in); String response = rlr.readLine(); if (response != null && response.indexOf("200") != -1) sendInvalidLogin(out, "Wrong response" + response); else { sendInvalidLogin(out, "Wrong response"); return false; } } return true; } /** * Called from <code>run()</code> when connection is initiated from us, * handles handshaking. */ public boolean connect() { ReadLineReader rlr; try { socket = new Socket(ip, port); socket.setSoTimeout(CONNECT_SOCKET_TIMEOUT); in = new DataInputStream(new BufferedInputStream(socket.getInputStream())); out = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream())); writerThread.setOutputStream(out); } catch (SocketException se) { Debug.log("gnutella: timeout reached"); setStatus(STATUS_DISCONNECTED, se.getMessage()); return false; } catch (IOException ie) { Debug.log(ie.getMessage() + " " + ip + ":" + port); setStatus(STATUS_DISCONNECTED, ie.getMessage()); return false; } rlr = new ReadLineReader(in); try { out.write(NETWORK_NAME.getBytes()); out.write(LOGINMSG_04.getBytes()); out.write(10); out.write(10); out.flush(); } catch (IOException ie) { Debug.log("gnutella: Could not handshake"); setStatus(STATUS_DISCONNECTED); return false; } String response = rlr.readLine(); if (response == null || !response.equalsIgnoreCase(NETWORK_NAME + LOGIN_OK_04)) { Debug.log("gnutella: " + response); return false; } return true; } /** * Closes socket if open. */ public void close() { if (socket != null) { try { socket.close(); } catch(IOException e) { } socket = null; writerThread.close(); if (in != null) { try { in.close(); } catch(IOException e) {} } } setStatus(STATUS_DISCONNECTED); } /** * Checks for equity. */ public boolean equals(Object o) { if (o == null) { return false; } else { try { Servent handler = (Servent) o; return handler.ip.equals(ip) && handler.port == port; } catch(ClassCastException e) { return false; } } } /** * Returns ip address of remote host. */ public String getAddress() { return ip; } public long getConnectTime() { return System.currentTimeMillis() - connectTime; } /** * Returns string of local ip address this socket is bound to. Returns * <code>null</code> if socket is <code>null</code>. */ public String getLocalAddress() { if (socket != null) { return socket.getLocalAddress().getHostAddress(); } else { return null; } } /** * Returns number of messages this servent has already received. */ public int getMessagesReceived() { return messagesReceived; } /** * Returns number of messages this servent has already sent. */ public int getMessagesSent() { return writerThread.getMessagesSent(); } /** * Returns number of files the direct remote host shares. */ public int getNumberOfFiles() { return numOfFiles; } /** * Returns number of kilo bytes the direct remote host shares. */ public int getNumberOfKBs() { return numOfKBs; } /** * Returns port of remote host. */ public int getPort() { return port; } /** * Returns sendQueue. */ public SendQueue getSendQueue() { return sendQueue; } /** * Returns uptime of connection in seconds. */ public long getUpTime() { if (startTime != 0) return (System.currentTimeMillis() - startTime) / 1000; return startTime; } protected String getStatusMsg(int index) { return STATUS_MSGS[index]; } private void handleStats(PongMessage msg) { if (msg.getHops() == 0) { numOfFiles = msg.getNumberOfFiles(); numOfKBs = msg.getNumberOfKBs(); } } public boolean isConnected() { return (status == STATUS_CONNECTED); } /** * Returns type of connection. */ public boolean isIncoming() { return incomingConnection; } /** * Sends a ping to the remote host after timeout * <code>ALIVE_PING_INTERVAL</code> if <code>sendQueue</code> has no * messages queued. * @see ALIVE_PING_INTERVAL */ public void keepAlivePing() { if ((System.currentTimeMillis() - lastAlivePing) > ALIVE_PING_INTERVAL && sendQueue.size() == 0) sendAlivePing(); } /** * Main loop reads in new messages and calls respective functions in * <code>routingTable</code>. * @see RoutingTable */ public void run() { boolean success = false; setStatus(STATUS_CONNECTING); connectTime = System.currentTimeMillis(); if (incomingConnection) { success = accept(); } else { success = connect(); } if (!success) { setStatus(STATUS_DISCONNECTED); return; } setStatus(STATUS_CONNECTED); startTime = System.currentTimeMillis(); try { socket.setSoTimeout(DEFAULT_SOCKET_TIMEOUT); } catch (SocketException se) { Debug.log(se); } sendPing(); writerThread.start(); while (alive) { try { boolean isUnknownMessage = false; DescriptorHeader header = new DescriptorHeader(in); messagesReceived++; switch (header.payloadDescriptor) { case DescriptorHeader.PING: PingMessage pingMsg = new PingMessage(header); routingTable.route(pingMsg, this); break; case DescriptorHeader.PONG: PongMessage pongMsg = new PongMessage(header, in); handleStats(pongMsg); routingTable.route(pongMsg, this); break; case DescriptorHeader.QUERY: QueryMessage queryMsg = new QueryMessage(header, in); routingTable.route(queryMsg, this); break; case DescriptorHeader.QUERY_HIT: QueryHitMessage queryHitMsg = new QueryHitMessage(header, in); routingTable.route(queryHitMsg, this); break; case DescriptorHeader.PUSH: PushMessage pushMsg = new PushMessage(header, in); routingTable.route(pushMsg, this); break; case DescriptorHeader.BYE: ByeMessage byeMsg = new ByeMessage(header, in); Debug.log("bye: " + byeMsg.getErrorCode() + " " + byeMsg.getErrorDescription()); break; default: UnknownMessage unknownMessage = new UnknownMessage(header, in); Debug.log("Unknown message received"); isUnknownMessage = true; } if (isUnknownMessage) { badMessageCount++; } else if (badMessageCount > 0) { badMessageCount--; } if (badMessageCount > 2) { throw new InvalidMessageException("Too many unknown messages"); } if (sendQueue.size() == 0) { keepAlivePing(); } } catch(InterruptedIOException e) { if (alive) { sendAlivePing(); } } catch(IOException e) { Debug.log("Peer disconnected"); break; } catch(InvalidMessageException e) { Debug.log(e); break; } } writerThread.stop(); close(); if (incomingConnection) { try { thread.sleep(5000); } catch(InterruptedException e) { } } setStatus(STATUS_DISCONNECTED); } /** * Sends message to remote host. Message is queued in * <code>sendQueue</code> and sent by <code>writerThread</code>. * @see SendQueue * @see Writerthread */ public void send(Message msg, int priority) { if (getStatus() == STATUS_CONNECTED) { sendQueue.add(msg, priority); } } private void sendAlivePing() { Debug.log("gnutella: sending alive ping"); lastAlivePing = System.currentTimeMillis(); PingMessage msg = new PingMessage(); msg.setTTL((byte) 1); routingTable.send(msg, this); } private void sendInvalidLogin(DataOutputStream out, String reason) { try { out.write(NETWORK_NAME.getBytes()); out.write(LOGIN_FORBIDDEN.getBytes()); out.flush(); Debug.log(reason); } catch (IOException ie) { } close(); } /** * Sends a ping message through this servent. */ public void sendPing() { routingTable.send(new PingMessage(), this); } /** * Behave as host cache and send some cached hosts. */ private void sendServerHeaders(DataOutputStream os) throws IOException { Debug.log("Sending some server entries"); LineWriter out = new LineWriter(os); out.println(USER_AGENT + ": " + VENDORCODE); out.println(X_YOU_ARE + ": " + ip); StringBuffer cachedHosts = new StringBuffer(); Connections connections = Connections.getInstance(); int j = 0; synchronized (connections) { for (Iterator i = connections.iterator(); i.hasNext() && j < 10; j++) { Servent s = (Servent) i.next(); cachedHosts.append(s.getAddress()); cachedHosts.append(':'); cachedHosts.append(s.getPort()); cachedHosts.append(", "); } } /* remove last comma again */ if (j > 0) { cachedHosts.deleteCharAt(cachedHosts.length() - 1); } out.println(X_TRY + ": " + cachedHosts.toString()); out.println(); } /** * Start connection to remote host. */ public synchronized void start() { if (!alive) { alive = true; thread = new Thread(this); thread.start(); } } /** * Stop connection to remote host. */ public synchronized void stop() { if (alive) { alive = false; writerThread.stop(); setStatus(STATUS_DISCONNECTED, "User disconnect"); close(); } } /** * Returns the host string containing both, the remote host's ip and port * as string separated by a column. */ public String toString() { return hostString; } }

The table below shows all metrics for Servent.java.

MetricValueDescription