?????? 项目中最近要搞个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("<", "<") .replaceAll(">", ">") .replaceAll(" ", " ") .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日志的,这个你们都懂的,好了,希望此文对大家有所帮助。
原文:http://songfeng-123.iteye.com/blog/2237379