ssh2streamchannel.java

来自「一个非常好的ssh客户端实现」· Java 代码 · 共 324 行

JAVA
324
字号
/****************************************************************************** * * Copyright (c) 1999-2003 AppGate Network Security AB. All Rights Reserved. *  * This file contains Original Code and/or Modifications of Original Code as * defined in and that are subject to the MindTerm Public Source License, * Version 2.0, (the 'License'). You may not use this file except in compliance * with the License. *  * You should have received a copy of the MindTerm Public Source License * along with this software; see the file LICENSE.  If not, write to * AppGate Network Security AB, Otterhallegatan 2, SE-41118 Goteborg, SWEDEN * *****************************************************************************/package com.mindbright.ssh2;import java.io.InputStream;import java.io.OutputStream;import java.io.IOException;import java.io.InterruptedIOException;import com.mindbright.util.Queue;/** * Class implementing streams-based channels. That is channels which * locally are connected to a pair of Input/Output streams. It is also * possible to apply filters to the channels. */public class SSH2StreamChannel extends SSH2Channel {    protected InputStream  in;    protected OutputStream out;    protected Thread transmitter;    protected Thread receiver;    protected Queue  rxQueue;    protected long   txCounter;    protected long   rxCounter;    /*     * NOTE, if enabled can cause dead-lock when doing re-keyexchange     * if we initiate it, hence SSH2Transport.incompatibleCantReKey is     * set accordingly     */    private boolean rxChanIsQueued;    /**     * Create a new stream channel of the given type. The channel is     * associated with an ssh connection. Channel types are     * defined in <code>SSH2Connection</code> and starts with     * <code>CH_TYPE</code>.     *     * @param channelType Type of channel to create.     * @param connection The ssh connection to associate the channel with.     * @param creator The object the channel is created from.     * @param in The input stream from which data to be sent over the     * channel is read.     * @param out The output stream onto which data received from the     * channel is written.     */    protected SSH2StreamChannel(int channelType, SSH2Connection connection,				Object creator,				InputStream in, OutputStream out) {	super(channelType, connection, creator);	rxChanIsQueued =	    "true".equals(connection.getPreferences().			  getPreference(SSH2Preferences.QUEUED_RX_CHAN));	this.in  = in;	this.out = out;	createStreams();    }    /**     * Apply the given filter to this channel.     *     * @param filter Filter to apply.     */    public void applyFilter(SSH2StreamFilter filter) {	if(filter != null) {	    in  = filter.getInputFilter(in);	    out = filter.getOutputFilter(out);	}    }    private void channelTransmitLoop() {	connection.getLog().debug("SSH2StreamChannel",				  "starting ch. #" + channelId +				  " (" + getType() + ") transmitter");	Thread.yield();	try {	    SSH2TransportPDU pdu;	    int              maxSz = 0;	    int              rcvSz = 0;            boolean          interrupted = false;	    while(!eofSent && !closeSent) {		pdu =		    SSH2TransportPDU.createOutgoingPacket(SSH2.MSG_CHANNEL_DATA,							  txMaxPktSz + 256);		pdu.writeInt(peerChanId);		maxSz = checkTxWindowSize(rcvSz);                do {                                            try {                        rcvSz = in.read(pdu.data, pdu.wPos + 4, maxSz);                        interrupted = false;                    } catch (InterruptedIOException e) {                        interrupted = true;                    }                } while (interrupted);		if(rcvSz == -1) {		    sendEOF();		} else if (!eofSent && !closeSent) {		    pdu.writeInt(rcvSz);		    pdu.wPos  += rcvSz;		    txCounter += rcvSz;		    transmit(pdu);		}	    }	} catch (IOException e) {	    if(!eofSent) {		connection.getLog().error("SSH2StreamChannel",					  "channelTransmitLoop",					  e.toString());	    }	} finally {	    try { in.close(); } catch (IOException e) { /* don't care */ };	    sendClose();	}	connection.getLog().debug("SSH2StreamChannel",				  "exiting ch. #" +				  channelId + " (" + getType() +				  ") transmitter, " + txCounter +				  " bytes tx");    }    private void channelReceiveLoop() {	connection.getLog().debug("SSH2StreamChannel",				  "starting ch. #" + channelId +				  " (" + getType() + ") receiver");	Thread.yield();	try {	    SSH2TransportPDU pdu;	    while((pdu = (SSH2TransportPDU)rxQueue.getFirst()) != null) {		rxWrite(pdu);	    }	} catch (IOException e) {	    connection.getLog().error("SSH2StreamChannel",				      "channelReceiveLoop",				      e.toString());	} finally {	    rxClosing();	}	connection.getLog().debug("SSH2StreamChannel",				  "exiting ch. #" +				  channelId + " (" + getType() +				  ") receiver, " + rxCounter +				  " bytes rx");    }    private final void rxWrite(SSH2TransportPDU pdu) throws IOException {	int len = pdu.readInt();	int off = pdu.getRPos();	rxCounter += len;	out.write(pdu.data, off, len);	pdu.release();	checkRxWindowSize(len);    }    private final void rxClosing() {	// Signal to transmitter that this is an orderly shutdown	//	eofSent = true;	try { out.close(); } catch (IOException e) { /* don't care */ }	try { in.close(); } catch (IOException e) { /* don't care */ }	outputClosed();	// there is a slight chance that the transmitter is waiting for	// window adjust in which case we must interrupt it here so it	// doesn't hang	//	if(txCurrWinSz == 0) {	    txCurrWinSz = -1;	    transmitter.interrupt();	}    }    private final synchronized int checkTxWindowSize(int lastSz) {	txCurrWinSz -= lastSz;	while(txCurrWinSz == 0) {	    // Our window is full, wait for ACK from peer	    try {		this.wait();	    } catch (InterruptedException e) {		if(!eofSent) {		    connection.getLog().error("SSH2StreamChannel",					      "checkTxWindowSize",					      "window adjust wait interrupted");		}	    }	}	// Try sending remaining window size or max packet size before ACK	//	int dataSz = (txCurrWinSz < txMaxPktSz ? txCurrWinSz : txMaxPktSz);	return dataSz;    }    protected final void checkRxWindowSize(int len) {	rxCurrWinSz -= len;	if(rxCurrWinSz < 0) {	    connection.fatalDisconnect(SSH2.DISCONNECT_PROTOCOL_ERROR,				       "Peer overflowed window");	} else if(rxCurrWinSz <= (rxInitWinSz >>> 1)) {	    // ACK on >= 50% of window received	    SSH2TransportPDU pdu =		SSH2TransportPDU.createOutgoingPacket(SSH2.MSG_CHANNEL_WINDOW_ADJUST);	    pdu.writeInt(peerChanId);	    pdu.writeInt(rxInitWinSz - rxCurrWinSz);	    transmit(pdu);	    rxCurrWinSz = rxInitWinSz; 	}    }    protected void data(SSH2TransportPDU pdu) {	if(rxChanIsQueued) {	    rxQueue.putLast(pdu);	} else {	    try {		rxWrite(pdu);	    } catch (IOException e) {		connection.getLog().error("SSH2StreamChannel",					  "data",					  e.toString());		rxClosing();	    }	}    }    protected void openConfirmationImpl(SSH2TransportPDU pdu) {	startStreams();    }    protected boolean openFailureImpl(int reasonCode, String reasonText,				   String langTag) {	// Just return false since we don't want to keep the channel,	// handle in derived class if needed	return false;    }    protected synchronized void windowAdjustImpl(int inc) {	txCurrWinSz += inc;	this.notify();    }    protected void eofImpl() {	if(rxChanIsQueued) {	    rxQueue.setBlocking(false);	} else {	    rxClosing();	}    }    protected void closeImpl() {	eofImpl();    }    /**     * Called when no more data can be written to the channel.     */    protected void outputClosed() {	// Do nothing, handle in derived class if needed    }    protected void handleRequestImpl(String type, boolean wantReply,				     SSH2TransportPDU pdu) {	// Do nothing, handle in derived class if needed    }    /**     * Create the transmitter and receiver threads.     */    protected void createStreams() {	if(rxChanIsQueued) {	    receiver = new Thread(new Runnable() {		    public void run() {			channelReceiveLoop();		    }		}, "SSH2StreamRX_" + getType() + "_" + channelId);	    receiver.setDaemon(false);	    rxQueue =		new Queue(connection.getPreferences().			  getIntPreference(SSH2Preferences.QUEUE_DEPTH),			  connection.getPreferences().			  getIntPreference(SSH2Preferences.QUEUE_HIWATER));	}	transmitter = new Thread(new Runnable() {		public void run() {		    channelTransmitLoop();		}	    }, "SSH2StreamTX_" + getType() + "_" + channelId);	transmitter.setDaemon(false);    }    /**     * Starts the transmitter and receiver threads.     */    protected void startStreams() {	transmitter.start();	if(rxChanIsQueued) {	    receiver.start();	}    }    public void waitUntilClosed() {	super.waitUntilClosed();	if(rxChanIsQueued) {	    try { receiver.join(); } catch (InterruptedException e) { }	}    }}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?