自己实现远程传输文件类

4 篇文章 0 订阅
3 篇文章 0 订阅

标题

自己实现远程传输文件类

MyScp.java

package com;

public class MyScp {
	
	public static void main(String[] args) {
		new MyScp();
	}

	public MyScp() {
		super();
		new Ui();
	}
	

}


Ui.java

package com;

import java.awt.Container;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.TransferHandler;
import javax.swing.WindowConstants;

public class Ui extends JFrame implements ActionListener{

	private static final long serialVersionUID = 1L;
	private JTextField text;
	private String defaultFilePath;
	private JButton buttonS1;
	private JButton buttonS2;
	private JButton buttonS3;
	private JButton buttonS4;
	private JButton buttonS5;
	private JButton buttonS6;
	
	public Ui() {
		this.setTitle("POC");	 	  //创建窗口
		this.setSize(300, 200);  		 //设置窗口大小
		this.setLocationRelativeTo(null);//设置窗口相对与指定组件的位置,null表示在中间
		this.setResizable(false);		//不能改变窗口大小
		this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);//窗口关闭时退出程序
		
		Container con_GridLayout = getContentPane();
		JPanel panelBorderlayout = new JPanel();
		JPanel panel_FlowLayout = new JPanel();
		
		con_GridLayout.setLayout(new GridLayout(2,1)); //2行3列的布局
		panelBorderlayout.setLayout(new BorderLayout());             //设置空布局,即绝对布局
		panel_FlowLayout.setLayout(new FlowLayout()); //流式布局
		//FlowLayout.setAlignment(FlowLayout.LEFT);//设置JPanel中控件的对齐方式	
		
		text = new JTextField(30);
		text.setText("/Users/holmes/Desktop/");
		//拖动文件显示文件路径
		text.setTransferHandler(new TransferHandler(){
			private static final long serialVersionUID = 1L; 
			public boolean importData(JComponent comp, Transferable t) {
				try {
					Object o = t.getTransferData(DataFlavor.javaFileListFlavor);
					String filepath = o.toString();
					if(filepath.startsWith("[")) {
						filepath = filepath.substring(1);
					}
					if(filepath.endsWith("]")) {
						filepath = filepath.substring(0, filepath.length() - 1);
					}
					text.setText(filepath);
					defaultFilePath = text.getText();
					return true;
				}catch(Exception e) {
					
				}
				return false;
			}
			public boolean canImport(JComponent comp, DataFlavor[] flavors) {
				for(int i = 0; i < flavors.length; i++) {
					if(DataFlavor.javaFileListFlavor.equals(flavors[i])) {
						return true;
					}
				}
				return false;
			}
		});
		
		
		buttonS1 = new JButton("S1");
		buttonS2 = new JButton("S2");
		buttonS3 = new JButton("S3");
		buttonS4 = new JButton("OTP");
		buttonS5 = new JButton("Mars Cal");
		buttonS6 = new JButton("Mars Test");
		buttonS1.addActionListener(this);
		buttonS2.addActionListener(this);
		buttonS3.addActionListener(this);
		buttonS4.addActionListener(this);
		buttonS5.addActionListener(this);
		buttonS6.addActionListener(this);

	
		
		panelBorderlayout.add(text,BorderLayout.CENTER);
		
		panel_FlowLayout.add(buttonS1);
		panel_FlowLayout.add(buttonS2);
		panel_FlowLayout.add(buttonS3);
		panel_FlowLayout.add(buttonS4);
		panel_FlowLayout.add(buttonS5);
		panel_FlowLayout.add(buttonS6);

	
		con_GridLayout.add(panelBorderlayout);
		con_GridLayout.add(panel_FlowLayout);
		
		//设置窗口为可见的
		this.setVisible(true);
	}
	@Override
	public void actionPerformed(ActionEvent e) {
		if(e.getSource() == buttonS1)
		{
			// 创建线程对象,采用匿名内部类方式。
	        Thread t = new Thread(new Runnable(){
	            @Override
	            public void run() {
	            	System.out.println(defaultFilePath);
	            	new UiStationType(0,0,0,"S1",defaultFilePath);
	            	
	            }
	        });
	        // 启动线程
	        t.start();
		}
		else if(e.getSource() == buttonS2)
		{
			// 创建线程对象,采用匿名内部类方式。
	        Thread t = new Thread(new Runnable(){
	            @Override
	            public void run() {
	            	new UiStationType(1,302, 0, "S2",defaultFilePath);
	            }
	        });
	        // 启动线程
	        t.start();
		}
		else if(e.getSource() == buttonS3)
		{
			// 创建线程对象,采用匿名内部类方式。
	        Thread t = new Thread(new Runnable(){
	            @Override
	            public void run() {
	            	new UiStationType(2, 604, 0, "S3",defaultFilePath);
	            }
	        });
	        // 启动线程
	        t.start();
		}
		else if(e.getSource() == buttonS4)
		{
			// 创建线程对象,采用匿名内部类方式。
	        Thread t = new Thread(new Runnable(){
	            @Override
	            public void run() {
	            	new UiStationType(3, 906, 0, "OTP",defaultFilePath);
	            }
	        });
	        // 启动线程
	        t.start();
		}
		else if(e.getSource() == buttonS5)
		{
			// 创建线程对象,采用匿名内部类方式。
	        Thread t = new Thread(new Runnable(){
	            @Override
	            public void run() {
	            	new UiStationType(4, 1208, 0, "Mars Cal",defaultFilePath);
	            }
	        });
	        // 启动线程
	        t.start();
		}
		else if(e.getSource() == buttonS6)
		{
			// 创建线程对象,采用匿名内部类方式。
	        Thread t = new Thread(new Runnable(){
	            @Override
	            public void run() {
	            	new UiStationType(5, 1510, 0, "Mars Test",defaultFilePath);
	            }
	        });
	        // 启动线程
	        t.start();
		}
	}
	
}


UiStationType.java

package com;

import java.awt.Container;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.BorderLayout;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.TransferHandler;

public class UiStationType extends JFrame implements ActionListener{

	private static final long serialVersionUID = 1L;
	private int row;						//ip数据在表格中的列号
	private String str;
	private String cmd;
	private JButton button_scp;				//上传按钮
	private JButton button_unzip;			//解压按钮
	private JButton button_panduanfile;		//判断文件存不存在按钮
	private JButton button_delete;			//删除按钮
	private JTextField textFilePath;		//文件文本框
	private String localFilePath;			//本地电脑文件路径
	private String remoteFilePath;			//远程电脑文件路径
	private String Wipasxnext;				//远程电脑文件名
	private JLabel labelFilePath;			//文件路径标签
	private int x;							//定位窗口位置x
	private int y;							//定位窗口位置y
	public UiStationType(int row, int x, int y, String str, String defaultFilePath) {
		this.str = str;
		this.x = x;
		this.y = y;
		this.setTitle(str);	 	  //创建窗口
		this.row = row;
		this.setSize(300, 250);  		 //设置窗口大小
		this.setLocation(x,y);
		//this.setLocationRelativeTo(null);//设置窗口相对与指定组件的位置,null表示在中间
		this.setResizable(false);		//不能改变窗口大小
		//this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);//窗口关闭时退出程序
		
		Container con_GridLayout = getContentPane();  	//获取控制面板
		JPanel panelBorderlayout = new JPanel();		//新建面板
		JPanel panel_FlowLayout = new JPanel();			//新建面板
		
		con_GridLayout.setLayout(new GridLayout(2,1)); 			//设置2行3列的布局
		panelBorderlayout.setLayout(new BorderLayout());        //设置边框布局
		panel_FlowLayout.setLayout(new FlowLayout()); 			//设置流式布局
		
		button_scp = new JButton("上传");
		button_unzip = new JButton("解压");
		button_delete = new JButton("删除");
		button_panduanfile = new JButton("判断文件存不存在");		
		button_scp.addActionListener(this);
		button_unzip.addActionListener(this);
		button_delete.addActionListener(this);
		button_panduanfile.addActionListener(this);
		
		textFilePath = new JTextField(30);	//文件路径文本框
		textFilePath.setText(""+defaultFilePath+"");
		labelFilePath = new JLabel("文件路径");
		localFilePath = "";
		remoteFilePath = "";
		
		//拖动文件显示文件路径
		textFilePath.setTransferHandler(new TransferHandler(){
			private static final long serialVersionUID = 1L; 
			public boolean importData(JComponent comp, Transferable t) {
				try {
					Object o = t.getTransferData(DataFlavor.javaFileListFlavor);
					String filepath = o.toString();
					if(filepath.startsWith("[")) {
						filepath = filepath.substring(1);
					}
					if(filepath.endsWith("]")) {
						filepath = filepath.substring(0, filepath.length() - 1);
					}
					textFilePath.setText(filepath);
					localFilePath = filepath;	  //设置本地电脑文件路径
					setremoteFilePath(filepath); //设置远程电脑文件路径
					return true;
				}catch(Exception e) {
					
				}
				return false;
			}
			public boolean canImport(JComponent comp, DataFlavor[] flavors) {
				for(int i = 0; i < flavors.length; i++) {
					if(DataFlavor.javaFileListFlavor.equals(flavors[i])) {
						return true;
					}
				}
				return false;
			}
		});
		
		
		panelBorderlayout.add(labelFilePath,BorderLayout.WEST);
		panelBorderlayout.add(textFilePath,BorderLayout.CENTER);
		
		panel_FlowLayout.add(button_scp);
		panel_FlowLayout.add(button_unzip);
		panel_FlowLayout.add(button_delete);
		panel_FlowLayout.add(button_panduanfile);
	
		con_GridLayout.add(panelBorderlayout);
		con_GridLayout.add(panel_FlowLayout);
		
		//设置窗口为可见的
		this.setVisible(true);
	}
	@Override
	public void actionPerformed(ActionEvent e) {
		if(e.getSource() == button_scp)
		{
			//获取文本框中文件的路径
			remoteFilePath = textFilePath.getText();
			//把获取文本框中的文件路径变为远程电脑文件路径
			setremoteFilePath(remoteFilePath);
			// 创建线程对象,采用匿名内部类方式。
	        Thread t = new Thread(new Runnable(){
	            @Override
	            public void run() {
	            	if(Wipasxnext.equals("WiPASXNext.zip")) {
	            		new ActionScp(localFilePath, remoteFilePath, Wipasxnext, row, str, x, y).mainScp();
	            	}else {
	            		new ActionScp(localFilePath, remoteFilePath, Wipasxnext, row, str, x, y).scp();
	            	}
	            }
	        });
	        // 启动线程
	        t.start();
		}
		else if(e.getSource() == button_panduanfile)
		{
			//获取文本框中文件的路径
			remoteFilePath = textFilePath.getText();
			//把获取文本框中的文件路径变为远程电脑文件路径
			setremoteFilePath(remoteFilePath);
			cmd = "[ -a "+remoteFilePath+" ] && echo OK";
			// 创建线程对象,采用匿名内部类方式。
	        Thread t = new Thread(new Runnable(){
	            @Override
	            public void run() {
	            	new ActionSession(cmd,str, x, y, row);
	            }
	        });
	        // 启动线程
	        t.start();
		}
		else if(e.getSource() == button_unzip)
		{
			//获取文本框中文件的路径
			remoteFilePath = textFilePath.getText();
			//把获取文本框中的文件路径变为远程电脑文件路径
			setremoteFilePath(remoteFilePath);
			if(Wipasxnext.equals("WiPASXNext.zip")) {
				cmd = "unzip -o -d /Users/gdlocal/Desktop "+remoteFilePath+""; 
			}else {
				cmd = "unzip -n -d /Users/gdlocal/Desktop "+remoteFilePath+""; 
			}
			//cmd = "open "+remoteFilePath+"";
			// 创建线程对象,采用匿名内部类方式。
	        Thread t = new Thread(new Runnable(){
	            @Override
	            public void run() {
	            	new ActionSession(cmd, str, x, y, row);
	            }
	        });
	        // 启动线程
	        t.start();
		}
		else if(e.getSource() == button_delete)
		{
			//获取文本框中文件的路径
			remoteFilePath = textFilePath.getText();
			//把获取文本框中的文件路径变为远程电脑文件路径
			setremoteFilePath(remoteFilePath);
			cmd = "rm -v -rf "+remoteFilePath+"";
			// 创建线程对象,采用匿名内部类方式。
	        Thread t = new Thread(new Runnable(){
	            @Override
	            public void run() {
	               new ActionSession(cmd, str, x, y, row);
	            }
	        });
	        // 启动线程
	        t.start();
		}	
	}
	
	//设置文件名称路径
	public void setremoteFilePath(String str) {
		//获取第一个/ 的位置
		int index = str.indexOf("/");
		//根据第一个/ 的位置获得第二个/的位置
		int index_start = str.indexOf("/", index + 1);
		//根据第二个/ 的位置获得第三个/的位置
		int index_end = str.indexOf("/", index_start + 1);
		String str_start = str.substring(0,index_start + 1);//截取字符串
		String str_end = str.substring(index_end, str.length());
		//本地电脑文件路径
		localFilePath = str;
		//远程电脑文件路径
		remoteFilePath = str_start + "gdlocal" + str_end; 		
		//获取文件名
		int index_last = str.lastIndexOf("/");
		Wipasxnext = str.substring(index_last + 1,str.length());
	}
	
}


ActionScp.java

package com;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;

import javax.swing.JDialog;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;

import ch.ethz.ssh2.Connection;
//这些是调用jar包的类,已经自己实现了
//mport ch.ethz.ssh2.Connection;
//import ch.ethz.ssh2.SCPClient;
//import ch.ethz.ssh2.Session;
import ch.ethz.ssh2.StreamGobbler;
import jxl.Cell;
import jxl.Sheet;
import jxl.Workbook;
import jxl.read.biff.BiffException;
/**
 * 上传文件
 * @author holmes
 *
 */
