package com.limegroup.gnutella.connection; import java.util.List; import java.util.Map; import org.limewire.collection.Buffer; import org.limewire.util.ByteUtils; import com.limegroup.gnutella.Response; import com.limegroup.gnutella.messages.BadPacketException; import com.limegroup.gnutella.messages.Message; import com.limegroup.gnutella.messages.QueryReply; import com.limegroup.gnutella.messages.QueryRequest; /** * Keeps track of sent/received messages & the amount that dropped. */ public class ConnectionStats { /** The number of messages sent. This includes messages that are dropped. */ private volatile int _numMessagesSent; /** The number of messages received. This includes messages that are spam. */ private volatile int _numMessagesReceived; /** * The number of messages received on this connection either filtered out * or dropped because we didn't know how to route them. */ private volatile int _numReceivedMessagesDropped; /** * The number of messages I dropped because the * output queue overflowed. This happens when the remote host * cannot receive packets as quickly as I am trying to send them. * No synchronization is necessary. */ private volatile int _numSentMessagesDropped; private volatile long repliesReceived; private volatile long repliesSent; private volatile long queriesReceived; private volatile long queriesSent; /** * _lastSent/_lastSentDropped and _lastReceived/_lastRecvDropped the values * of _numMessagesSent/_numSentMessagesDropped and * _numMessagesReceived/_numReceivedMessagesDropped at the last call to * getPercentDropped. LOCKING: These are synchronized by this; * finer-grained schemes could be used. */ private volatile int _lastReceived; private volatile int _lastRecvDropped; private volatile int _lastSent; private volatile int _lastSentDropped; private final Buffer<Byte> responsesPerReply = new Buffer<Byte>(200); private final Buffer<Byte> altsPerResponse = new Buffer<Byte>(200); private final Buffer<Short> altsPerReply = new Buffer<Short>(200); // Getters. public int getSent() { return _numMessagesSent; } public int getReceived() { return _numMessagesReceived; } public int getSentDropped() { return _numSentMessagesDropped; } public int getReceivedDropped() { return _numReceivedMessagesDropped; } public long getRepliesReceived() { return repliesReceived; } public long getQueriesReceived() { return queriesReceived; } /** Adds a number of dropped sent messages. */ public void addSentDropped(int dropped) { _numSentMessagesDropped += dropped; } /** Adds a sent message. */ public void addSent(Message m) { _numMessagesSent++; if (m instanceof QueryRequest) queriesSent++; else if (m instanceof QueryReply) repliesSent++; } /** Increments the number of received messages that have been dropped. */ public void addReceivedDropped() { _numReceivedMessagesDropped++; } /** Increments the stat for the number of messages received. */ public void addReceived() { _numMessagesReceived++; } public void replyReceived(QueryReply r) { // parse reply outside of lock byte count = (byte)r.getUniqueResultCount(); List<Response> responses; try { responses = r.getResultsAsList(); } catch (BadPacketException ignore){ return; } synchronized(this) { responsesPerReply.add(count); short total = 0; for (Response resp : responses) { byte resps = (byte)resp.getLocations().size(); altsPerResponse.add(resps); total += resps; } altsPerReply.add(total); repliesReceived++; } } public void queryReceived() { queriesReceived++; } /** * @modifies this * @effects Returns the percentage of messages sent on this * since the last call to getPercentReceivedDropped that were * dropped by this end of the connection. */ public synchronized float getPercentReceivedDropped() { int rdiff = _numMessagesReceived - _lastReceived; int ddiff = _numReceivedMessagesDropped - _lastRecvDropped; float percent=(rdiff==0) ? 0.f : ((float)ddiff/(float)rdiff*100.f); _lastReceived = _numMessagesReceived; _lastRecvDropped = _numReceivedMessagesDropped; return percent; } /** * @modifies this * @effects Returns the percentage of messages sent on this * since the last call to getPercentSentDropped that were * dropped by this end of the connection. This value may be * greater than 100%, e.g., if only one message is sent but * four are dropped during a given time period. */ public synchronized float getPercentSentDropped() { int rdiff = _numMessagesSent - _lastSent; int ddiff = _numSentMessagesDropped - _lastSentDropped; float percent=(rdiff==0) ? 0.f : ((float)ddiff/(float)rdiff*100.f); _lastSent = _numMessagesSent; _lastSentDropped = _numSentMessagesDropped; return percent; } public void addStats(Map<String,Object> m) { m.put("nqr",repliesReceived); m.put("nqs",repliesSent); m.put("npr",queriesReceived); m.put("nps",queriesSent); synchronized(this) { m.put("nqr",repliesReceived); byte [] resps = new byte[responsesPerReply.size()]; byte [] alts = new byte[altsPerResponse.size()]; byte [] altsReply = new byte[altsPerReply.size() * 2]; for (int i = 0; i < responsesPerReply.size(); i++) resps[i] = responsesPerReply.get(i); for (int i = 0; i < altsPerResponse.size(); i++) alts[i] = altsPerResponse.get(i); for (int i = 0; i < altsPerReply.size(); i++) ByteUtils.short2leb(altsPerReply.get(i), altsReply, i * 2); m.put("respreply", resps); // 50 bytes m.put("altresp", alts); // 50 bytes m.put("altreply", altsReply); // 100 bytes } } }