?????? 项目中最近要搞个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