public class ActionScp {
	private static String  DEFAULTCHART="UTF-8";
	private String pwd = "gdlocal";
	private String user = "gdlocal";
	private int port =22;
	private MyConnection conn;
	private String filePath;
	private String remoteFilePath;
	private String Wipasxnext;
	private boolean isAuthed = false;
	private MySession session;
	private MySession session2;
	private MySCPClient scp;
	private String result;
	private String [] arr;
	private int row;
	private File file;
	private Workbook wb;
	private Sheet sheet;
	public int i;          			//循环增量以及进度条的值
	private int sum;
	private int ipsum;
	private int x;							//窗口位置x
	private int y;							//窗口位置y
	private String strInformation;			//窗口标题
	
	
	public ActionScp(String filePath, String remoteFilePath,String Wipaxnext, int row, String str, int x, int y) {
		super();
		this.filePath = filePath;
		this.remoteFilePath = remoteFilePath;
		this.Wipasxnext = Wipaxnext;
		this.row = row;
		this.x = x;
		this.y = y;
		this.strInformation = str;
		arr = new String[200];
		//初始化数组
		for (int i = 0; i < arr.length; i++) { 
				arr[i] = "";
		}
		file = new File("/Users/holmes/eclipse-workspace/POC/data/ip.xls");	
		//获取IP
		s1_ip();
		new mySwing(x, y, ipsum, str);
	}

