All Downloads are FREE. Search and download functionalities are using the official Maven repository.

mq5.0-source.main.http-tunnel.tunnel.src.main.java.com.sun.messaging.jmq.httptunnel.tunnel.HttpTunnelConnection Maven / Gradle / Ivy

There is a newer version: 5.1
Show newest version
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2000-2012 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

/*
 * @(#)HttpTunnelConnection.java	1.17 06/28/07
 */ 

package com.sun.messaging.jmq.httptunnel.tunnel;

import java.util.*;
import java.io.*;
import com.sun.messaging.jmq.util.timer.*;
import com.sun.messaging.jmq.httptunnel.api.share.HttpTunnelDefaults;

/**
 * This class implements the state engine for a HTTP tunnel
 * connection. It uses the unreliable packet delivery mechanism
 * provided by the appropriate implementation of
 * {@link com.sun.messaging.jmq.httptunnel.tunnel.HttpTunnelDriver}.
 * 

* It also provides methods to read bytes from and write bytes to * the connection stream. */ public class HttpTunnelConnection implements HttpTunnelDefaults { private HttpTunnelDriver wire; private int connId; private int nextRecvSeq; private int rxWindowMax; private HttpTunnelPacket[] recvQ; private Object recvQLock; private int readOffset; private boolean sendWindowUpdate; private boolean connCloseReceived; private boolean connAbortReceived; private int rxConnCloseSeq; private int lastAckSeq; private int nextSendSeq; private int txWindowMax; private int dupAckCount; private Vector sendQ; private Object sendQLock; private boolean txDataDisabled; private boolean connCloseSent; private int txConnCloseSeq; private Hashtable rexmitTable; private long RTO; private long measuredRTO; private int pullPeriod; private int connectionTimeout; private int nRetransmit; private int nFastRetransmit; private static MQTimer timer = new MQTimer(true); private static boolean DEBUG = Boolean.getBoolean("httptunnel.debug"); private static long CLOSE_WAIT_TIMEOUT = Long.getLong("imq.httptunnel.close_wait", 60000).longValue(); private String remoteip = null; /** * Create and initialize a new connection object. */ public HttpTunnelConnection(int connId, HttpTunnelDriver wire) { this.wire = wire; this.connId = connId; nextRecvSeq = 0; rxWindowMax = DEFAULT_WINDOW_SIZE; recvQ = new HttpTunnelPacket[DEFAULT_WINDOW_SIZE]; recvQLock = new Object(); readOffset = 0; sendWindowUpdate = false; connCloseReceived = false; connAbortReceived = false; rxConnCloseSeq = 0; lastAckSeq = -1; // = (remote initial nextRecvSeq) - 1 nextSendSeq = 0; txWindowMax = 16; // Start with a reasonable default value. dupAckCount = 0; sendQ = new Vector(); sendQLock = new Object(); txDataDisabled = false; connCloseSent = false; txConnCloseSeq = 0; rexmitTable = new Hashtable(); RTO = INITIAL_RETRANSMIT_PERIOD; measuredRTO = INITIAL_RETRANSMIT_PERIOD; pullPeriod = -1; connectionTimeout = -1; nRetransmit = 0; nFastRetransmit = 0; TimerTask dummyTask = new TimerTask() { public void run() {} }; try { timer.schedule(dummyTask, 1000); dummyTask.cancel(); } catch (IllegalStateException ise) { timer = new MQTimer(true); } // DebugStats ds = new DebugStats(this); } public void setRemoteAddr(String ip) { remoteip = ip; } public String getRemoteAddr() { return remoteip; } /** * Check if a given sequence number fits within a given range. */ private boolean checkRange(int first, int last, int n) { if (first < last) return (n >= first && n <= last); else if (first > last) return (n >= first || n <= last); else return (first == n); } /** * Receive a data/acknowledgement packet from the driver. */ public void receivePacket(HttpTunnelPacket p, boolean moreData) { int packetType = p.getPacketType(); if (packetType == DATA_PACKET) receiveData(p, moreData); else receiveAck(p); return; } /** * Handles a data packet from the network. * @param p Incoming packet * @param moreData true if driver has more data * packets that need to be processed. If so, this method defers * sending an acknowledgement, if possible. */ private void receiveData(HttpTunnelPacket p, boolean moreData) { int seq = p.getSequence(); boolean ackNow = true; synchronized (recvQLock) { // BugID 4758336 : Check for CONN_CLOSE_PACKET even if // recvQ is null. if (p.getPacketType() == CONN_CLOSE_PACKET) { connCloseReceived = true; rxConnCloseSeq = seq; } if (recvQ == null) { // BugID 4758336 : Drop data packets. Connection is // being closed. The only packet we need to worry // about at this point is the CONN_CLOSE_PACKET... if (connCloseReceived) flushAndClose(); return; } if (checkRange(nextRecvSeq, nextRecvSeq + rxWindowMax - 1, seq)) { // If the close packet was generated due to ungraceful // termination on the other side, do not accept any // packets beyond the close - because there is nobody // at the other end to do retransmissions anymore... if (connCloseReceived && p.getPacketType() == DATA_PACKET) { if (checkRange( rxConnCloseSeq, rxConnCloseSeq + rxWindowMax - 1, seq)) return; } // Store the packet. int first = nextRecvSeq; if (recvQ[0] != null) first = recvQ[0].getSequence(); recvQ[seq - first] = p; if (seq == nextRecvSeq) { int n; for (n = 0; n < recvQ.length; n++) { if (recvQ[n] == null) break; } nextRecvSeq = first + n; rxWindowMax = recvQ.length - n; recvQLock.notify(); // If driver says it has more data packets, // don't ack this packet right away... ackNow = ! moreData; } } if (ackNow) sendAck(); } } /** * Send an acknowledgement packet. * Besides acknowledging a data packet sequence number, an * acknowledgement packet also conveys receivers window size. */ private void sendAck() { if (connAbortReceived) return; int seq = (nextRecvSeq - 1); HttpTunnelPacket ack = new HttpTunnelPacket(); ack.setPacketType(ACK); ack.setPacketBody(null); ack.setConnId(connId); ack.setSequence(seq); ack.setWinsize(rxWindowMax); ack.setChecksum(0); wire.sendPacket(ack); if (connCloseReceived && rxConnCloseSeq == seq) { // We ack the CONN_CLOSE_PACKET only once. wire.shutdown(connId); } if (connCloseReceived == false && rxWindowMax == 0) { // This ack tells the sender to stop sending packets // since the receiver is flooded. The receiver must send // 'window update' ack whenever it is ready to receive // more packets. If the sender somehow misses this // 'window update' it will rely on the 'window update // probe' mechanism and eventually learn that it can // resume transmission. sendWindowUpdate = true; } else { sendWindowUpdate = false; } } /** * Schedule a packet for retransmission. * This method always uses the current value of RTO * as retransmission timeout. */ private void startRetransmitTimer(int seq) { HttpTunnelTimerTask task = new HttpTunnelTimerTask(this, seq); synchronized (rexmitTable) { rexmitTable.put(Integer.toString(seq), task); timer.schedule(task, RTO); } } /** * Cancel a scheduled packet retransmission. This method is * called when a packet is acknowledged. */ private void stopRetransmitTimer(int seq) { HttpTunnelTimerTask task = null; synchronized (rexmitTable) { task = (HttpTunnelTimerTask) rexmitTable.remove(Integer.toString(seq)); if (task != null) task.cancel(); } } /** * Cancels all the scheduled packet retransmissions. This method is * called during connection close sequence. */ private void stopRetransmitTimers() { synchronized (rexmitTable) { for (Enumeration e = rexmitTable.elements(); e.hasMoreElements(); /* */) { ((HttpTunnelTimerTask)e.nextElement()).cancel(); } rexmitTable.clear(); } } Random r = new Random(); int total = 0; int drop = 0; static final int ERRORRATE = -1; /** * Artificially induces packet transmission errors. This method is * used ONLY FOR TESTING. */ private boolean dropPacket(ExtHttpTunnelPacket p) { total++; if ((int)(r.nextFloat()*100) < ERRORRATE) { drop++; if (drop % 100 == 0) System.out.println( "Packet drop rate : " + (drop * 100 / total) + " %"); return true; } return false; } /** * Send a packet reliably over this connection. * This method allocates a sequence number for this packet and * lines up the packet for reliable delivery. */ private void sendData(ExtHttpTunnelPacket p) throws IOException { synchronized (sendQLock) { if (txDataDisabled) { throw new IOException("Connection closed."); } if (sendQ == null) { // BugID 4758336 : If this connection has already been // closed by the peer, and if this method is called // for sending CONN_CLOSE_PACKET from this end, do not // generate an IOException. if (p.getPacketType() == CONN_CLOSE_PACKET) { return; } if (connCloseReceived) throw new IOException("Broken pipe."); else throw new IOException("Connection closed."); } int seq = nextSendSeq++; p.setSequence(seq); long waitStart = -1; while (txWindowMax == 0 || checkRange(lastAckSeq + 1, lastAckSeq + txWindowMax, seq) == false) { if (waitStart == -1) waitStart = System.currentTimeMillis(); try { // Wait for window update sendQLock.wait(MAX_RETRANSMIT_PERIOD); } catch (Exception e) {} if (sendQ == null) { throw new IOException("Broken pipe."); } // If txWindowMax is 0, keep sending the current // packet periodically as a 'window update probe'. if (txWindowMax == 0 && (System.currentTimeMillis() - waitStart >= MAX_RETRANSMIT_PERIOD)) { // Set the dirty flag so that we don't updateRTO // based on ACK for the probe packet. p.setDirtyFlag(true); wire.sendPacket(p); waitStart = -1; } } sendQ.addElement(p); if (p.getPacketType() == CONN_CLOSE_PACKET) { txDataDisabled = true; connCloseSent = true; txConnCloseSeq = seq; } } p.setTxTime(System.currentTimeMillis()); startRetransmitTimer(p.getSequence()); // if (! dropPacket(p)) wire.sendPacket(p); } /** * Retransmit a packet. * @param seq Sequence number to be retransmitted. * @param fromTimer true if the retransmission is * due to timer expiry (as against the 'fast retransmission' mechanism.) */ public void retransmitPacket(int seq, boolean fromTimer) { ExtHttpTunnelPacket p = null; boolean doSend = false; synchronized (sendQLock) { if (sendQ == null) return; if (sendQ.size() == 0) return; int first = ((HttpTunnelPacket) sendQ.elementAt(0)).getSequence(); int last = first + sendQ.size() - 1; if (checkRange(first, last, seq) == false) return; p = (ExtHttpTunnelPacket) sendQ.elementAt(seq - first); p.setDirtyFlag(true); if (first == seq) doSend = true; } if (fromTimer) startRetransmitTimer(p.getSequence()); if (doSend) { int count = p.getRetransmitCount(); p.setRetransmitCount(count + 1); wire.sendPacket(p); if (fromTimer && p.getRetransmitCount() > 1) { RTO <<= 1; if (RTO > MAX_RETRANSMIT_PERIOD) RTO = MAX_RETRANSMIT_PERIOD; } if (fromTimer) nRetransmit++; else nFastRetransmit++; } } /** * Handles an acknowledgement packet from the network. * If the acknowledged sequence number is valid -

* 1. Removes the buffered packets and stops retransmission timers.

* 2. Calculates the roundtrip delay and updates retransmission * timeout.

* 3. Sets the txWindowMax to receiver's advertized window size. * (flow control).

* 4. Retransmits data packet using the 'fast retransmit' mechanism */ private void receiveAck(HttpTunnelPacket p) { int seq = p.getSequence(); synchronized (sendQLock) { if (sendQ == null) { return; } if (connCloseSent && txConnCloseSeq == seq) { txShutdown(); wire.shutdown(connId); return; } if (sendQ.size() > 0) { int first = ((HttpTunnelPacket) sendQ.elementAt(0)).getSequence(); int last = first + sendQ.size() - 1; if (checkRange(first, last, seq) == true) { while (true) { ExtHttpTunnelPacket tmp = (ExtHttpTunnelPacket) sendQ.elementAt(0); sendQ.removeElementAt(0); stopRetransmitTimer(tmp.getSequence()); if (tmp.getSequence() == seq) { if (!tmp.getDirtyFlag()) updateRTO(tmp); break; } } dupAckCount = 0; RTO = measuredRTO; } else if (seq == (first - 1)) { dupAckCount++; if (dupAckCount == FAST_RETRANSMIT_ACK_COUNT) { retransmitPacket(first, false); } } } lastAckSeq = seq; txWindowMax = p.getWinsize(); sendQLock.notify(); } } /** * Update the round trip delay using the latest round * trip time measurement. This method uses the simple approach * described in original TCP RFC 793. Current TCP implementations * use much more efficient approach for RTO calculation, but * for now this should be adequate. */ private void updateRTO(ExtHttpTunnelPacket p) { measuredRTO >>= 1; long RTT = System.currentTimeMillis() - p.getTxTime(); // ALPHA = 7/8 = 0.875; BETA = 2 long SRTT = ((measuredRTO << 3) - measuredRTO + RTT) >>> 3; measuredRTO = SRTT << 1; if (measuredRTO < MIN_RETRANSMIT_PERIOD) measuredRTO = MIN_RETRANSMIT_PERIOD; } /** * Consume the data received on this connection. */ public int readData(byte[] buffer) throws IOException { return readData(buffer, 0, buffer.length); } /** * Throws away the packets from the receive queue that have * been consumed. */ private void discardPackets(int n) { System.arraycopy(recvQ, n, recvQ, 0, recvQ.length - n); for (int i = recvQ.length - n; i < recvQ.length; i++) recvQ[i] = null; rxWindowMax += n; } /** * Consume the data received on this connection. */ public int readData(byte[] buffer, int off, int maxlen) throws IOException { int copied = 0; boolean endOfStream = false; boolean windowMoved = false; synchronized (recvQLock) { while (true) { if (recvQ == null) { if (connCloseReceived) throw new IOException("Connection reset by peer."); else throw new IOException("Connection closed."); } if (recvQ[0] != null) { int packetType = recvQ[0].getPacketType(); if (packetType == DATA_PACKET || packetType == CONN_CLOSE_PACKET) break; discardPackets(1); // e.g. CONN_OPTION_PACKET windowMoved = true; continue; } try { recvQLock.wait(); } catch (Exception e) {} } int n = 0; while (n < recvQ.length && recvQ[n] != null && copied < maxlen) { HttpTunnelPacket p = recvQ[n]; if (p.getPacketType() == CONN_CLOSE_PACKET) { endOfStream = true; n++; continue; } if (p.getPacketType() == CONN_OPTION_PACKET) { n++; continue; } int len = p.getPacketDataSize() - readOffset; if (len > maxlen - copied) len = maxlen - copied; if (buffer != null) { System.arraycopy(p.getPacketBody(), readOffset, buffer, off, len); } readOffset += len; off += len; copied += len; if (readOffset == p.getPacketDataSize()) { readOffset = 0; n++; } } if (endOfStream) { rxShutdown(); } else { if (n > 0) { discardPackets(n); windowMoved = true; } // Receive window has moved. if (windowMoved && sendWindowUpdate) { // Send window update acknowledgement. sendAck(); } } } return copied; } /** * Return the number of bytes available for reading. */ public int available() throws IOException { synchronized (recvQLock) { int ret = 0; if (recvQ == null || recvQ[0] == null) return 0; if (recvQ[0].getPacketType() == DATA_PACKET) ret += (recvQ[0].getPacketDataSize() - readOffset); int n = 1; while (n < recvQ.length && recvQ[n] != null) { if (recvQ[n].getPacketType() == DATA_PACKET) ret += recvQ[n].getPacketDataSize(); n++; } return ret; } } /** * Send application data. */ public void writeData(byte[] data) throws IOException { if (data == null || data.length == 0) return; ExtHttpTunnelPacket p = new ExtHttpTunnelPacket(); p.setPacketType(DATA_PACKET); p.setPacketBody(data); p.setConnId(connId); p.setWinsize(0); p.setChecksum(0); sendData(p); } /** * Ignore the current connection state and close the connection. */ private void flushAndClose() { nextRecvSeq = rxConnCloseSeq + 1; sendAck(); } /** * Initiate the connection close protocol. */ public void closeConn() throws IOException { boolean sendClosePkt = true; synchronized (recvQLock) { if (connCloseReceived) { flushAndClose(); sendClosePkt = false; } } synchronized (sendQLock) { rxShutdown(); if (sendClosePkt) { // Send the CONN_CLOSE_PACKET to the other end. ExtHttpTunnelPacket p = new ExtHttpTunnelPacket(); p.setPacketType(CONN_CLOSE_PACKET); p.setPacketBody(null); p.setConnId(connId); p.setWinsize(0); p.setChecksum(0); sendData(p); } long waitStart = System.currentTimeMillis(); // Block until everything is acknowledged. // One exception - The last message in the sendQ is always // CONN_CLOSE_PACKET. We don't need to wait for it to be // acknowledged... while (sendQ != null && sendQ.size() > 1) { try { // Wait for window update sendQLock.wait(CLOSE_WAIT_TIMEOUT); } catch (InterruptedException e) {} if (sendQ != null) sendQLock.notify(); // This notification was not for me. if ((System.currentTimeMillis() - waitStart) > CLOSE_WAIT_TIMEOUT) { break; // Can't wait forever... } } } } public int getConnId() { return connId; } public int getPullPeriod() { return pullPeriod; } public void setPullPeriod(int pullPeriod) throws IOException { if (this.pullPeriod == pullPeriod) return; this.pullPeriod = pullPeriod; ExtHttpTunnelPacket p = new ExtHttpTunnelPacket(); p.setPacketType(CONN_OPTION_PACKET); p.setConnId(connId); p.setWinsize(0); p.setChecksum(0); ByteArrayOutputStream bos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(bos); try { dos.writeInt(CONOPT_PULL_PERIOD); dos.writeInt(pullPeriod); dos.flush(); bos.flush(); } catch (Exception e) {} byte[] buf = bos.toByteArray(); p.setPacketBody(buf); sendData(p); } public int getConnectionTimeout() { return connectionTimeout; } public void setConnectionTimeout(int connectionTimeout) throws IOException { if (this.connectionTimeout == connectionTimeout) return; this.connectionTimeout = connectionTimeout; ExtHttpTunnelPacket p = new ExtHttpTunnelPacket(); p.setPacketType(CONN_OPTION_PACKET); p.setConnId(connId); p.setWinsize(0); p.setChecksum(0); ByteArrayOutputStream bos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(bos); try { dos.writeInt(CONOPT_CONNECTION_TIMEOUT); dos.writeInt(connectionTimeout); dos.flush(); bos.flush(); } catch (Exception e) {} byte[] buf = bos.toByteArray(); p.setPacketBody(buf); sendData(p); } /** * Handles connection parameter negotiations. */ public void handleConnOption(HttpTunnelPacket p) { byte[] buf = p.getPacketBody(); ByteArrayInputStream bis = new ByteArrayInputStream(buf); DataInputStream dis = new DataInputStream(bis); try { int optname = dis.readInt(); switch (optname) { case CONOPT_PULL_PERIOD: pullPeriod = dis.readInt(); break; case CONOPT_CONNECTION_TIMEOUT: connectionTimeout = dis.readInt(); break; } } catch (Exception e) {} receiveData(p, false); // Treat it like a normal data packet. } /** * Handles connection close request sent by remote end. */ public void handleClose(HttpTunnelPacket p) { txShutdown(); receiveData(p, false); // Treat it like a normal data packet. } /** * Handle connection abort notification. */ public void handleAbort(HttpTunnelPacket p) { synchronized (recvQLock) { if (connAbortReceived) return; connAbortReceived = true; // 'Translate' the abort notification into a CONN_CLOSE_PACKET // Some people may think this is a hack. I personally have // no comments on that issue. p.setPacketType(CONN_CLOSE_PACKET); p.setSequence(nextRecvSeq); handleClose(p); // Don't expect any more packets... wire.shutdown(connId); } } /** * Shutdown the packet receiver. */ private void rxShutdown() { synchronized (recvQLock) { recvQ = null; recvQLock.notifyAll(); } } /** * Shutdown the packet transmitter. */ private void txShutdown() { synchronized (sendQLock) { stopRetransmitTimers(); sendQ = null; sendQLock.notifyAll(); } } public Vector getStats() { if (sendQ == null && recvQ == null) return null; Vector s = new Vector(); s.addElement("connId = " + connId); s.addElement("RX.nextRecvSeq = " + nextRecvSeq); s.addElement("RX.rxWindowMax = " + rxWindowMax); s.addElement("RX.rxConnCloseSeq = " + rxConnCloseSeq); s.addElement("sendWindowUpdate = " + sendWindowUpdate); s.addElement("connCloseReceived = " + connCloseReceived); s.addElement("connAbortReceived = " + connAbortReceived); s.addElement("TX.lastAckSeq = " + lastAckSeq); s.addElement("TX.nextSendSeq = " + nextSendSeq); s.addElement("TX.txWindowMax = " + txWindowMax); s.addElement("TX.dupAckCount = " + dupAckCount); s.addElement("TX.txConnCloseSeq = " + txConnCloseSeq); s.addElement("txDataDisabled = " + txDataDisabled); s.addElement("connCloseSent = " + connCloseSent); s.addElement("RTO = " + RTO); s.addElement("measuredRTO = " + measuredRTO); s.addElement("TX.nRetransmit = " + nRetransmit); s.addElement("TX.nFastRetransmit = " + nFastRetransmit); return s; } public Hashtable getDebugState() { Hashtable ht = wire.getDebugState(); ht.put("connId", String.valueOf(connId)); ht.put("RX.nextRecvSeq", String.valueOf(nextRecvSeq)); ht.put("RX.rxWindowMax", String.valueOf(rxWindowMax)); ht.put("RX.rxConnCloseSeq", String.valueOf(rxConnCloseSeq)); ht.put("sendWindowUpdate", String.valueOf(sendWindowUpdate)); ht.put("connCloseReceived", String.valueOf(connCloseReceived)); ht.put("connAbortReceived", String.valueOf(connAbortReceived)); ht.put("TX.lastAckSeq", String.valueOf(lastAckSeq)); ht.put("TX.nextSendSeq", String.valueOf(nextSendSeq)); ht.put("TX.txWindowMax", String.valueOf(txWindowMax)); ht.put("TX.dupAckCount", String.valueOf(dupAckCount)); ht.put("TX.txConnCloseSeq", String.valueOf(txConnCloseSeq)); ht.put("txDataDisabled", String.valueOf(txDataDisabled)); ht.put("connCloseSent", String.valueOf(connCloseSent)); ht.put("RTO", String.valueOf(RTO)); ht.put("measuredRTO", String.valueOf(measuredRTO)); ht.put("TX.nRetransmit", String.valueOf(nRetransmit)); ht.put("TX.nFastRetransmit", String.valueOf(nFastRetransmit)); return ht; } } class HttpTunnelTimerTask extends TimerTask { HttpTunnelConnection conn = null; int seq = 0; public HttpTunnelTimerTask(HttpTunnelConnection conn, int seq) { this.conn = conn; this.seq = seq; } public void run() { conn.retransmitPacket(seq, true); } } class ExtHttpTunnelPacket extends HttpTunnelPacket { private long txTime = 0; private int retransmitCount = 0; private boolean dirtyFlag = false; public void setTxTime(long txTime) { this.txTime = txTime; } public long getTxTime() { return txTime; } public void setRetransmitCount(int retransmitCount) { this.retransmitCount = retransmitCount; } public int getRetransmitCount() { return retransmitCount; } public void setDirtyFlag(boolean dirtyFlag) { this.dirtyFlag = dirtyFlag; } public boolean getDirtyFlag() { return dirtyFlag; } } class DebugStats extends Thread { HttpTunnelConnection conn; public DebugStats(HttpTunnelConnection conn) { this.conn = conn; setDaemon(true); start(); } public void run() { while (true) { Vector s = conn.getStats(); System.out.println("-----------------------------------------"); if (s == null) { System.out.println("CONNECTION CLOSED : " + conn.getConnId()); } else { for (int i = 0; i < s.size(); i++) { System.out.println((String) s.elementAt(i)); } } System.out.println("-----------------------------------------"); if (s == null) break; try { Thread.sleep(10000); } catch (Exception e) { e.printStackTrace(); } } } } /* * EOF */





© 2015 - 2024 Weber Informatics LLC | Privacy Policy