浏览器远程telnet登陆

       项目中最近要搞个telnet远程登陆,需要浏览器进行实时交互。我们都知道,浏览器的http协议是无状态的,一次请求,三次握手,状态肯定是保证不了的,那到底该怎么办呢?

       现在比较前卫的技术,例如websocket可以做到这一点,但是websocket是基于HTML5开发的,IE9以下版本不能支持,而且需要在tomcat7.0.47以上版本运行,而LZ项目组使用的是1.6版本,因此最终被LZ排除在外。那最笨的也是最有效的就是轮循(polling)了,就是定时刷新,采用ajax局部请求和刷新相关html页面。

       贴个完成的页面给大伙瞅瞅,html样式,是个弹出框。。。

 

       java连接telnet的代码还是比较简单的,commons-net-2.0.jar是工程依赖包,文字后贴上代码。当然,调试过程中,遇到了很多莫名其妙的问题。

       1、首先遇到的困难是,输入命令后出现了---- More ----,噩梦啊,一页居然显示不完,LZ试着发送'\r\n'模拟回车,一行一行的读取,后来,LZ发现发送空格可以一次翻一页,原谅我linux小白。。。总算解决了问题。

       2、接着就出现了一断乱码字符'x[42D'(x打不出来),百思不得其解,一个一个字符打印出来,居然是char=27,脱离操作,长见识了,出现more以后输入命令后换行,开头都会多这么一个鸟东西,而且是跟着52个无用字符,那么,过滤掉,ok。

       3、然后就是时不时的假死,特别郁闷,打开JDK文档,里面是这么写的,InputStream。

public abstract int read()throws IOException
从输入流中读取数据的下一个字节。返回 0 到 255 范围内的 int 字节值。如果因为已经到达流末尾
而没有可用的字节,则返回值 -1。在输入数据可用、检测到流末尾或者抛出异常前,此方法一直阻塞。

 从输入流中读取数据的下一个字节。返原来我们读取普通的文件的时候,都是一次性读取,流是以-1作为读取文件的结束,不会遇到阻塞的问题。而temlet是长连接,是Socket连接的一种,此时的流是一直可用的,所以很可能出现阻塞问题,所以读取的时候一定要自己增加结束判断,或者使用in.available()判断是否有可用资源,然后再使用in.read()方法。

       4、我们都知道浏览器多次请求,肯定是多线程的,那么就需要有一个管理类去管理这些连接,管理类就不贴了,就是一个单例的map管理类,我是用户名+时间戳作为交互的key进行定位的,存储在map中,telnet工具类内部也有超时线程,10分钟后断开连接,防止占用过多的内存。

import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;

import org.apache.commons.net.telnet.TelnetClient;
import org.apache.log4j.Logger;

public class TelnetClientUtil
{
	/**
	 * log4j日志记录器
	 */
	private static final Logger log = Logger.getLogger(TelnetClientUtil.class);
	
	/**
	 * telnet客户端
	 */
	private TelnetClient telnetClient = new TelnetClient();
	
	/** 
	 * 输入流,接收返回信息 
	 */
	private InputStream in;
	
	/** 
	 * 向 服务器写入 命令 
	 */
	private PrintStream out;
	
	/**
	 * 分割符
	 */
	private char prompt = '$';
	
	/**
	 * 是否连接
	 */
	public boolean isconnected = false;

	public void logout()
	{
		try
		{
			if(telnetClient.isConnected())
			{
				telnetClient.disconnect();
			}
			isconnected = false;
		}
		catch (IOException e)
		{
			log.info(e);
		}
	}
	