	/**
	 * 上传文件	
	 */
	public void scp()
	{	
		sum = 0;
		i = 0;
		while(arr[i] != "") {
			System.out.println(arr[i]);
			String ip = arr[i];
			//创建链接
			conn = new MyConnection(ip, port);
			//conn.connect();
			try {
				conn.connect(null, 5000, 0);	//第二个参数连接超时时间
			} catch (IOException e) {
				i = i + 1;
				sum = sum + 1;	
				e.printStackTrace();
				System.out.println(arr[i] + "连接远程电脑失败!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
				continue;
			}  
			//登陆
			try {
				isAuthed = conn.authenticateWithPassword(user, pwd);
			} catch (IOException e) {
				i = i + 1;
				sum = sum + 1;	
				e.printStackTrace();
				continue;
			}
			//获取SCPClient
			if(isAuthed) 
			{
				System.out.println( "登陆成功!");
				//打开一个会话
				try {
					session = conn.openSession();
				} catch (IOException e) {
					i = i + 1;
					sum = sum + 1;	
					e.printStackTrace();
					System.out.println("打开远程会话1失败!!!!!!!!!!!!!!!!!!!");
					continue;
				}
				//判断远程电脑文件存不存在
				//session.execCommand("[ -f /Users/gdlocal/Desktop/WiPASXNext136_886.zip ] && echo OK");
				//session.execCommand("[ -a /Users/gdlocal/Desktop/"+filename+" ] && echo OK");
				try {
					session.execCommand("[ -a "+this.remoteFilePath+" ] && echo OK");
				} catch (IOException e) {
					i = i + 1;
					sum = sum + 1;	
					System.out.println("远程会话1失败!!!!!!!!!!!!!!!!!!!");
					e.printStackTrace();
					continue;
				}
				result = processStdout(session.getStdout(), DEFAULTCHART);
				if(!result.isEmpty()) {
					i = i + 1;
					System.out.println("文件存在");
					session.close();
					continue;
				}
				session.close();
				try {
					session2 = conn.openSession();
				} catch (IOException e1) {
					i = i + 1;
					sum = sum + 1;	
					System.out.println("打开远程会话2失败!!!!!!!!!!!!!!!!!!!");
					e1.printStackTrace();
				}
				try {
					session2.execCommand("[ -a /Users/gdlocal/Desktop/ExpressWipasx/"+this.remoteFilePath+" ] && echo OK");
				} catch (IOException e) {
					i = i + 1;
					sum = sum + 1;	
					System.out.println("远程会话2失败!!!!!!!!!!!!!!!!!!!");
					e.printStackTrace();
				}
				result = "";
				result = processStdout(session2.getStdout(), DEFAULTCHART);
				if(!result.isEmpty()) {
					i = i + 1;
					System.out.println("文件存在");
					session2.close();
					continue;
				}
				session2.close();
				try {
					scp = conn.createSCPClient();
				} catch (IOException e) {
					e.printStackTrace();
				}
				//MySCPClient scp = new MySCPClient(conn);
				try {
					scp.put(this.filePath, "/Users/gdlocal/Desktop/");
					//myPut();
				} catch (IOException e) {
					i = i + 1;
					sum = sum + 1;	
					System.out.println("远程复制文件失败!!!!!!!!!!!!!!!!!!!");
					e.printStackTrace();
				}
				System.out.println(arr[i] + "成功上传文件");
				conn.close();
			}
			else 
			{
				System.out.println( arr[i] + "登陆失败!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
				sum = sum + 1;	
				conn.close();
			}	
		i = i + 1;
		}
		new information(this.x, this.y, this.sum, this.strInformation);
	}
	public String mainScp() {
		sum = 0;
		int i = 0;
		while(arr[i] != "") {
			System.out.println(arr[i]);
			result = "";
			String ip = arr[i];
			//创建连接
			conn = new MyConnection(ip, port);
			try {
				//conn.connect();
				conn.connect(null, 5000, 0);	//第二个参数连接超时时间
			} catch (IOException e) {
				i = i + 1;
				sum = sum + 1;
				e.printStackTrace();
				continue;
			}
			//登陆
			try {
				isAuthed = conn.authenticateWithPassword(user, pwd);
			} catch (IOException e) {
				i = i + 1;
				sum = sum + 1;
				e.printStackTrace();
				continue;
			}
			if(isAuthed && conn != null) {
				//打开一个会话
				try {
					session = conn.openSession();
				} catch (IOException e) {
					e.printStackTrace();
				}
				try {
					scp = conn.createSCPClient();
					//SCPClient scp = new SCPClient(conn);
				} catch (IOException e) {
					e.printStackTrace();
				}
				try {
					scp.put(this.filePath, "/Users/gdlocal/Desktop/");
				} catch (IOException e) {
					e.printStackTrace();
				}
				System.out.println(arr[i] + "成功上传文件");
				//SCPInputStream is =  scp.get("/users/gdlocal/Desktop/WiPASXNext_88.zip");
				conn.close();
				result = processStdout(session.getStdout(), DEFAULTCHART);
				System.out.println(result);
				conn.close();
			}
			else 
			{
				System.out.println(arr[i] + "登陆失败!!!!!!!!!!!!!!!!!!!!!!!!!!!");
				sum = sum + 1;
				conn.close();
			}
			i = i + 1;
		}
		JOptionPane.showMessageDialog(null, "登陆失败的数量:"+sum+" ", "帮助", 1);
		return "";
	}
	//开始myPut
	//上传数据
		private void sendFiles(MySession sess, String files, String remoteFiles, String mode) throws IOException
		{
			byte[] buffer = new byte[8192];

			OutputStream os = new BufferedOutputStream(sess.getStdin(), 40000);
			InputStream is = new BufferedInputStream(sess.getStdout(), 512);

			readResponse(is);


				File f = new File(files);

				long remain = f.length();

				String remoteName;

				if ((remoteFiles != null) && (remoteFiles != null))
					remoteName = remoteFiles;
				else
					remoteName = f.getName();

				//修改远程电脑的remoteName文件的权限
				String cline = "C" + mode + " " + remain + " " + Wipasxnext + "\n";
				System.out.println(cline);
				os.write(cline.getBytes());

				os.flush();

				readResponse(is);

				FileInputStream fis = null;

				try
				{
					fis = new FileInputStream(f);

					while (remain > 0)
					{
						int trans;
						if (remain > buffer.length)
							trans = buffer.length;
						else
							trans = (int) remain;

						if (fis.read(buffer, 0, trans) != trans)
							throw new IOException("Cannot read enough from local file " + files);

						os.write(buffer, 0, trans);

						remain -= trans;
					}
				}
				finally
				{
					if (fis != null)
						fis.close();
				}

				os.write(0);
				os.flush();

				readResponse(is);

			os.write("E\n".getBytes());
			os.flush();
		}
	public String myPut()throws IOException   {
		MySession session3 = null;
		sum = 0;
		result = "";
		i = 0;
		while(arr[i] != "") {
			System.out.println(arr[i]);
			String ip = arr[i];
			//创建连接
			conn = new MyConnection(ip, port);
			try {
				//conn.connect();
				conn.connect(null, 5000, 0);	//第二个参数连接超时时间
			} catch (IOException e) {
				i = i + 1;
				sum = sum + 1;
				e.printStackTrace();
				System.out.println(arr[i] + "连接远程电脑失败!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
				continue;
			}
			//登陆
			try {
				isAuthed = conn.authenticateWithPassword(user, pwd);
			} catch (IOException e) {
				i = i + 1;
				sum = sum + 1;
				e.printStackTrace();
				continue;
			}
			if(isAuthed && conn != null) {
				try
				{
					String cmd = "scp -t -d " + "/Users/gdlocal/Desktop";
					session3 = conn.openSession();
					session3.execCommand(cmd);
					sendFiles(session3, this.filePath, "/Users/gdlocal/Desktop/", "0600");
				}
				catch (IOException e)
				{
					throw (IOException) new IOException("Error during SCP transfer.").initCause(e);
				}
				finally
				{
					if (session3 != null)
						session3.close();
				}
				result = processStdout(session.getStdout(), DEFAULTCHART);
				System.out.println(result);
				session3.close();
				conn.close();
			}
			else 
			{
				System.out.println(arr[i] + "登陆失败!!!!!!!!!!!!!!!!!!!!!!!!!!!");
				sum = sum + 1;
				conn.close();
			}
			i = i + 1;
		}
		new information(this.x, this.y, this.sum, this.strInformation);
		return "";
	}
	
	private void readResponse(InputStream is) throws IOException
	{
		int c = is.read();

		if (c == 0)
			return;

		if (c == -1)
			throw new IOException("Remote scp terminated unexpectedly.");

		if ((c != 1) && (c != 2))
			throw new IOException("Remote scp sent illegal error code.");

		if (c == 2)
			throw new IOException("Remote scp terminated with error.");

		String err = receiveLine(is);

		throw new IOException("Remote scp terminated with error (" + err + ").");

	}
	private String receiveLine(InputStream is) throws IOException
	{
		StringBuffer sb = new StringBuffer(30);

		while (true)
		{
			/* This is a random limit - if your path names are longer, then adjust it */

			if (sb.length() > 8192)
				throw new IOException("Remote scp sent a too long line");

			int c = is.read();

			if (c < 0)
				throw new IOException("Remote scp terminated unexpectedly.");

			if (c == '\n')
				break;

			sb.append((char) c);

		}
		return sb.toString();
	}
	//反回cmd命令执行结果
	private static String processStdout(InputStream in, String charset)
	{
		InputStream stdout = new StreamGobbler(in);
		StringBuffer buffer = new StringBuffer();
		try {
			@SuppressWarnings("resource")
			BufferedReader br = new BufferedReader(new InputStreamReader(stdout, charset));
			String line = null;
			while((line = br.readLine()) != null) {
				buffer.append(line+"\n");
			}
		} catch (UnsupportedEncodingException e) {
			System.out.println("解析脚本出错");
			//e.printStackTrace();
		} catch (IOException e) {
			System.out.println("解析脚本出错");
			//e.printStackTrace();
		}
		return buffer.toString();
	}
	
	public void s1_ip() {
		try {
			wb = Workbook.getWorkbook(file);
		} catch (BiffException | IOException e) {
			e.printStackTrace();
		}
		sheet = wb.getSheet("Sheet1"); 
		for (int i = 1; i < sheet.getRows(); i++) {//i行
			Cell cell = sheet.getCell(row, i);
			if(cell.getContents() == "") {
				continue;
			}
			arr[i-1] = cell.getContents();
			ipsum = ipsum + 1;
		}
		System.out.println("ipsum:"+ipsum);
		wb.close();
	}
	
	/**
	 * 内部类
	 * 进度条
	 * @author holmes
	 *
	 */
	public class mySwing extends JDialog {
		/**
		 * 弹窗
		 */
		private static final long serialVersionUID = 1L;
		public JProgressBar progressbar;  //创建进度条
		
		mySwing(int x, int y, int Maximum, String str) {
			this.setTitle(""+str+"正在下载...");
			this.setSize(150,100);//设置窗口大小
			this.setLocation(x,y+275);
			progressbar = new JProgressBar();  						//创建进度条
			progressbar.setMaximum(Maximum);
			progressbar.setStringPainted(true);   					//显示当前进度条信息
			progressbar.setBorderPainted(false); 					//设置进度条边框不显示
			progressbar.setForeground(new Color(0, 210, 40)); 		//设置进度条的前景色
			progressbar.setBackground(new Color(188, 190, 194));  	//设置进度条的背景颜色
			
			JPanel My_panel = new JPanel(new BorderLayout());//面板
			My_panel.add(progressbar, BorderLayout.SOUTH);
			this.setContentPane(My_panel);//窗口添加面板
			//this.setResizable(false);
			this.setVisible(true);//设置窗口为可见
			new Thread(new myRepaint()).start();  //进度条线程开始
		}
		private class myRepaint implements Runnable{
			//另一种方法,创建一个线程运行进度条
			public void run() {
				while(true)
				{
					Dimension d = progressbar.getSize();
					//设置进度条的值
					progressbar.setValue(i);  
					Rectangle rect = new Rectangle(0, 0, d.width, d.height);
					progressbar.setValue(i);
					progressbar.paintImmediately(rect);
					//repaint();
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}	
	}
	
	

	

}


ActionSession.java

package com;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;

import javax.swing.JDialog;
import javax.swing.JPanel;
import javax.swing.JProgressBar;

import ch.ethz.ssh2.Connection;
import ch.ethz.ssh2.Session;
import ch.ethz.ssh2.StreamGobbler;
import jxl.Cell;
import jxl.Sheet;
import jxl.Workbook;
import jxl.read.biff.BiffException;

/**
 * 远程cmd命令
 * @return
 */
public class ActionSession {
	private static String  DEFAULTCHART="UTF-8";
	private String pwd = "gdlocal";
	private String user = "gdlocal";
	private int port =22;
	private MyConnection conn;
	private MySession session;
	private boolean isAuthed = false;
	private String cmd;
	private String result;
	private String [] arr;
	private int row;
	private File file;
	private Workbook wb;
	private Sheet sheet;
	public int i;          //循环增量以及进度条值
	private int ipsum;		//ip数量
	private int sum = 0;    //连接电脑失败的数量
	private int x;							//窗口位置x
	private int y;							//窗口位置y
	private String strInformation;			//窗口标题
	

	public ActionSession(String cmd, String str, int x, int y, int row) {
		super();
		this.cmd = cmd;
		this.row = row;
		this.x = x;
		this.y = y;
		this.strInformation = str;
		arr = new String[200];
		//初始化数组
		for (int i = 0; i < arr.length; i++) { 
				arr[i] = "";
		}
		file = new File("/Users/holmes/eclipse-workspace/POC/data/ip.xls");	
		//获取IP
		s1_ip();
		new mySwing(x, y, ipsum, str);
		action();
	}
	public String action() {
		sum = 0;
		result = "";
		i = 0;
		while(arr[i] != "") {
			System.out.println(arr[i]);
			String ip = arr[i];
			//创建连接
			conn = new MyConnection(ip, port);
			try {
				//conn.connect();
				conn.connect(null, 5000, 0);	//第二个参数连接超时时间
			} catch (IOException e) {
				i = i + 1;
				sum = sum + 1;
				e.printStackTrace();
				System.out.println(arr[i] + "连接远程电脑失败!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
				continue;
			}
			//登陆
			try {
				isAuthed = conn.authenticateWithPassword(user, pwd);
			} catch (IOException e) {
				i = i + 1;
				sum = sum + 1;
				e.printStackTrace();
				continue;
			}
			if(isAuthed && conn != null) {
				//打开一个会话
				try {
					session = conn.openSession();
				} catch (IOException e) {
					i = i + 1;
					sum = sum + 1;	
					e.printStackTrace();
					System.out.println("打开远程会话1失败!!!!!!!!!!!!!!!!!!!");
					continue;
				}
				try {
					session.execCommand(cmd);
				} catch (IOException e) {
					i = i + 1;
					sum = sum + 1;	
					System.out.println("远程会话1失败!!!!!!!!!!!!!!!!!!!");
					e.printStackTrace();
					continue;
				}
				result = processStdout(session.getStdout(), DEFAULTCHART);
				System.out.println(result);
				session.close();
				conn.close();
			}
			else 
			{
				System.out.println(arr[i] + "登陆失败!!!!!!!!!!!!!!!!!!!!!!!!!!!");
				sum = sum + 1;
				conn.close();
			}
			i = i + 1;
		}
		new information(this.x, this.y, this.sum, this.strInformation);
		return "";
	}
	//反回cmd命令执行结果
	private static String processStdout(InputStream in, String charset)
	{
		InputStream stdout = new StreamGobbler(in);
		StringBuffer buffer = new StringBuffer();
		try {
			@SuppressWarnings("resource")
			BufferedReader br = new BufferedReader(new InputStreamReader(stdout, charset));
			String line = null;
			while((line = br.readLine()) != null) {
				//buffer.append(line+"\n");
				buffer.append(line);
			}
		} catch (UnsupportedEncodingException e) {
			System.out.println("解析脚本出错");
			//e.printStackTrace();
		} catch (IOException e) {
			System.out.println("解析脚本出错");
			//e.printStackTrace();
		}
		String result = buffer.toString();
		if(result.equalsIgnoreCase("OK")) {
			result = "OK文件存在";
		}else if(result.indexOf("Archive:") != -1) {
			result = "解压成功";
		}else if(result.indexOf("/Users/gdlocal") != -1) {
			result = "删除成功";
		}else {
			result = "文件不存在或者解压失败或者删除失败!!!!!!";
		}
		
		return result;
	}
	
	
	public void s1_ip() {
		try {
			wb = Workbook.getWorkbook(file);
		} catch (BiffException | IOException e) {
			e.printStackTrace();
		}
		sheet = wb.getSheet("Sheet1"); 
		for (int i = 1; i < sheet.getRows(); i++) {//i行
			Cell cell = sheet.getCell(row, i);
			if(cell.getContents() == "") {
				continue;
			}
			arr[i-1] = cell.getContents();
			ipsum = ipsum + 1;
		}
		System.out.println("ipsum:"+ipsum);
		wb.close();
	}
	
	
	/**
	 * 内部类
	 * 进度条
	 * @author holmes
	 *
	 */
	public class mySwing extends JDialog {
		/**
		 * 弹窗
		 */
		private static final long serialVersionUID = 1L;
		public JProgressBar progressbar;  //创建进度条
		
		mySwing(int x, int y, int Maximum, String str) {
			this.setTitle(""+str+"正在下载...");
			this.setSize(150,100);//设置窗口大小
			this.setLocation(x,y+275);
			progressbar = new JProgressBar();  						//创建进度条
			progressbar.setMaximum(Maximum);
			progressbar.setStringPainted(true);   					//显示当前进度条信息
			progressbar.setBorderPainted(false); 					//设置进度条边框不显示
			progressbar.setForeground(new Color(0, 210, 40)); 		//设置进度条的前景色
			progressbar.setBackground(new Color(188, 190, 194));  	//设置进度条的背景颜色
			
			JPanel My_panel = new JPanel(new BorderLayout());//面板
			My_panel.add(progressbar, BorderLayout.SOUTH);
			this.setContentPane(My_panel);//窗口添加面板
			//this.setResizable(false);
			this.setVisible(true);//设置窗口为可见
			new Thread(new myRepaint()).start();  //进度条线程开始
		}
		private class myRepaint implements Runnable{
			//另一种方法,创建一个线程运行进度条
			public void run() {
				while(true)
				{
					Dimension d = progressbar.getSize();
					//设置进度条的值
					progressbar.setValue(i);  
					Rectangle rect = new Rectangle(0, 0, d.width, d.height);
					progressbar.setValue(i);
					progressbar.paintImmediately(rect);
					//repaint();
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				
				}
			}
		}	
	}
	
}


information.java

package com;

import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.GridLayout;

public class information extends JDialog{
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private JLabel label;
	private JLabel label2;
	
	public information(int x, int y, int sum, String str) {
		this.setTitle(str);	 	  //创建窗口
		this.setSize(150, 100);  		 //设置窗口大小
		this.setLocation(x+150, y+275);
		this.setResizable(false);		//不能改变窗口大小
		label = new JLabel("传输完成");
		label2 = new JLabel("失败的数量:"+sum+"");
		Container con_GridLayout = getContentPane();
		JPanel panelBorderlayout = new JPanel();
		con_GridLayout.setLayout(new GridLayout(1,1)); 		//1行1列的布局
		panelBorderlayout.setLayout(new BorderLayout());   //设置空布局,即绝对布局
		panelBorderlayout.add(label,BorderLayout.NORTH);
		panelBorderlayout.add(label2,BorderLayout.CENTER);
		con_GridLayout.add(panelBorderlayout);
		//设置窗口为可见的
		this.setVisible(true);
		
	}
}


MyConnection.java

package com;


import java.io.CharArrayWriter;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.security.SecureRandom;
import java.util.Vector;

import ch.ethz.ssh2.ConnectionInfo;
import ch.ethz.ssh2.ConnectionMonitor;
import ch.ethz.ssh2.DHGexParameters;
import ch.ethz.ssh2.HTTPProxyException;
import ch.ethz.ssh2.InteractiveCallback;
import ch.ethz.ssh2.LocalPortForwarder;
import ch.ethz.ssh2.ProxyData;
import ch.ethz.ssh2.ServerHostKeyVerifier;
import ch.ethz.ssh2.auth.AuthenticationManager;
import ch.ethz.ssh2.channel.ChannelManager;
import ch.ethz.ssh2.crypto.CryptoWishList;
import ch.ethz.ssh2.crypto.cipher.BlockCipherFactory;
import ch.ethz.ssh2.crypto.digest.MAC;
import ch.ethz.ssh2.transport.KexManager;
import ch.ethz.ssh2.transport.TransportManager;
import ch.ethz.ssh2.util.TimeoutService;
import ch.ethz.ssh2.util.TimeoutService.TimeoutToken;

/**
 * A <code>Connection</code> is used to establish an encrypted TCP/IP
 * connection to a SSH-2 server.
 * <p>
 * Typically, one
 * <ol>
 * <li>creates a {@link #Connection(String) Connection} object.</li>
 * <li>calls the {@link #connect() connect()} method.</li>
 * <li>calls some of the authentication methods (e.g., {@link #authenticateWithPublicKey(String, File, String) authenticateWithPublicKey()}).</li>
 * <li>calls one or several times the {@link #openSession() openSession()} method.</li>
 * <li>finally, one must close the connection and release resources with the {@link #close() close()} method.</li>
 * </ol>
 * 
 * @author Christian Plattner, plattner@inf.ethz.ch
 * @version $Id: Connection.java,v 1.28 2006/09/12 15:35:26 cplattne Exp $
 */

public class MyConnection
{
	/**
	 * The identifier presented to the SSH-2 server.
	 */
	public final static String identification = "Ganymed Build_210";

	/* Will be used to generate all random data needed for the current connection.
	 * Note: SecureRandom.nextBytes() is thread safe.
	 */

	private SecureRandom generator;

	/**
	 * Unless you know what you are doing, you will never need this.
	 * 
	 * @return The list of supported cipher algorithms by this implementation.
	 */
	public static synchronized String[] getAvailableCiphers()
	{
		return BlockCipherFactory.getDefaultCipherList();
	}

	/**
	 * Unless you know what you are doing, you will never need this.
	 * 
	 * @return The list of supported MAC algorthims by this implementation.
	 */
	public static synchronized String[] getAvailableMACs()
	{
		return MAC.getMacList();
	}

	/**
	 * Unless you know what you are doing, you will never need this.
	 * 
	 * @return The list of supported server host key algorthims by this implementation.
	 */
	public static synchronized String[] getAvailableServerHostKeyAlgorithms()
	{
		return KexManager.getDefaultServerHostkeyAlgorithmList();
	}

	private AuthenticationManager am;		//身份验证管理器

	private boolean authenticated = false;
	private ChannelManager cm;				//信道管理器

	private CryptoWishList cryptoWishList = new CryptoWishList();   //加密

	private DHGexParameters dhgexpara = new DHGexParameters();

	private final String hostname;

	private final int port;

	private TransportManager tm;			//传输管理器

	private boolean tcpNoDelay = false;

	private ProxyData proxyData = null;

	@SuppressWarnings("rawtypes")
	private Vector connectionMonitors = new Vector();

	/**
	 * Prepares a fresh <code>Connection</code> object which can then be used
	 * to establish a connection to the specified SSH-2 server.
	 * <p>
	 * Same as {@link #Connection(String, int) Connection(hostname, 22)}. 
	 * 
	 * @param hostname the hostname of the SSH-2 server.
	 */
	public MyConnection(String hostname)
	{
		this(hostname, 22);
	}

	/**
	 * Prepares a fresh <code>Connection</code> object which can then be used
	 * to establish a connection to the specified SSH-2 server.
	 * 
	 * @param hostname
	 *            the host where we later want to connect to.
	 * @param port
	 *            port on the server, normally 22.
	 */
	public MyConnection(String hostname, int port)
	{
		this.hostname = hostname;
		this.port = port;
	}

	/**
	 * After a successful connect, one has to authenticate oneself. This method
	 * is based on DSA (it uses DSA to sign a challenge sent by the server).
	 * <p>
	 * If the authentication phase is complete, <code>true</code> will be
	 * returned. If the server does not accept the request (or if further
	 * authentication steps are needed), <code>false</code> is returned and
	 * one can retry either by using this or any other authentication method
	 * (use the <code>getRemainingAuthMethods</code> method to get a list of
	 * the remaining possible methods).
	 * 
	 * @param user
	 *            A <code>String</code> holding the username.
	 * @param pem
	 *            A <code>String</code> containing the DSA private key of the
	 *            user in OpenSSH key format (PEM, you can't miss the
	 *            "-----BEGIN DSA PRIVATE KEY-----" tag). The string may contain
	 *            linefeeds.
	 * @param password
	 *            If the PEM string is 3DES encrypted ("DES-EDE3-CBC"), then you
	 *            must specify the password. Otherwise, this argument will be
	 *            ignored and can be set to <code>null</code>.
	 * 
	 * @return whether the connection is now authenticated.
	 * @throws IOException
	 * 
	 * @deprecated You should use one of the {@link #authenticateWithPublicKey(String, File, String) authenticateWithPublicKey()}
	 * 		      methods, this method is just a wrapper for it and will
	 *            disappear in future builds.
	 * 
	 */
	public synchronized boolean authenticateWithDSA(String user, String pem, String password) throws IOException
	{
		if (tm == null)
			throw new IllegalStateException("Connection is not established!");

		if (authenticated)
			throw new IllegalStateException("Connection is already authenticated!");

		if (am == null)
			am = new AuthenticationManager(tm);

		if (cm == null)
			cm = new ChannelManager(tm);

		if (user == null)
			throw new IllegalArgumentException("user argument is null");

		if (pem == null)
			throw new IllegalArgumentException("pem argument is null");

		authenticated = am.authenticatePublicKey(user, pem.toCharArray(), password, getOrCreateSecureRND());

		return authenticated;
	}

	/**
	 * A wrapper that calls {@link #authenticateWithKeyboardInteractive(String, String[], InteractiveCallback)
	 * authenticateWithKeyboardInteractivewith} a <code>null</code> submethod list.
	 * 
	 * @param user
	 *            A <code>String</code> holding the username.
	 * @param cb
	 *            An <code>InteractiveCallback</code> which will be used to
	 *            determine the responses to the questions asked by the server.
	 * @return whether the connection is now authenticated.
	 * @throws IOException
	 */
	public synchronized boolean authenticateWithKeyboardInteractive(String user, InteractiveCallback cb)
			throws IOException
	{
		return authenticateWithKeyboardInteractive(user, null, cb);
	}

	/**
	 * After a successful connect, one has to authenticate oneself. This method
	 * is based on "keyboard-interactive", specified in
	 * draft-ietf-secsh-auth-kbdinteract-XX. Basically, you have to define a
	 * callback object which will be feeded with challenges generated by the
	 * server. Answers are then sent back to the server. It is possible that the
	 * callback will be called several times during the invocation of this
	 * method (e.g., if the server replies to the callback's answer(s) with
	 * another challenge...)
	 * <p>
	 * If the authentication phase is complete, <code>true</code> will be
	 * returned. If the server does not accept the request (or if further
	 * authentication steps are needed), <code>false</code> is returned and
	 * one can retry either by using this or any other authentication method
	 * (use the <code>getRemainingAuthMethods</code> method to get a list of
	 * the remaining possible methods).
	 * <p>
	 * Note: some SSH servers advertise "keyboard-interactive", however, any
	 * interactive request will be denied (without having sent any challenge to
	 * the client).
	 * 
	 * @param user
	 *            A <code>String</code> holding the username.
	 * @param submethods
	 *            An array of submethod names, see
	 *            draft-ietf-secsh-auth-kbdinteract-XX. May be <code>null</code>
	 *            to indicate an empty list.
	 * @param cb
	 *            An <code>InteractiveCallback</code> which will be used to
	 *            determine the responses to the questions asked by the server.
	 * 
	 * @return whether the connection is now authenticated.
	 * @throws IOException
	 */
	public synchronized boolean authenticateWithKeyboardInteractive(String user, String[] submethods,
			InteractiveCallback cb) throws IOException
	{
		if (cb == null)
			throw new IllegalArgumentException("Callback may not ne NULL!");

		if (tm == null)
			throw new IllegalStateException("Connection is not established!");

		if (authenticated)
			throw new IllegalStateException("Connection is already authenticated!");

		if (am == null)
			am = new AuthenticationManager(tm);

		if (cm == null)
			cm = new ChannelManager(tm);

		if (user == null)
			throw new IllegalArgumentException("user argument is null");

		authenticated = am.authenticateInteractive(user, submethods, cb);

		return authenticated;
	}

	/**
	 * After a successfull connect, one has to authenticate oneself. This method
	 * sends username and password to the server.
	 * <p>
	 * If the authentication phase is complete, <code>true</code> will be
	 * returned. If the server does not accept the request (or if further
	 * authentication steps are needed), <code>false</code> is returned and
	 * one can retry either by using this or any other authentication method
	 * (use the <code>getRemainingAuthMethods</code> method to get a list of
	 * the remaining possible methods).
	 * <p>
	 * Note: if this method fails, then please double-check that it is actually
	 * offered by the server (use {@link #getRemainingAuthMethods(String) getRemainingAuthMethods()}.
	 * <p>
	 * Often, password authentication is disabled, but users are not aware of it.
	 * Many servers only offer "publickey" and "keyboard-interactive". However,
	 * even though "keyboard-interactive" *feels* like password authentication
	 * (e.g., when using the putty or openssh clients) it is *not* the same mechanism.
	 * 成功连接后,必须对自己进行身份验证。此方法将用户名和密码发送到服务器。
	 * 如果认证阶段完成,将返回true。如果服务器不接受请求(或者如果需要进一步的身份验证步骤),
	 * 则返回false并且可以使用身份验证方法或任何其他身份验证方法重试(使用getRemainingAuthMethods方法来获取剩余可能方法的列表
	 * 注意:如果此方法失败,请仔细检查它是否实际由服务器提供(使用{@link #getRemainingAuthMethods(String) getRemainingAuthMethods()})
	 * 通常,密码身份验证被禁用,但用户并不知道。
	 * 许多服务器只提供“publickey”和“keyboard-interactive”。然而,即使“键盘交互”感觉像密码验证
	 * (例如,当使用putty或openssh客户端时)它不是相同的机制
	 * @param user
	 * @param password
	 * @return if the connection is now authenticated.
	 * @throws IOException
	 */
	public synchronized boolean authenticateWithPassword(String user, String password) throws IOException
	{
		if (tm == null)
			throw new IllegalStateException("Connection is not established!");

		if (authenticated)
			throw new IllegalStateException("Connection is already authenticated!");

		if (am == null)
			am = new AuthenticationManager(tm);

		if (cm == null)
			cm = new ChannelManager(tm);

		if (user == null)
			throw new IllegalArgumentException("user argument is null");

		if (password == null)
			throw new IllegalArgumentException("password argument is null");

		authenticated = am.authenticatePassword(user, password);

		return authenticated;
	}

	/**
	 * After a successful connect, one has to authenticate oneself.
	 * The authentication method "publickey" works by signing a challenge
	 * sent by the server. The signature is either DSA or RSA based - it
	 * just depends on the type of private key you specify, either a DSA
	 * or RSA private key in PEM format. And yes, this is may seem to be a
	 * little confusing, the method is called "publickey" in the SSH-2 protocol
	 * specification, however since we need to generate a signature, you
	 * actually have to supply a private key =).
	 * <p>
	 * The private key contained in the PEM file may also be encrypted ("Proc-Type: 4,ENCRYPTED").
	 * The library supports DES-CBC and DES-EDE3-CBC encryption, as well
	 * as the more exotic PEM encrpytions AES-128-CBC, AES-192-CBC and AES-256-CBC.
	 * <p>
	 * If the authentication phase is complete, <code>true</code> will be
	 * returned. If the server does not accept the request (or if further
	 * authentication steps are needed), <code>false</code> is returned and
	 * one can retry either by using this or any other authentication method
	 * (use the <code>getRemainingAuthMethods</code> method to get a list of
	 * the remaining possible methods).
	 * <p>
	 * NOTE PUTTY USERS: Event though your key file may start with "-----BEGIN..."
	 * it is not in the expected format. You have to convert it to the OpenSSH
	 * key format by using the "puttygen" tool (can be downloaded from the Putty
	 * website). Simply load your key and then use the "Conversions/Export OpenSSH key"
	 * functionality to get a proper PEM file.
	 * 
	 * @param user
	 *            A <code>String</code> holding the username.
	 * @param pemPrivateKey
	 *            A <code>char[]</code> containing a DSA or RSA private key of the
	 *            user in OpenSSH key format (PEM, you can't miss the
	 *            "-----BEGIN DSA PRIVATE KEY-----" or "-----BEGIN RSA PRIVATE KEY-----"
	 *            tag). The char array may contain linebreaks/linefeeds.
	 * @param password
	 *            If the PEM structure is encrypted ("Proc-Type: 4,ENCRYPTED") then
	 *            you must specify a password. Otherwise, this argument will be ignored
	 *            and can be set to <code>null</code>.
	 * 
	 * @return whether the connection is now authenticated.
	 * @throws IOException
	 */
	public synchronized boolean authenticateWithPublicKey(String user, char[] pemPrivateKey, String password)
			throws IOException
	{
		if (tm == null)
			throw new IllegalStateException("Connection is not established!");

		if (authenticated)
			throw new IllegalStateException("Connection is already authenticated!");

		if (am == null)
			am = new AuthenticationManager(tm);

		if (cm == null)
			cm = new ChannelManager(tm);

		if (user == null)
			throw new IllegalArgumentException("user argument is null");

		if (pemPrivateKey == null)
			throw new IllegalArgumentException("pemPrivateKey argument is null");

		authenticated = am.authenticatePublicKey(user, pemPrivateKey, password, getOrCreateSecureRND());

		return authenticated;
	}

	/**
	 * A convenience wrapper function which reads in a private key (PEM format, either DSA or RSA)
	 * and then calls <code>authenticateWithPublicKey(String, char[], String)</code>.
	 * <p>
	 * NOTE PUTTY USERS: Event though your key file may start with "-----BEGIN..."
	 * it is not in the expected format. You have to convert it to the OpenSSH
	 * key format by using the "puttygen" tool (can be downloaded from the Putty
	 * website). Simply load your key and then use the "Conversions/Export OpenSSH key"
	 * functionality to get a proper PEM file.
	 * 
	 * @param user
	 *            A <code>String</code> holding the username.
	 * @param pemFile
	 *            A <code>File</code> object pointing to a file containing a DSA or RSA
	 *            private key of the user in OpenSSH key format (PEM, you can't miss the
	 *            "-----BEGIN DSA PRIVATE KEY-----" or "-----BEGIN RSA PRIVATE KEY-----"
	 *            tag).
	 * @param password
	 *            If the PEM file is encrypted then you must specify the password.
	 *            Otherwise, this argument will be ignored and can be set to <code>null</code>.
	 * 
	 * @return whether the connection is now authenticated.
	 * @throws IOException
	 */
	public synchronized boolean authenticateWithPublicKey(String user, File pemFile, String password)
			throws IOException
	{
		if (pemFile == null)
			throw new IllegalArgumentException("pemFile argument is null");

		char[] buff = new char[256];

		CharArrayWriter cw = new CharArrayWriter();

		FileReader fr = new FileReader(pemFile);

		while (true)
		{
			int len = fr.read(buff);
			if (len < 0)
				break;
			cw.write(buff, 0, len);
		}

		fr.close();

		return authenticateWithPublicKey(user, cw.toCharArray(), password);
	}

	/**
	 * Add a {@link ConnectionMonitor} to this connection. Can be invoked at any time,
	 * but it is best to add connection monitors before invoking
	 * <code>connect()</code> to avoid glitches (e.g., you add a connection monitor after
	 * a successful connect(), but the connection has died in the mean time. Then,
	 * your connection monitor won't be notified.) 
	 * <p>
	 * You can add as many monitors as you like.
	 * 
	 * @see ConnectionMonitor
	 * 
	 * @param cmon An object implementing the <code>ConnectionMonitor</code> interface.
	 */
	@SuppressWarnings("unchecked")
	public synchronized void addConnectionMonitor(ConnectionMonitor cmon)
	{
		if (cmon == null)
			throw new IllegalArgumentException("cmon argument is null");

		connectionMonitors.addElement(cmon);

		if (tm != null)
			tm.setConnectionMonitors(connectionMonitors);
	}

	/**
	 * Close the connection to the SSH-2 server. All assigned sessions will be
	 * closed, too. Can be called at any time. Don't forget to call this once
	 * you don't need a connection anymore - otherwise the receiver thread may
	 * run forever.
	 */
	public synchronized void close()
	{
		Throwable t = new Throwable("Closed due to user request.");
		close(t, false);
	}

	private void close(Throwable t, boolean hard)
	{
		if (cm != null)
			cm.closeAllChannels();

		if (tm != null)
		{
			tm.close(t, hard == false);
			tm = null;
		}
		am = null;
		cm = null;
		authenticated = false;
	}

	/**
	 * Same as {@link #connect(ServerHostKeyVerifier, int, int) connect(null, 0, 0)}.
	 * 
	 * @return see comments for the {@link #connect(ServerHostKeyVerifier, int, int) connect(ServerHostKeyVerifier, int, int)} method.
	 * @throws IOException
	 */
	public synchronized ConnectionInfo connect() throws IOException
	{
		return connect(null, 0, 0);
	}

	/**
	 * Same as {@link #connect(ServerHostKeyVerifier, int, int) connect(verifier, 0, 0)}.
	 * 
	 * @return see comments for the {@link #connect(ServerHostKeyVerifier, int, int) connect(ServerHostKeyVerifier, int, int)} method.
	 * @throws IOException
	 */
	public synchronized ConnectionInfo connect(ServerHostKeyVerifier verifier) throws IOException
	{
		return connect(verifier, 0, 0);
	}

	/**
	 * Connect to the SSH-2 server and, as soon as the server has presented its
	 * host key, use the {@link ServerHostKeyVerifier#verifyServerHostKey(String,
	 * int, String, byte[]) ServerHostKeyVerifier.verifyServerHostKey()}
	 * method of the <code>verifier</code> to ask for permission to proceed.
	 * If <code>verifier</code> is <code>null</code>, then any host key will be
	 * accepted - this is NOT recommended, since it makes man-in-the-middle attackes
	 * VERY easy (somebody could put a proxy SSH server between you and the real server).
	 * <p>
	 * Note: The verifier will be called before doing any crypto calculations
	 * (i.e., diffie-hellman). Therefore, if you don't like the presented host key then
	 * no CPU cycles are wasted (and the evil server has less information about us).
	 * <p>
	 * However, it is still possible that the server presented a fake host key: the server
	 * cheated (typically a sign for a man-in-the-middle attack) and is not able to generate
	 * a signature that matches its host key. Don't worry, the library will detect such
	 * a scenario later when checking the signature (the signature cannot be checked before
	 * having completed the diffie-hellman exchange).
	 * <p>
	 * Note 2: The  {@link ServerHostKeyVerifier#verifyServerHostKey(String,
	 * int, String, byte[]) ServerHostKeyVerifier.verifyServerHostKey()} method
	 * will *NOT* be called from the current thread, the call is being made from a
	 * background thread (there is a background dispatcher thread for every
	 * established connection). 
	 * <p>
	 * Note 3: This method will block as long as the key exchange of the underlying connection
	 * has not been completed (and you have not specified any timeouts).
	 * <p>
	 * Note 4: If you want to re-use a connection object that was successfully connected,
	 * then you must call the {@link #close()} method before invoking <code>connect()</code> again.
	 * 
	 * @param verifier
	 *            An object that implements the
	 *            {@link ServerHostKeyVerifier} interface. Pass <code>null</code>
	 *            to accept any server host key - NOT recommended.
	 *            
	 * @param connectTimeout
	 *            Connect the underlying TCP socket to the server with the given timeout
	 *            value (non-negative, in milliseconds). Zero means no timeout. If a proxy is being
	 *            used (see {@link #setProxyData(ProxyData)}), then this timeout is used for the
	 *            connection establishment to the proxy.
	 * 
	 * @param kexTimeout
	 *            Timeout for complete connection establishment (non-negative,
	 *            in milliseconds). Zero means no timeout. The timeout counts from the
	 *            moment you invoke the connect() method and is cancelled as soon as the
	 *            first key-exchange round has finished. It is possible that
	 *            the timeout event will be fired during the invocation of the
	 *            <code>verifier</code> callback, but it will only have an effect after
	 *            the <code>verifier</code> returns.
	 *            
	 * @return A {@link ConnectionInfo} object containing the details of
	 *            the established connection.
	 *         
	 * @throws IOException
	 *            If any problem occurs, e.g., the server's host key is not
	 *            accepted by the <code>verifier</code> or there is problem during
	 *            the initial crypto setup (e.g., the signature sent by the server is wrong).
	 *            <p>
	 *            In case of a timeout (either connectTimeout or kexTimeout)
	 *            a SocketTimeoutException is thrown.
	 *            <p>
	 *            An exception may also be thrown if the connection was already successfully
	 *            connected (no matter if the connection broke in the mean time) and you invoke
	 *            <code>connect()</code> again without having called {@link #close()} first.
	 *            <p>
	 *            If a HTTP proxy is being used and the proxy refuses the connection,
	 *            then a {@link HTTPProxyException} may be thrown, which
	 *            contains the details returned by the proxy. If the proxy is buggy and does
	 *            not return a proper HTTP response, then a normal IOException is thrown instead.        
	 */
	public synchronized ConnectionInfo connect(ServerHostKeyVerifier verifier, int connectTimeout, int kexTimeout)
			throws IOException
	{
		final class TimeoutState
		{
			boolean isCancelled = false;
			boolean timeoutSocketClosed = false;
		}

		if (tm != null)
			throw new IOException("Connection to " + hostname + " is already in connected state!");

		if (connectTimeout < 0)
			throw new IllegalArgumentException("connectTimeout must be non-negative!");

		if (kexTimeout < 0)
			throw new IllegalArgumentException("kexTimeout must be non-negative!");

		final TimeoutState state = new TimeoutState();

		tm = new TransportManager(hostname, port);

		tm.setConnectionMonitors(connectionMonitors);

		/* Make sure that the runnable below will observe the new value of "tm"
		 * and "state" (the runnable will be executed in a different thread, which
		 * may be already running, that is why we need a memory barrier here).
		 * See also the comment in Channel.java if you
		 * are interested in the details.
		 * 
		 * OKOK, this is paranoid since adding the runnable to the todo list
		 * of the TimeoutService will ensure that all writes have been flushed
		 * before the Runnable reads anything
		 * (there is a synchronized block in TimeoutService.addTimeoutHandler).
		 */
		synchronized (tm)
		{	

			/* We could actually synchronize on anything. */
		}

		try
		{
			TimeoutToken token = null;

			if (kexTimeout > 0)
			{
				final Runnable timeoutHandler = new Runnable()
				{
					public void run()
					{
						synchronized (state)
						{
							if (state.isCancelled)
								return;
							state.timeoutSocketClosed = true;
							tm.close(new SocketTimeoutException("The connect timeout expired"), false);
						}
					}
				};

				long timeoutHorizont = System.currentTimeMillis() + kexTimeout;

				token = TimeoutService.addTimeoutHandler(timeoutHorizont, timeoutHandler);
			}

			try
			{
				tm.initialize(cryptoWishList, verifier, dhgexpara, connectTimeout, getOrCreateSecureRND(), proxyData);
			}
			catch (SocketTimeoutException se)
			{
				throw (SocketTimeoutException) new SocketTimeoutException(
						"The connect() operation on the socket timed out.").initCause(se);
			}

			tm.setTcpNoDelay(tcpNoDelay);

			/* Wait until first KEX has finished */

			ConnectionInfo ci = tm.getConnectionInfo(1);

			/* Now try to cancel the timeout, if needed */

			if (token != null)
			{
				TimeoutService.cancelTimeoutHandler(token);

				/* Were we too late? */

				synchronized (state)
				{
					if (state.timeoutSocketClosed)
						throw new IOException("This exception will be replaced by the one below =)");
					/* Just in case the "cancelTimeoutHandler" invocation came just a little bit
					 * too late but the handler did not enter the semaphore yet - we can
					 * still stop it.
					 */
					state.isCancelled = true;
				}
			}

			return ci;
		}
		catch (SocketTimeoutException ste)
		{
			throw ste;
		}
		catch (IOException e1)
		{
			/* This will also invoke any registered connection monitors */
			close(new Throwable("There was a problem during connect."), false);

			synchronized (state)
			{
				/* Show a clean exception, not something like "the socket is closed!?!" */
				if (state.timeoutSocketClosed)
					throw new SocketTimeoutException("The kexTimeout (" + kexTimeout + " ms) expired.");
			}

			/* Do not wrap a HTTPProxyException */
			if (e1 instanceof HTTPProxyException)
				throw e1;

			throw (IOException) new IOException("There was a problem while connecting to " + hostname + ":" + port)
					.initCause(e1);
		}
	}

	/**
	 * Creates a new {@link LocalPortForwarder}.
	 * A <code>LocalPortForwarder</code> forwards TCP/IP connections that arrive at a local
	 * port via the secure tunnel to another host (which may or may not be
	 * identical to the remote SSH-2 server).
	 * <p>
	 * This method must only be called after one has passed successfully the authentication step.
	 * There is no limit on the number of concurrent forwardings.
	 * 
	 * @param local_port the local port the LocalPortForwarder shall bind to.
	 * @param host_to_connect target address (IP or hostname)
	 * @param port_to_connect target port
	 * @return A {@link LocalPortForwarder} object.
	 * @throws IOException
	 */
	public synchronized MyLocalPortForwarder createLocalPortForwarder(int local_port, String host_to_connect,
			int port_to_connect) throws IOException
	{
		if (tm == null)
			throw new IllegalStateException("Cannot forward ports, you need to establish a connection first.");

		if (!authenticated)
			throw new IllegalStateException("Cannot forward ports, connection is not authenticated.");

		return new MyLocalPortForwarder(cm, local_port, host_to_connect, port_to_connect);
	}

	/**
	 * Creates a new {@link LocalStreamForwarder}.
	 * A <code>LocalStreamForwarder</code> manages an Input/Outputstream pair
	 * that is being forwarded via the secure tunnel into a TCP/IP connection to another host
	 * (which may or may not be identical to the remote SSH-2 server).
	 * 
	 * @param host_to_connect
	 * @param port_to_connect
	 * @return A {@link LocalStreamForwarder} object.
	 * @throws IOException
	 */
	public synchronized MyLocalStreamForwarder createLocalStreamForwarder(String host_to_connect, int port_to_connect)
			throws IOException
	{
		if (tm == null)
			throw new IllegalStateException("Cannot forward, you need to establish a connection first.");

		if (!authenticated)
			throw new IllegalStateException("Cannot forward, connection is not authenticated.");

		return new MyLocalStreamForwarder(cm, host_to_connect, port_to_connect);
	}

	/**
	 * Create a very basic {@link SCPClient} that can be used to copy
	 * files from/to the SSH-2 server.
	 * <p>
	 * Works only after one has passed successfully the authentication step.
	 * There is no limit on the number of concurrent SCP clients.
	 * <p>
	 * Note: This factory method will probably disappear in the future.
	 * 
	 * @return A {@link SCPClient} object.
	 * @throws IOException
	 */
	public synchronized MySCPClient createSCPClient() throws IOException
	{
		if (tm == null)
			throw new IllegalStateException("Cannot create SCP client, you need to establish a connection first.");

		if (!authenticated)
			throw new IllegalStateException("Cannot create SCP client, connection is not authenticated.");

		return new MySCPClient(this);
	}

	/**
	 * Force an asynchronous key re-exchange (the call does not block). The
	 * latest values set for MAC, Cipher and DH group exchange parameters will
	 * be used. If a key exchange is currently in progress, then this method has
	 * the only effect that the so far specified parameters will be used for the
	 * next (server driven) key exchange.
	 * <p>
	 * Note: This implementation will never start a key exchange (other than the initial one)
	 * unless you or the SSH-2 server ask for it.
	 * 
	 * @throws IOException
	 *             In case of any failure behind the scenes.
	 */
	public synchronized void forceKeyExchange() throws IOException
	{
		if (tm == null)
			throw new IllegalStateException("You need to establish a connection first.");

		tm.forceKeyExchange(cryptoWishList, dhgexpara);
	}

	/**
	 * Returns the hostname that was passed to the constructor.
	 * 
	 * @return the hostname
	 */
	public synchronized String getHostname()
	{
		return hostname;
	}

	/**
	 * Returns the port that was passed to the constructor.
	 * 
	 * @return the TCP port
	 */
	public synchronized int getPort()
	{
		return port;
	}

	/**
	 * Returns a {@link ConnectionInfo} object containing the details of
	 * the connection. Can be called as soon as the connection has been
	 * established (successfully connected).
	 * 
	 * @return A {@link ConnectionInfo} object.
	 * @throws IOException
	 *             In case of any failure behind the scenes.
	 */
	public synchronized ConnectionInfo getConnectionInfo() throws IOException
	{
		if (tm == null)
			throw new IllegalStateException(
					"Cannot get details of connection, you need to establish a connection first.");
		return tm.getConnectionInfo(1);
	}

	/**
	 * After a successful connect, one has to authenticate oneself. This method
	 * can be used to tell which authentication methods are supported by the
	 * server at a certain stage of the authentication process (for the given
	 * username).
	 * <p>
	 * Note 1: the username will only be used if no authentication step was done
	 * so far (it will be used to ask the server for a list of possible
	 * authentication methods). Otherwise, this method ignores the user name and
	 * returns a cached method list (which is based on the information contained
	 * in the last negative server response).
	 * <p>
	 * Note 2: the server may return method names that are not supported by this
	 * implementation.
	 * <p>
	 * After a successful authentication, this method must not be called
	 * anymore.
	 * 
	 * @param user
	 *            A <code>String</code> holding the username.
	 * 
	 * @return a (possibly emtpy) array holding authentication method names.
	 * @throws IOException
	 */
	public synchronized String[] getRemainingAuthMethods(String user) throws IOException
	{
		if (user == null)
			throw new IllegalArgumentException("user argument may not be NULL!");

		if (tm == null)
			throw new IllegalStateException("Connection is not established!");

		if (authenticated)
			throw new IllegalStateException("Connection is already authenticated!");

		if (am == null)
			am = new AuthenticationManager(tm);

		if (cm == null)
			cm = new ChannelManager(tm);

		return am.getRemainingMethods(user);
	}

	/**
	 * Determines if the authentication phase is complete. Can be called at any
	 * time.
	 * 
	 * @return <code>true</code> if no further authentication steps are
	 *         needed.
	 */
	public synchronized boolean isAuthenticationComplete()
	{
		return authenticated;
	}

	/**
	 * Returns true if there was at least one failed authentication request and
	 * the last failed authentication request was marked with "partial success"
	 * by the server. This is only needed in the rare case of SSH-2 server setups
	 * that cannot be satisfied with a single successful authentication request
	 * (i.e., multiple authentication steps are needed.)
	 * <p>
	 * If you are interested in the details, then have a look at
	 * draft-ietf-secsh-userauth-XX.txt.
	 * 
	 * @return if the there was a failed authentication step and the last one
	 *         was marked as a "partial success".
	 */
	public synchronized boolean isAuthenticationPartialSuccess()
	{
		if (am == null)
			return false;

		return am.getPartialSuccess();
	}

	/**
	 * Checks if a specified authentication method is available. This method is
	 * actually just a wrapper for {@link #getRemainingAuthMethods(String)
	 * getRemainingAuthMethods()}.
	 * 
	 * @param user
	 *            A <code>String</code> holding the username.
	 * @param method
	 *            An authentication method name (e.g., "publickey", "password",
	 *            "keyboard-interactive") as specified by the SSH-2 standard.
	 * @return if the specified authentication method is currently available.
	 * @throws IOException
	 */
	public synchronized boolean isAuthMethodAvailable(String user, String method) throws IOException
	{
		if (method == null)
			throw new IllegalArgumentException("method argument may not be NULL!");

		String methods[] = getRemainingAuthMethods(user);

		for (int i = 0; i < methods.length; i++)
		{
			if (methods[i].compareTo(method) == 0)
				return true;
		}

		return false;
	}

	private final SecureRandom getOrCreateSecureRND()
	{
		if (generator == null)
			generator = new SecureRandom();
		
		return generator;
	}
	
	/**
	 * Open a new {@link Session} on this connection. Works only after one has passed
	 * successfully the authentication step. There is no limit on the number of
	 * concurrent sessions.
	 * 
	 * @return A {@link Session} object.
	 * @throws IOException
	 */
	public synchronized MySession openSession() throws IOException
	{
		if (tm == null)
			throw new IllegalStateException("Cannot open session, you need to establish a connection first.");

		if (!authenticated)
			throw new IllegalStateException("Cannot open session, connection is not authenticated.");

		return new MySession(cm, getOrCreateSecureRND());
	}

	/**
	 * Removes duplicates from a String array, keeps only first occurence
	 * of each element. Does not destroy order of elements; can handle nulls.
	 * Uses a very efficient O(N^2) algorithm =)
	 * 
	 * @param list a String array.
	 * @return a cleaned String array.
	 */
	private String[] removeDuplicates(String[] list)
	{
		if ((list == null) || (list.length < 2))
			return list;

		String[] list2 = new String[list.length];

		int count = 0;

		for (int i = 0; i < list.length; i++)
		{
			boolean duplicate = false;

			String element = list[i];

			for (int j = 0; j < count; j++)
			{
				if (((element == null) && (list2[j] == null)) || ((element != null) && (element.equals(list2[j]))))
				{
					duplicate = true;
					break;
				}
			}

			if (duplicate)
				continue;

			list2[count++] = list[i];
		}

		if (count == list2.length)
			return list2;

		String[] tmp = new String[count];
		System.arraycopy(list2, 0, tmp, 0, count);

		return tmp;
	}

	/**
	 * Unless you know what you are doing, you will never need this.
	 * 
	 * @param ciphers
	 */
	public synchronized void setClient2ServerCiphers(String[] ciphers)
	{
		if ((ciphers == null) || (ciphers.length == 0))
			throw new IllegalArgumentException();
		ciphers = removeDuplicates(ciphers);
		BlockCipherFactory.checkCipherList(ciphers);
		cryptoWishList.c2s_enc_algos = ciphers;
	}

	/**
	 * Unless you know what you are doing, you will never need this.
	 * 
	 * @param macs
	 */
	public synchronized void setClient2ServerMACs(String[] macs)
	{
		if ((macs == null) || (macs.length == 0))
			throw new IllegalArgumentException();
		macs = removeDuplicates(macs);
		MAC.checkMacList(macs);
		cryptoWishList.c2s_mac_algos = macs;
	}

	/**
	 * Sets the parameters for the diffie-hellman group exchange. Unless you
	 * know what you are doing, you will never need this. Default values are
	 * defined in the {@link DHGexParameters} class.
	 * 
	 * @param dgp {@link DHGexParameters}, non null.
	 * 
	 */
	public synchronized void setDHGexParameters(DHGexParameters dgp)
	{
		if (dgp == null)
			throw new IllegalArgumentException();

		dhgexpara = dgp;
	}

	/**
	 * Unless you know what you are doing, you will never need this.
	 * 
	 * @param ciphers
	 */
	public synchronized void setServer2ClientCiphers(String[] ciphers)
	{
		if ((ciphers == null) || (ciphers.length == 0))
			throw new IllegalArgumentException();
		ciphers = removeDuplicates(ciphers);
		BlockCipherFactory.checkCipherList(ciphers);
		cryptoWishList.s2c_enc_algos = ciphers;
	}

	/**
	 * Unless you know what you are doing, you will never need this.
	 * 
	 * @param macs
	 */
	public synchronized void setServer2ClientMACs(String[] macs)
	{
		if ((macs == null) || (macs.length == 0))
			throw new IllegalArgumentException();

		macs = removeDuplicates(macs);
		MAC.checkMacList(macs);
		cryptoWishList.s2c_mac_algos = macs;
	}

	/**
	 * Define the set of allowed server host key algorithms to be used for
	 * the following key exchange operations.
	 * <p>
	 * Unless you know what you are doing, you will never need this.
	 * 
	 * @param algos An array of allowed server host key algorithms.
	 * 	SSH-2 defines <code>ssh-dss</code> and <code>ssh-rsa</code>.
	 * 	The entries of the array must be ordered after preference, i.e.,
	 *  the entry at index 0 is the most preferred one. You must specify
	 *  at least one entry.
	 */
	public synchronized void setServerHostKeyAlgorithms(String[] algos)
	{
		if ((algos == null) || (algos.length == 0))
			throw new IllegalArgumentException();

		algos = removeDuplicates(algos);
		KexManager.checkServerHostkeyAlgorithmsList(algos);
		cryptoWishList.serverHostKeyAlgorithms = algos;
	}

	/**
	 * Enable/disable TCP_NODELAY (disable/enable Nagle's algorithm) on the underlying socket.
	 * <p>
	 * Can be called at any time. If the connection has not yet been established
	 * then the passed value will be stored and set after the socket has been set up.
	 * The default value that will be used is <code>false</code>.
	 * 
	 * @param enable the argument passed to the <code>Socket.setTCPNoDelay()</code> method.
	 * @throws IOException
	 */
	public synchronized void setTCPNoDelay(boolean enable) throws IOException
	{
		tcpNoDelay = enable;

		if (tm != null)
			tm.setTcpNoDelay(enable);
	}

	/**
	 * Used to tell the library that the connection shall be established through a proxy server.
	 * It only makes sense to call this method before calling the {@link #connect() connect()}
	 * method.
	 * <p>
	 * At the moment, only HTTP proxies are supported.
	 * <p>
	 * Note: This method can be called any number of times. The {@link #connect() connect()}
	 * method will use the value set in the last preceding invocation of this method.
	 * 
	 * @see HTTPProxyData
	 * 
	 * @param proxyData Connection information about the proxy. If <code>null</code>, then
	 *                  no proxy will be used (non surprisingly, this is also the default).
	 */
	public synchronized void setProxyData(ProxyData proxyData)
	{
		this.proxyData = proxyData;
	}

	/**
	 * Request a remote port forwarding.
	 * If successful, then forwarded connections will be redirected to the given target address.
	 * You can cancle a requested remote port forwarding by calling
	 * {@link #cancelRemotePortForwarding(int) cancelRemotePortForwarding()}.
	 * <p>
	 * A call of this method will block until the peer either agreed or disagreed to your request-
	 * <p>
	 * Note 1: this method typically fails if you
	 * <ul>
	 * <li>pass a port number for which the used remote user has not enough permissions (i.e., port
	 * &lt; 1024)</li>
	 * <li>or pass a port number that is already in use on the remote server</li>
	 * <li>or if remote port forwarding is disabled on the server.</li>
	 * </ul>
	 * <p>
	 * Note 2: (from the openssh man page): By default, the listening socket on the server will be
	 * bound to the loopback interface only. This may be overriden by specifying a bind address.
	 * Specifying a remote bind address will only succeed if the server's <b>GatewayPorts</b> option
	 * is enabled (see sshd_config(5)).
	 * 
	 * @param bindAddress address to bind to on the server:
	 *                    <ul>
	 *                    <li>"" means that connections are to be accepted on all protocol families
	 *                    supported by the SSH implementation</li>
	 *                    <li>"0.0.0.0" means to listen on all IPv4 addresses</li>
	 *                    <li>"::" means to listen on all IPv6 addresses</li>
	 *                    <li>"localhost" means to listen on all protocol families supported by the SSH
	 *                    implementation on loopback addresses only, [RFC3330] and RFC3513]</li>
	 *                    <li>"127.0.0.1" and "::1" indicate listening on the loopback interfaces for
	 *                    IPv4 and IPv6 respectively</li>
	 *                    </ul>
	 * @param bindPort port number to bind on the server (must be &gt; 0)
	 * @param targetAddress the target address (IP or hostname)
	 * @param targetPort the target port
	 * @throws IOException
	 */
	public synchronized void requestRemotePortForwarding(String bindAddress, int bindPort, String targetAddress,
			int targetPort) throws IOException
	{
		if (tm == null)
			throw new IllegalStateException("You need to establish a connection first.");

		if (!authenticated)
			throw new IllegalStateException("The connection is not authenticated.");

		if ((bindAddress == null) || (targetAddress == null) || (bindPort <= 0) || (targetPort <= 0))
			throw new IllegalArgumentException();

		cm.requestGlobalForward(bindAddress, bindPort, targetAddress, targetPort);
	}

	/**
	 * Cancel an earlier requested remote port forwarding. 
	 * Currently active forwardings will not be affected (e.g., disrupted).
	 * Note that further connection forwarding requests may be received until
	 * this method has returned.
	 * 
	 * @param bindPort the allocated port number on the server
	 * @throws IOException if the remote side refuses the cancel request or another low
	 *         level error occurs (e.g., the underlying connection is closed)
	 */
	public synchronized void cancelRemotePortForwarding(int bindPort) throws IOException
	{
		if (tm == null)
			throw new IllegalStateException("You need to establish a connection first.");

		if (!authenticated)
			throw new IllegalStateException("The connection is not authenticated.");

		cm.requestCancelGlobalForward(bindPort);
	}
	
	/**
	 * Provide your own instance of SecureRandom. Can be used, e.g., if you
	 * want to seed the used SecureRandom generator manually.
	 * <p>
	 * The SecureRandom instance is used during key exchanges, public key authentication,
	 * x11 cookie generation and the like.
	 * 
	 * @param rnd a SecureRandom instance
	 */
	public synchronized void setSecureRandom(SecureRandom rnd)
	{
		if (rnd == null)
			throw new IllegalArgumentException();
		
		this.generator = rnd;
	}
}


MySCPClient.java

package com;


import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
//这是jar调用类,已经自己实现了
//import ch.ethz.ssh2.Connection;
//import ch.ethz.ssh2.Session;


/**
 * 
 * 
 *一个非常基本的<code>SCPClient</code>可用于从/目录复制文件
 *这个scp客户端是线程安全的,你可以下载和上传不同的设置的文件同时没有任何麻烦,<
 *code>SCPClient</code>是实际上将每个请求映射到不同的{@link Session}
 * @author Christian Plattner, plattner@inf.ethz.ch
 * @version $Id: SCPClient.java,v 1.11 2006/08/02 11:57:12 cplattne Exp $
 */

public class MySCPClient
{
	MyConnection conn;

	class LenNamePair
	{
		long length;
		String filename;
	}

	public MySCPClient(MyConnection conn)
	{
		if (conn == null)
			throw new IllegalArgumentException("Cannot accept null argument!");
		this.conn = conn;
	}

	private void readResponse(InputStream is) throws IOException
	{
		int c = is.read();

		if (c == 0)
			return;

		if (c == -1)
			throw new IOException("Remote scp terminated unexpectedly.");

		if ((c != 1) && (c != 2))
			throw new IOException("Remote scp sent illegal error code.");

		if (c == 2)
			throw new IOException("Remote scp terminated with error.");

		String err = receiveLine(is);
		throw new IOException("Remote scp terminated with error (" + err + ").");
	}

	private String receiveLine(InputStream is) throws IOException
	{
		StringBuffer sb = new StringBuffer(30);

		while (true)
		{
			/* This is a random limit - if your path names are longer, then adjust it */

			if (sb.length() > 8192)
				throw new IOException("Remote scp sent a too long line");

			int c = is.read();

			if (c < 0)
				throw new IOException("Remote scp terminated unexpectedly.");

			if (c == '\n')
				break;

			sb.append((char) c);

		}
		return sb.toString();
	}

	private LenNamePair parseCLine(String line) throws IOException
	{
		/* Minimum line: "xxxx y z" ---> 8 chars */

		long len;

		if (line.length() < 8)
			throw new IOException("Malformed C line sent by remote SCP binary, line too short.");

		if ((line.charAt(4) != ' ') || (line.charAt(5) == ' '))
			throw new IOException("Malformed C line sent by remote SCP binary.");

		int length_name_sep = line.indexOf(' ', 5);

		if (length_name_sep == -1)
			throw new IOException("Malformed C line sent by remote SCP binary.");

		String length_substring = line.substring(5, length_name_sep);
		String name_substring = line.substring(length_name_sep + 1);

		if ((length_substring.length() <= 0) || (name_substring.length() <= 0))
			throw new IOException("Malformed C line sent by remote SCP binary.");

		if ((6 + length_substring.length() + name_substring.length()) != line.length())
			throw new IOException("Malformed C line sent by remote SCP binary.");

		try
		{
			len = Long.parseLong(length_substring);
		}
		catch (NumberFormatException e)
		{
			throw new IOException("Malformed C line sent by remote SCP binary, cannot parse file length.");
		}

		if (len < 0)
			throw new IOException("Malformed C line sent by remote SCP binary, illegal file length.");

		LenNamePair lnp = new LenNamePair();
		lnp.length = len;
		lnp.filename = name_substring;

		return lnp;
	}

	private void sendBytes(MySession sess, byte[] data, String fileName, String mode) throws IOException
	{
		OutputStream os = sess.getStdin();
		InputStream is = new BufferedInputStream(sess.getStdout(), 512);

		readResponse(is);

		String cline = "C" + mode + " " + data.length + " " + fileName + "\n";

		os.write(cline.getBytes());
		os.flush();

		readResponse(is);

		os.write(data, 0, data.length);
		os.write(0);
		os.flush();

		readResponse(is);

		os.write("E\n".getBytes());
		os.flush();
	}

	//上传数据
	private void sendFiles(MySession sess, String[] files, String[] remoteFiles, String mode) throws IOException
	{
		byte[] buffer = new byte[8192];

		OutputStream os = new BufferedOutputStream(sess.getStdin(), 40000);
		InputStream is = new BufferedInputStream(sess.getStdout(), 512);

		readResponse(is);

		for (int i = 0; i < files.length; i++)
		{
			File f = new File(files[i]);
			long remain = f.length();

			String remoteName;

			if ((remoteFiles != null) && (remoteFiles.length > i) && (remoteFiles[i] != null))
				remoteName = remoteFiles[i];
			else
				remoteName = f.getName();
			
			//修改远程电脑的remoteName文件的权限
			String cline = "C" + mode + " " + remain + " " + remoteName + "\n";  

			os.write(cline.getBytes());
			
			os.flush();

			readResponse(is);

			FileInputStream fis = null;

			try
			{
				fis = new FileInputStream(f);

				while (remain > 0)
				{
					int trans;
					if (remain > buffer.length) 
						trans = buffer.length;
					else 
						trans = (int) remain;

					if (fis.read(buffer, 0, trans) != trans)
						throw new IOException("Cannot read enough from local file " + files[i]);
					
					os.write(buffer, 0, trans);

					remain -= trans;
				}
			}
			finally
			{
				if (fis != null)
					fis.close();
			}

			os.write(0);
			os.flush();

			readResponse(is);
		}

		os.write("E\n".getBytes());
		os.flush();
	}

	private void receiveFiles(MySession sess, OutputStream[] targets) throws IOException
	{
		byte[] buffer = new byte[8192];

		OutputStream os = new BufferedOutputStream(sess.getStdin(), 512);
		InputStream is = new BufferedInputStream(sess.getStdout(), 40000);

		os.write(0x0);
		os.flush();

		for (int i = 0; i < targets.length; i++)
		{
			LenNamePair lnp = null;

			while (true)
			{
				int c = is.read();
				if (c < 0)
					throw new IOException("Remote scp terminated unexpectedly.");

				String line = receiveLine(is);

				if (c == 'T')
				{
					/* Ignore modification times */

					continue;
				}

				if ((c == 1) || (c == 2))
					throw new IOException("Remote SCP error: " + line);

				if (c == 'C')
				{
					lnp = parseCLine(line);
					break;

				}
				throw new IOException("Remote SCP error: " + ((char) c) + line);
			}

			os.write(0x0);
			os.flush();

			long remain = lnp.length;

			while (remain > 0)
			{
				int trans;
				if (remain > buffer.length)
					trans = buffer.length;
				else
					trans = (int) remain;

				int this_time_received = is.read(buffer, 0, trans);

				if (this_time_received < 0)
				{
					throw new IOException("Remote scp terminated connection unexpectedly");
				}

				targets[i].write(buffer, 0, this_time_received);

				remain -= this_time_received;
			}

			readResponse(is);

			os.write(0x0);
			os.flush();
		}
	}

	private void receiveFiles(MySession sess, String[] files, String target) throws IOException
	{
		byte[] buffer = new byte[8192];

		OutputStream os = new BufferedOutputStream(sess.getStdin(), 512);
		InputStream is = new BufferedInputStream(sess.getStdout(), 40000);

		os.write(0x0);
		os.flush();

		for (int i = 0; i < files.length; i++)
		{
			LenNamePair lnp = null;

			while (true)
			{
				int c = is.read();
				if (c < 0)
					throw new IOException("Remote scp terminated unexpectedly.");

				String line = receiveLine(is);

				if (c == 'T')
				{
					/* Ignore modification times */

					continue;
				}

				if ((c == 1) || (c == 2))
					throw new IOException("Remote SCP error: " + line);

				if (c == 'C')
				{
					lnp = parseCLine(line);
					break;

				}
				throw new IOException("Remote SCP error: " + ((char) c) + line);
			}

			os.write(0x0);
			os.flush();

			File f = new File(target + File.separatorChar + lnp.filename);
			FileOutputStream fop = null;

			try
			{
				fop = new FileOutputStream(f);

				long remain = lnp.length;

				while (remain > 0)
				{
					int trans;
					if (remain > buffer.length)
						trans = buffer.length;
					else
						trans = (int) remain;

					int this_time_received = is.read(buffer, 0, trans);

					if (this_time_received < 0)
					{
						throw new IOException("Remote scp terminated connection unexpectedly");
					}

					fop.write(buffer, 0, this_time_received);

					remain -= this_time_received;
				}
			}
			finally
			{
				if (fop != null)
					fop.close();
			}

			readResponse(is);

			os.write(0x0);
			os.flush();
		}
	}

	/**
	 * Copy a local file to a remote directory, uses mode 0600 when creating
	 * the file on the remote side.
	 * 
	 * @param localFile
	 *            Path and name of local file.
	 * @param remoteTargetDirectory
	 *            Remote target directory. Use an empty string to specify the default directory.
	 * 
	 * @throws IOException
	 */
	public void put(String localFile, String remoteTargetDirectory) throws IOException
	{
		put(new String[] { localFile }, remoteTargetDirectory, "0600");
	}

	/**
	 * Copy a set of local files to a remote directory, uses mode 0600 when
	 * creating files on the remote side.
	 * 
	 * @param localFiles
	 *            Paths and names of local file names.
	 * @param remoteTargetDirectory
	 *            Remote target directory. Use an empty string to specify the default directory.
	 * 
	 * @throws IOException
	 */

	public void put(String[] localFiles, String remoteTargetDirectory) throws IOException
	{
		put(localFiles, remoteTargetDirectory, "0600");
	}

	/**
	 * Copy a local file to a remote directory, uses the specified mode when
	 * creating the file on the remote side.
	 * 
	 * @param localFile
	 *            Path and name of local file.
	 * @param remoteTargetDirectory
	 *            Remote target directory. Use an empty string to specify the default directory.
	 * @param mode
	 *            a four digit string (e.g., 0644, see "man chmod", "man open")
	 * @throws IOException
	 */
	public void put(String localFile, String remoteTargetDirectory, String mode) throws IOException
	{
		put(new String[] { localFile }, remoteTargetDirectory, mode);
	}

	/**
	 * Copy a local file to a remote directory, uses the specified mode and remote filename
	 * when creating the file on the remote side.
	 * 
	 * @param localFile
	 *            Path and name of local file.
	 * @param remoteFileName
	 *            The name of the file which will be created in the remote target directory.
	 * @param remoteTargetDirectory
	 *            Remote target directory. Use an empty string to specify the default directory.
	 * @param mode
	 *            a four digit string (e.g., 0644, see "man chmod", "man open")
	 * @throws IOException
	 */
	public void put(String localFile, String remoteFileName, String remoteTargetDirectory, String mode)
			throws IOException
	{
		put(new String[] { localFile }, new String[] { remoteFileName }, remoteTargetDirectory, mode);
	}

	/**
	 * Create a remote file and copy the contents of the passed byte array into it.
	 * Uses mode 0600 for creating the remote file.
	 * 
	 * @param data
	 *            the data to be copied into the remote file.
	 * @param remoteFileName
	 *            The name of the file which will be created in the remote target directory.
	 * @param remoteTargetDirectory
	 *            Remote target directory. Use an empty string to specify the default directory.
	 * @throws IOException
	 */

	public void put(byte[] data, String remoteFileName, String remoteTargetDirectory) throws IOException
	{
		put(data, remoteFileName, remoteTargetDirectory, "0600");
	}

	/**
	 * Create a remote file and copy the contents of the passed byte array into it.
	 * The method use the specified mode when creating the file on the remote side.
	 * 
	 * @param data
	 *            the data to be copied into the remote file.
	 * @param remoteFileName
	 *            The name of the file which will be created in the remote target directory.
	 * @param remoteTargetDirectory
	 *            Remote target directory. Use an empty string to specify the default directory.
	 * @param mode
	 *            a four digit string (e.g., 0644, see "man chmod", "man open")
	 * @throws IOException
	 */
	public void put(byte[] data, String remoteFileName, String remoteTargetDirectory, String mode) throws IOException
	{
		MySession sess = null;

		if ((remoteFileName == null) || (remoteTargetDirectory == null) || (mode == null))
			throw new IllegalArgumentException("Null argument.");

		if (mode.length() != 4)
			throw new IllegalArgumentException("Invalid mode.");

		for (int i = 0; i < mode.length(); i++)
			if (Character.isDigit(mode.charAt(i)) == false)
				throw new IllegalArgumentException("Invalid mode.");

		remoteTargetDirectory = remoteTargetDirectory.trim();
		remoteTargetDirectory = (remoteTargetDirectory.length() > 0) ? remoteTargetDirectory : ".";

		String cmd = "scp -t -d " + remoteTargetDirectory;

		try
		{
			sess = conn.openSession();
			sess.execCommand(cmd);
			sendBytes(sess, data, remoteFileName, mode);
		}
		catch (IOException e)
		{
			throw (IOException) new IOException("Error during SCP transfer.").initCause(e);
		}
		finally
		{
			if (sess != null)
				sess.close();
		}
	}

	/**
	 * Copy a set of local files to a remote directory, uses the specified mode
	 * when creating the files on the remote side.
	 * 
	 * @param localFiles
	 *            Paths and names of the local files.
	 * @param remoteTargetDirectory
	 *            Remote target directory. Use an empty string to specify the default directory.
	 * @param mode
	 *            a four digit string (e.g., 0644, see "man chmod", "man open")
	 * @throws IOException
	 */
	public void put(String[] localFiles, String remoteTargetDirectory, String mode) throws IOException
	{
		put(localFiles, null, remoteTargetDirectory, mode);
	}

	//调用此方法上传数据
	public void put(String[] localFiles, String[] remoteFiles, String remoteTargetDirectory, String mode)
			throws IOException
	{
		MySession sess = null;

		/* remoteFiles may be null, indicating that the local filenames shall be used */

		if ((localFiles == null) || (remoteTargetDirectory == null) || (mode == null))
			throw new IllegalArgumentException("Null argument.");

		if (mode.length() != 4)
			throw new IllegalArgumentException("Invalid mode.");

		for (int i = 0; i < mode.length(); i++) 
			if (Character.isDigit(mode.charAt(i)) == false) //判断字符是否为数字,如果字符为数字,则返回true,否则返回false
				throw new IllegalArgumentException("Invalid mode.");

		if (localFiles.length == 0)
			return;
		remoteTargetDirectory = remoteTargetDirectory.trim(); //去掉字符串两端的多余的空格。
		remoteTargetDirectory = (remoteTargetDirectory.length() > 0) ? remoteTargetDirectory : ".";  //如果远程电脑目录为空则上传到当前目录下

		String cmd = "scp -t -d " + remoteTargetDirectory;

		for (int i = 0; i < localFiles.length; i++)
		{
			if (localFiles[i] == null)
				throw new IllegalArgumentException("Cannot accept null filename.");
		}

		try
		{
			sess = conn.openSession();
			sess.execCommand(cmd);
			sendFiles(sess, localFiles, remoteFiles, mode);
		}
		catch (IOException e)
		{
			throw (IOException) new IOException("Error during SCP transfer.").initCause(e);
		}
		finally
		{
			if (sess != null)
				sess.close();
		}
	}

	/**
	 * Download a file from the remote server to a local directory.
	 * 
	 * @param remoteFile
	 *            Path and name of the remote file.
	 * @param localTargetDirectory
	 *            Local directory to put the downloaded file.
	 * 
	 * @throws IOException
	 */
	public void get(String remoteFile, String localTargetDirectory) throws IOException
	{
		get(new String[] { remoteFile }, localTargetDirectory);
	}

	/**
	 * Download a file from the remote server and pipe its contents into an <code>OutputStream</code>.
	 * Please note that, to enable flexible usage of this method, the <code>OutputStream</code> will not
	 * be closed nor flushed.  
	 * 
	 * @param remoteFile
	 * 			Path and name of the remote file.
	 * @param target
	 * 			OutputStream where the contents of the file will be sent to.
	 * @throws IOException
	 */
	public void get(String remoteFile, OutputStream target) throws IOException
	{
		get(new String[] { remoteFile }, new OutputStream[] { target });
	}

	private void get(String remoteFiles[], OutputStream[] targets) throws IOException
	{
		MySession sess = null;

		if ((remoteFiles == null) || (targets == null))
			throw new IllegalArgumentException("Null argument.");

		if (remoteFiles.length != targets.length)
			throw new IllegalArgumentException("Length of arguments does not match.");

		if (remoteFiles.length == 0)
			return;

		String cmd = "scp -f";

		for (int i = 0; i < remoteFiles.length; i++)
		{
			if (remoteFiles[i] == null)
				throw new IllegalArgumentException("Cannot accept null filename.");

			String tmp = remoteFiles[i].trim();

			if (tmp.length() == 0)
				throw new IllegalArgumentException("Cannot accept empty filename.");

			cmd += (" " + tmp);
		}

		try
		{
			sess = conn.openSession();
			sess.execCommand(cmd);
			receiveFiles(sess, targets);
		}
		catch (IOException e)
		{
			throw (IOException) new IOException("Error during SCP transfer.").initCause(e);
		}
		finally
		{
			if (sess != null)
				sess.close();
		}
	}

	/**
	 * Download a set of files from the remote server to a local directory.
	 * 
	 * @param remoteFiles
	 *            Paths and names of the remote files.
	 * @param localTargetDirectory
	 *            Local directory to put the downloaded files.
	 * 
	 * @throws IOException
	 */
	public void get(String remoteFiles[], String localTargetDirectory) throws IOException
	{
		MySession sess = null;

		if ((remoteFiles == null) || (localTargetDirectory == null))
			throw new IllegalArgumentException("Null argument.");

		if (remoteFiles.length == 0)
			return;

		String cmd = "scp -f";

		for (int i = 0; i < remoteFiles.length; i++)
		{
			if (remoteFiles[i] == null)
				throw new IllegalArgumentException("Cannot accept null filename.");

			String tmp = remoteFiles[i].trim();

			if (tmp.length() == 0)
				throw new IllegalArgumentException("Cannot accept empty filename.");

			cmd += (" " + tmp);
		}

		try
		{
			sess = conn.openSession();
			sess.execCommand(cmd);
			receiveFiles(sess, remoteFiles, localTargetDirectory);
		}
		catch (IOException e)
		{
			throw (IOException) new IOException("Error during SCP transfer.").initCause(e);
		}
		finally
		{
			if (sess != null)
				sess.close();
		}
	}
}


MySession.java

package com;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.SecureRandom;

import ch.ethz.ssh2.ChannelCondition;
import ch.ethz.ssh2.channel.Channel;
import ch.ethz.ssh2.channel.ChannelManager;
import ch.ethz.ssh2.channel.X11ServerData;

/**
 * A <code>Session</code> is a remote execution of a program. "Program" means
 * in this context either a shell, an application or a system command. The
 * program may or may not have a tty. Only one single program can be started on
 * a session. However, multiple sessions can be active simultaneously.
 * 
 * @author Christian Plattner, plattner@inf.ethz.ch
 * @version $Id: Session.java,v 1.9 2006/02/14 19:43:16 cplattne Exp $
 */
public class MySession
{
	ChannelManager cm;
	Channel cn;

	boolean flag_pty_requested = false;
	boolean flag_x11_requested = false;
	boolean flag_execution_started = false;
	boolean flag_closed = false;

	String x11FakeCookie = null;

	final SecureRandom rnd;
	
	MySession(ChannelManager cm, SecureRandom rnd) throws IOException
	{
		this.cm = cm;
		this.cn = cm.openSessionChannel();
		this.rnd = rnd;
	}

	/**
	 * Basically just a wrapper for lazy people - identical to calling
	 * <code>requestPTY("dumb", 0, 0, 0, 0, null)</code>.
	 * 
	 * @throws IOException
	 */
	public void requestDumbPTY() throws IOException
	{
		requestPTY("dumb", 0, 0, 0, 0, null);
	}

	/**
	 * Basically just another wrapper for lazy people - identical to calling
	 * <code>requestPTY(term, 0, 0, 0, 0, null)</code>.
	 * 
	 * @throws IOException
	 */
	public void requestPTY(String term) throws IOException
	{
		requestPTY(term, 0, 0, 0, 0, null);
	}

	/**
	 * Allocate a pseudo-terminal for this session.
	 * <p>
	 * This method may only be called before a program or shell is started in
	 * this session.
	 * <p>
	 * Different aspects can be specified:
	 * <p>
	 * <ul>
	 * <li>The TERM environment variable value (e.g., vt100)</li>
	 * <li>The terminal's dimensions.</li>
	 * <li>The encoded terminal modes.</li>
	 * </ul>
	 * Zero dimension parameters are ignored. The character/row dimensions
	 * override the pixel dimensions (when nonzero). Pixel dimensions refer to
	 * the drawable area of the window. The dimension parameters are only
	 * informational. The encoding of terminal modes (parameter
	 * <code>terminal_modes</code>) is described, e.g., in
	 * draft-ietf-secsh-connect-XY.txt.
	 * 
	 * @param term
	 *            The TERM environment variable value (e.g., vt100)
	 * @param term_width_characters
	 *            terminal width, characters (e.g., 80)
	 * @param term_height_characters
	 *            terminal height, rows (e.g., 24)
	 * @param term_width_pixels
	 *            terminal width, pixels (e.g., 640)
	 * @param term_height_pixels
	 *            terminal height, pixels (e.g., 480)
	 * @param terminal_modes
	 *            encoded terminal modes (may be <code>null</code>)
	 * @throws IOException
	 */
	public void requestPTY(String term, int term_width_characters, int term_height_characters, int term_width_pixels,
			int term_height_pixels, byte[] terminal_modes) throws IOException
	{
		if (term == null)
			throw new IllegalArgumentException("TERM cannot be null.");

		if ((terminal_modes != null) && (terminal_modes.length > 0))
		{
			if (terminal_modes[terminal_modes.length - 1] != 0)
				throw new IOException("Illegal terminal modes description, does not end in zero byte");
		}
		else
			terminal_modes = new byte[] { 0 };

		synchronized (this)
		{
			/* The following is just a nicer error, we would catch it anyway later in the channel code */
			if (flag_closed)
				throw new IOException("This session is closed.");

			if (flag_pty_requested)
				throw new IOException("A PTY was already requested.");

			if (flag_execution_started)
				throw new IOException(
						"Cannot request PTY at this stage anymore, a remote execution has already started.");

			flag_pty_requested = true;
		}

		cm.requestPTY(cn, term, term_width_characters, term_height_characters, term_width_pixels, term_height_pixels,
				terminal_modes);
	}

	/**
	 * Request X11 forwarding for the current session.
	 * <p>
	 * You have to supply the name and port of your X-server.
	 * <p>
	 * This method may only be called before a program or shell is started in
	 * this session.
	 * 
	 * @param hostname the hostname of the real (target) X11 server (e.g., 127.0.0.1)
	 * @param port the port of the real (target) X11 server (e.g., 6010)
	 * @param cookie if non-null, then present this cookie to the real X11 server
	 * @param singleConnection if true, then the server is instructed to only forward one single
	 *        connection, no more connections shall be forwarded after first, or after the session
	 *        channel has been closed
	 * @throws IOException
	 */
	public void requestX11Forwarding(String hostname, int port, byte[] cookie, boolean singleConnection)
			throws IOException
	{
		if (hostname == null)
			throw new IllegalArgumentException("hostname argument may not be null");

		synchronized (this)
		{
			/* The following is just a nicer error, we would catch it anyway later in the channel code */
			if (flag_closed)
				throw new IOException("This session is closed.");

			if (flag_x11_requested)
				throw new IOException("X11 forwarding was already requested.");

			if (flag_execution_started)
				throw new IOException(
						"Cannot request X11 forwarding at this stage anymore, a remote execution has already started.");

			flag_x11_requested = true;
		}

		/* X11ServerData - used to store data about the target X11 server */

		X11ServerData x11data = new X11ServerData();

		x11data.hostname = hostname;
		x11data.port = port;
		x11data.x11_magic_cookie = cookie; /* if non-null, then present this cookie to the real X11 server */

		/* Generate fake cookie - this one is used between remote clients and the ganymed proxy */

		byte[] fakeCookie = new byte[16];
		String hexEncodedFakeCookie;

		/* Make sure that this fake cookie is unique for this connection */

		while (true)
		{
			rnd.nextBytes(fakeCookie);

			/* Generate also hex representation of fake cookie */

			StringBuffer tmp = new StringBuffer(32);
			for (int i = 0; i < fakeCookie.length; i++)
			{
				String digit2 = Integer.toHexString(fakeCookie[i] & 0xff);
				tmp.append((digit2.length() == 2) ? digit2 : "0" + digit2);
			}
			hexEncodedFakeCookie = tmp.toString();

			/* Well, yes, chances are low, but we want to be on the safe side */

			if (cm.checkX11Cookie(hexEncodedFakeCookie) == null)
				break;
		}

		/* Ask for X11 forwarding */

		cm.requestX11(cn, singleConnection, "MIT-MAGIC-COOKIE-1", hexEncodedFakeCookie, 0);

		/* OK, that went fine, get ready to accept X11 connections... */
		/* ... but only if the user has not called close() in the meantime =) */

		synchronized (this)
		{
			if (flag_closed == false)
			{
				this.x11FakeCookie = hexEncodedFakeCookie;
				cm.registerX11Cookie(hexEncodedFakeCookie, x11data);
			}
		}

		/* Now it is safe to start remote X11 programs */
	}

	/**
	 * Execute a command on the remote machine.
	 * 
	 * @param cmd
	 *            The command to execute on the remote host.
	 * @throws IOException
	 */
	public void execCommand(String cmd) throws IOException
	{
		if (cmd == null)
			throw new IllegalArgumentException("cmd argument may not be null");

		synchronized (this)
		{
			/* The following is just a nicer error, we would catch it anyway later in the channel code */
			if (flag_closed)
				throw new IOException("This session is closed.");

			if (flag_execution_started)
				throw new IOException("A remote execution has already started.");

			flag_execution_started = true;
		}

		cm.requestExecCommand(cn, cmd);
	}

	/**
	 * Start a shell on the remote machine.
	 * 
	 * @throws IOException
	 */
	public void startShell() throws IOException
	{
		synchronized (this)
		{
			/* The following is just a nicer error, we would catch it anyway later in the channel code */
			if (flag_closed)
				throw new IOException("This session is closed.");

			if (flag_execution_started)
				throw new IOException("A remote execution has already started.");

			flag_execution_started = true;
		}

		cm.requestShell(cn);
	}

	/**
	 * Start a subsystem on the remote machine.
	 * Unless you know what you are doing, you will never need this.
	 * 
	 * @param name the name of the subsystem.
	 * @throws IOException
	 */
	public void startSubSystem(String name) throws IOException
	{
		if (name == null)
			throw new IllegalArgumentException("name argument may not be null");

		synchronized (this)
		{
			/* The following is just a nicer error, we would catch it anyway later in the channel code */
			if (flag_closed)
				throw new IOException("This session is closed.");

			if (flag_execution_started)
				throw new IOException("A remote execution has already started.");

			flag_execution_started = true;
		}

		cm.requestSubSystem(cn, name);
	}

	public InputStream getStdout()
	{
		return cn.getStdoutStream();
	}

	public InputStream getStderr()
	{
		return cn.getStderrStream();
	}

	public OutputStream getStdin()
	{
		return cn.getStdinStream();
	}

	/**
	 * This method blocks until there is more data available on either the
	 * stdout or stderr InputStream of this <code>Session</code>. Very useful
	 * if you do not want to use two parallel threads for reading from the two
	 * InputStreams. One can also specify a timeout. NOTE: do NOT call this
	 * method if you use concurrent threads that operate on either of the two
	 * InputStreams of this <code>Session</code> (otherwise this method may
	 * block, even though more data is available).
	 * 
	 * @param timeout
	 *            The (non-negative) timeout in <code>ms</code>. <code>0</code> means no
	 *            timeout, the call may block forever.
	 * @return
	 *            <ul>
	 *            <li><code>0</code> if no more data will arrive.</li>
	 *            <li><code>1</code> if more data is available.</li>
	 *            <li><code>-1</code> if a timeout occurred.</li>
	 *            </ul>
	 *            
	 * @throws    IOException
	 * @deprecated This method has been replaced with a much more powerful wait-for-condition
	 *             interface and therefore acts only as a wrapper.
	 * 
	 */
	public int waitUntilDataAvailable(long timeout) throws IOException
	{
		if (timeout < 0)
			throw new IllegalArgumentException("timeout must not be negative!");

		int conditions = cm.waitForCondition(cn, timeout, ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA
				| ChannelCondition.EOF);

		if ((conditions & ChannelCondition.TIMEOUT) != 0)
			return -1;

		if ((conditions & (ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA)) != 0)
			return 1;

		/* Here we do not need to check separately for CLOSED, since CLOSED implies EOF */

		if ((conditions & ChannelCondition.EOF) != 0)
			return 0;

		throw new IllegalStateException("Unexpected condition result (" + conditions + ")");
	}

	/**
	 * This method blocks until certain conditions hold true on the underlying SSH-2 channel.
	 * <p>
	 * This method returns as soon as one of the following happens:
	 * <ul>
	 * <li>at least of the specified conditions (see {@link ChannelCondition}) holds true</li>
	 * <li>timeout > 0 and a timeout occured (TIMEOUT will be set in result conditions)</a> 
	 * <li>the underlying channel was closed (CLOSED will be set in result conditions)</a>
	 * </ul>
	 * <p>
	 * In any case, the result value contains ALL current conditions, which may be more
	 * than the specified condition set (i.e., never use the "==" operator to test for conditions
	 * in the bitmask, see also comments in {@link ChannelCondition}). 
	 * <p>
	 * Note: do NOT call this method if you want to wait for STDOUT_DATA or STDERR_DATA and
	 * there are concurrent threads (e.g., StreamGobblers) that operate on either of the two
	 * InputStreams of this <code>Session</code> (otherwise this method may
	 * block, even though more data is available in the StreamGobblers).
	 * 
	 * @param condition_set a bitmask based on {@link ChannelCondition} values
	 * @param timeout non-negative timeout in ms, <code>0</code> means no timeout
	 * @return all bitmask specifying all current conditions that are true
	 */

	public int waitForCondition(int condition_set, long timeout)
	{
		if (timeout < 0)
			throw new IllegalArgumentException("timeout must be non-negative!");

		return cm.waitForCondition(cn, timeout, condition_set);
	}

	/**
	 * Get the exit code/status from the remote command - if available. Be
	 * careful - not all server implementations return this value. It is
	 * generally a good idea to call this method only when all data from the
	 * remote side has been consumed (see also the <code<WaitForCondition</code> method).
	 * 
	 * @return An <code>Integer</code> holding the exit code, or
	 *         <code>null</code> if no exit code is (yet) available.
	 */
	public Integer getExitStatus()
	{
		return cn.getExitStatus();
	}

	/**
	 * Get the name of the signal by which the process on the remote side was
	 * stopped - if available and applicable. Be careful - not all server
	 * implementations return this value.
	 * 
	 * @return An <code>String</code> holding the name of the signal, or
	 *         <code>null</code> if the process exited normally or is still
	 *         running (or if the server forgot to send this information).
	 */
	public String getExitSignal()
	{
		return cn.getExitSignal();
	}

	/**
	 * Close this session. NEVER forget to call this method to free up resources -
	 * even if you got an exception from one of the other methods (or when
	 * getting an Exception on the Input- or OutputStreams). Sometimes these other
	 * methods may throw an exception, saying that the underlying channel is
	 * closed (this can happen, e.g., if the other server sent a close message.)
	 * However, as long as you have not called the <code>close()</code>
	 * method, you may be wasting (local) resources.
	 * 
	 */
	public void close()
	{
		synchronized (this)
		{
			if (flag_closed)
				return;

			flag_closed = true;

			if (x11FakeCookie != null)
				cm.unRegisterX11Cookie(x11FakeCookie, true);

			try
			{
				cm.closeChannel(cn, "Closed due to user request", true);
			}
			catch (IOException ignored)
			{
			}
		}
	}
}


MyLocalPortForwarder.java

package com;

import java.io.IOException;
import ch.ethz.ssh2.channel.ChannelManager;
import ch.ethz.ssh2.channel.LocalAcceptThread;

/**
 * A <code>LocalPortForwarder</code> forwards TCP/IP connections to a local
 * port via the secure tunnel to another host (which may or may not be identical
 * to the remote SSH-2 server).
 * 通过安全隧道将TCP/IP连接转发到本地端口到另一台主机(可能与远程SSH-2服务器相同或不同)
 * 
 * @author Christian Plattner, plattner@inf.ethz.ch
 * @version $Id: LocalPortForwarder.java,v 1.5 2006/02/14 19:43:16 cplattne Exp $
 */
public class MyLocalPortForwarder
{
	ChannelManager cm;

	int local_port;

	String host_to_connect;

	int port_to_connect;

	LocalAcceptThread lat;

	MyLocalPortForwarder(ChannelManager cm, int local_port, String host_to_connect, int port_to_connect)
			throws IOException
	{
		this.cm = cm;
		this.local_port = local_port;
		this.host_to_connect = host_to_connect;
		this.port_to_connect = port_to_connect;

		lat = new LocalAcceptThread(cm, local_port, host_to_connect, port_to_connect);
		lat.setDaemon(true);
		lat.start();
	}

	/**
	 * Stop TCP/IP forwarding of newly arriving connections.
	 * 停止新到达连接的TCP/IP转发
	 * 
	 * @throws IOException
	 */
	public void close() throws IOException
	{
		lat.stopWorking();
	}
}


MyLocalStreamForwarder.java

package com;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import ch.ethz.ssh2.channel.Channel;
import ch.ethz.ssh2.channel.ChannelManager;
import ch.ethz.ssh2.channel.LocalAcceptThread;

/**
 * A <code>LocalStreamForwarder</code> forwards an Input- and Outputstream
 * pair via the secure tunnel to another host (which may or may not be identical
 * to the remote SSH-2 server).
 * 
 * @author Christian Plattner, plattner@inf.ethz.ch
 * @version $Id: LocalStreamForwarder.java,v 1.6 2006/02/14 19:43:16 cplattne Exp $
 */
public class MyLocalStreamForwarder
{
	ChannelManager cm;

	String host_to_connect;
	int port_to_connect;
	LocalAcceptThread lat;

	Channel cn;

	MyLocalStreamForwarder(ChannelManager cm, String host_to_connect, int port_to_connect) throws IOException
	{
		this.cm = cm;
		this.host_to_connect = host_to_connect;
		this.port_to_connect = port_to_connect;

		cn = cm.openDirectTCPIPChannel(host_to_connect, port_to_connect, "127.0.0.1", 0);
	}

	/**
	 * @return An <code>InputStream</code> object.
	 * @throws IOException
	 */
	public InputStream getInputStream() throws IOException
	{
		return cn.getStdoutStream();
	}

	/**
	 * Get the OutputStream. Please be aware that the implementation MAY use an
	 * internal buffer. To make sure that the buffered data is sent over the
	 * tunnel, you have to call the <code>flush</code> method of the
	 * <code>OutputStream</code>. To signal EOF, please use the
	 * <code>close</code> method of the <code>OutputStream</code>.
	 * 获取输出流,请注意,实现可以使用内部缓冲区。为了确保缓冲数据通过隧道发送,
	 * 您必须调用<code>OutputStream</code>的<code>flush<code>方法。
	 * 要发出EOF信号,请使用<code>OutputStream</code>的<code>close</code>方法
	 * 
	 * @return An <code>OutputStream</code> object.
	 * @throws IOException
	 */
	public OutputStream getOutputStream() throws IOException
	{
		return cn.getStdinStream();
	}

	/**
	 * Close the underlying SSH forwarding channel and free up resources.
	 * You can also use this method to force the shutdown of the underlying
	 * forwarding channel. Pending output (OutputStream not flushed) will NOT
	 * be sent. Pending input (InputStream) can still be read. If the shutdown
	 * operation is already in progress (initiated from either side), then this
	 * call is a no-op.
	 * 关闭底层SSH转发通道,释放资源。也可以使用此方法强制关闭底层转发通道。将不会发送待处理的输出(OutputStream未刷新)。
	 * 仍然可以读取待处理的输入(ImputStream)。如果关闭操作已在进行中(从任一方启动),则此调用为无操作.
	 * 
	 * @throws IOException
	 */
	public void close() throws IOException
	{
		cm.closeChannel(cn, "Closed due to user request.", true);
	}
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值