	public TelnetClientUtil(String ip, int port, String user, String password)
	{
		try
		{
			telnetClient.setConnectTimeout(30000);
			//telnetClient.setSoTimeout(30000);
			telnetClient.connect(ip, port);
			in = telnetClient.getInputStream();
			out = new PrintStream(telnetClient.getOutputStream());
			//this.prompt = user.equals("root") ? '#' : '$';

			if(getResponse(":").contains("Username:"))
			{
				write(user);
			}
			if(getResponse(":").contains("Password:"))
			{
				write(password);
			}
			if(getResponse(">").contains(">"))
			{
				log.info("[telnet] connect success!");
				//启动线程监控,断开无用连接
				isconnected = true;
				new Thread(new TimeoutThread()).start();
			}
			else
			{
				log.info("[telnet] connect error: connect to [" + ip + ":" + port + "] fail!");
				logout();
			}
		}
		catch (Exception e)
		{
			log.info("[telnet] connect error: connect to [" + ip + ":" + port + "] fail!");
			logout();
		}
	}
	
	/**
	 * 发送命令无返回值
	 * @param command
	 * @return
	 */
	private void write(String command)
	{
		try
		{
			out.println(command);
			out.flush();
		}
		catch (Exception e)
		{
			log.info(e);
		}
	}
	
	/**
	 * 发送指令
	 * @param command
	 * @param ip
	 * @param key
	 * @return
	 */
	public String sendCommand(String command, String ip, String key)
	{
		try
		{
			write(command);
			String response = getResponse(">");
			log.info("[telnet] response: [" + response + "], ip=[" + ip + "], key=[" + key + "]");
			return "<br/>" + response.replaceAll("<", "&lt;")
										.replaceAll(">", "&gt;")
										.replaceAll(" ", "&nbsp;")
										.replaceAll("\n", "<br/>");
		}
		catch (Exception e)
		{
			log.info(e);
		}
		return null;
	}

	/**
	 * 获取返回结果
	 * @param pattern
	 * @return
	 */
	private String getResponse(String pattern)
	{
		try
		{
			char lastChar = pattern.charAt(pattern.length() - 1);
			 
			StringBuffer sb = new StringBuffer();
			String content = "";
			char ch;
			while(sb.length() < 4000)
			{
				ch = (char) in.read();
				sb.append(ch);
				if (ch == lastChar && sb.toString().endsWith(pattern))
				{
					break;
				}
				dealWithMore(sb);
			}
			content = sb.toString();
			
			//读完的所有的流数据
			log.info("in.available size is" + in.available());
			while(in.available() != 0)
			{
				ch = (char) in.read();
				sb.append(ch);
				dealWithMore(sb);
			}
			
			return content;
		}
		catch (Throwable e)
		{
			log.info(e);
		}
		return null;
	}
	
	/**
	 * 处理出现more更多的场景
	 * @param sb
	 * @throws Exception
	 */
	private void dealWithMore(StringBuffer sb) throws Exception
	{
		if(sb.toString().endsWith("---- More ----"))
		{
			log.info("enter [space] to continue.");
			int index = sb.indexOf("  ---- More ----");
			sb.delete(index, index + 16);
			write(" ");
			Thread.sleep(100);
			//more命令后输入回车,会有一个脱离操作,char=27,默认过滤掉52个无用字符
			for(int i = 0; i < 52; i++)
			{
				in.read();
			}
			log.info("skip [pause] to continue.");
		}
	}
	
	
	class TimeoutThread implements Runnable
	{
		@Override
		public void run()
		{
			long startTime = System.currentTimeMillis();
			while(true)
			{
				//超过10分钟断开连接
				if(System.currentTimeMillis() - startTime > 10 * 60 * 1000)
				{
					log.info("telnet超时,断开连接!");
					logout();
					break;
				}
				try
				{
					//每一分钟检测一次
					Thread.sleep(1000 * 60);
				}
				catch (InterruptedException e)
				{
					e.printStackTrace();
				}
			}
		}
		
	}
}
        最后不得不提一点,安全性的问题,当然这个需要用户那边去保证,万一用户发送了什么不该发送的命令,呵呵,所有操作都要log日志的,这个你们都懂的,好了,希望此文对大家有所帮助。

猜你喜欢

转载自songfeng-123.iteye.com/blog/2237379