最近在做一个远程控制的模块,其中用到了telnet协议,开始用的是apache-net包的telnetclient,但发现问题不少,比较慢,还有就是判断是否read完毕的问题。后来经过讨论打算实现自己的telnet,于是网址打罗了一番,找了一个,但是bug也不少,就开始封装。具体的telnet我已经发过2篇文章了,这里再发布一个深化封装的telnet实现。
仅供参考,可以在windows和linux上运行。
package baby.net.base; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetSocketAddress; import java.net.Socket; import java.util.ArrayList; import org.apache.log4j.Logger; /** * telnet 基本连接类 * * @description * @author weichaofan * @date 2013年10月25日 */ public class TelnetBase { private static final byte SB = (byte) 250;// 子选项开始 private static final byte SE = (byte) 240;// 子选项结束 private static final byte WILL = (byte) 251;// 选项协商 private static final byte WONT = (byte) 252;// 选项协商 private static final byte DO = (byte) 253;// 选项协商 private static final byte DONT = (byte) 254;// 选项协商 private static final byte IAC = (byte) 255;// 数据字节255 private static final byte ECHO = (byte) 1;// 回显 private static final byte IS = (byte) 0;// 是 private static final byte SUPPRESS = (byte) 3;// 抑制继续进行 private static final byte TT = (byte) 24;// 终端类型 private InputStream is; private OutputStream os; private Socket client; private byte[] readBuffer = new byte[20 * 1024]; private int miniReadIntervalMillSec = 3000;// 最短read阻塞间隔时间-毫秒 private int connectTimeout = 1000;// 连接超时时间 private int maxReadTimeout = 5000; public static String[] failTags = { "Failed", "fail", "incorrect" }; public static String[] loginTags = { "$", "#", ">", "ogin", "@" }; public static String[] commondEndTags= { "$", "#", ">"}; public static String[] allTags = { "Failed", "fail", "incorrect", "$", "#", ">", "ogin", "@" }; private String ip; private int port = 23; Logger logger = Logger.getLogger(getClass()); /** * * 打开telnet连接 * * @param ip * @param port * 23 * * @return * * @throws CmdException */ public TelnetBase(String ip) { this(ip, 23); } /** * * 打开telnet连接 * * @param ip * @param port * @return * @throws CmdException */ public TelnetBase(String ip, int port) { this.ip = ip; this.port = port; } /** * 连接 * * @return * @throws Exception */ public String connect() throws Exception { try { client = new Socket(); client.connect(new InetSocketAddress(ip, port), connectTimeout); client.setSoTimeout(miniReadIntervalMillSec);// 设置is的read方法阻塞时间 is = client.getInputStream(); os = client.getOutputStream(); } catch (Exception e) { this.close(); throw new Exception(e); } return readKeyWords("ogin:"); } /** * * 读取回显,并进行telnet协商 * * @return * * @throws IOException */ public String recieveEcho() throws IOException { int len = is.read(this.readBuffer); ArrayList<Byte> bsList = new ArrayList<Byte>(); ArrayList<Byte> cmdList = new ArrayList<Byte>(); for (int i = 0; i < len; i++) { int b = this.readBuffer[i] & 0xff;// &0xff是为了防止byte的255溢出,java中byte的取值是-128~127 if (b != 255) { if (b == '\n' || b == '\0') {// NVT中行结束符以'\r\n'表示,回车以'\r\0表示' continue; } bsList.add((byte) b); continue; } cmdList.add(IAC); switch (this.readBuffer[++i] & 0xff) { case 251:// 服务器想激活某选项 if ((readBuffer[++i] & 0xff) == 1) {// 同意回显 cmdList.add(DO); cmdList.add(ECHO); } else if ((readBuffer[i] & 0xff) == 3) {// 同意抑制继续执行 cmdList.add(DO); cmdList.add(SUPPRESS); // cmdList.add(GA); } else {// 不同意其他类型协商 cmdList.add(DONT); cmdList.add(readBuffer[i]); } break; case 253:// 服务器想让客户端发起激活某选项 if ((readBuffer[++i] & 0xff) == 24) {// 终端类型 cmdList.add(WONT);// 同意激活终端类型协商 cmdList.add(TT); } else if ((readBuffer[i] & 0xff) == 1) { cmdList.add(WILL); cmdList.add(ECHO); } else { cmdList.add(WONT);// 不同意其他类型协商 cmdList.add(readBuffer[i]); } break; case 250:// 子选项开始 cmdList.add(SB); if ((readBuffer[++i] & 0xff) == 24 && (readBuffer[++i] & 0xff) == 1) {// 发送你的终端类型 cmdList.add(TT); cmdList.add(IS);// 我的终端类型是 cmdList.add((byte) 'V'); cmdList.add((byte) 'T'); cmdList.add((byte) '1'); cmdList.add((byte) '0'); cmdList.add((byte) '0'); } break; case 240:// 子选项结束 cmdList.add(SE); break; case 252:// 必须同意 cmdList.add(DONT); cmdList.add(readBuffer[++i]); break; case 254:// 必须同意 cmdList.add(WONT); cmdList.add(readBuffer[++i]); break; } } // 如果有协商则向服务端发送协商选项 if (cmdList.size() > 0) { byte[] writeBuffer = new byte[cmdList.size()]; for (int i = 0; i < cmdList.size(); i++) { writeBuffer[i] = cmdList.get(i); } os.write(writeBuffer); } // 组织回显字符 int size = bsList.size(); String str = ""; if (size > 0) { byte[] bs = new byte[size]; for (int i = 0; i < size; i++) { bs[i] = bsList.get(i).byteValue(); } str = new String(bs, "gbk"); } else { // 如果是协商,则回传协商信息 if (cmdList.size() > 0) { str = recieveEcho(); } } // log(len, cmdList); return str; } private void log(int len, ArrayList<Byte> cmdList) { logger.debug("read===== "); for (int i = 0; i < len; i++) { logger.debug(readBuffer[i] & 0xff); logger.debug(" "); } if (cmdList.size() > 0) { logger.debug("write==== "); for (int i = 0; i < cmdList.size(); i++) { logger.debug(cmdList.get(i) & 0xff); logger.debug(" "); } } } /** * 用户名 命令中不要包括回车、换行 * * @param cmd * @param keyWords * @return */ public String sendUserName(String name) throws Exception { name += "\r\n"; os.write(name.getBytes()); return readKeyWords("assword"); } /** * 密码 命令中不要包括回车、换行 * * @param cmd * @param keyWords * @return */ public String sendUserPwd(String pwd) throws Exception { pwd += "\r\n"; os.write(pwd.getBytes()); return readKeyWords(allTags); } /** * 命令中不要包括回车、换行 * * @param cmd * @param keyWords * @return */ public String sendCmd(String cmd, String... keyWords) throws Exception { return sendCmd(cmd,false,keyWords); } /** * 命令中不要包括回车、换行 * * @param cmd * @param keyWords * @return */ public String sendCmd(String cmd,boolean excludeCommandCheck, String... keyWords) throws Exception { os.write((cmd + "\r\n").getBytes()); if(!excludeCommandCheck){ return readKeyWords(cmd,maxReadTimeout,keyWords); }else{ return readKeyWords(keyWords); } } /** * 命令中不要包括回车、换行 默认搜索条件为$、#、> * 不包含执行命令中的关键字 * @param cmd * @param keyWords * @return */ public String sendCommand(String cmd) throws Exception { return sendCommand(cmd,false); } /** * 命令中不要包括回车、换行 默认搜索条件为$、#、> * 是否包含执行命令中的关键字 * @param cmd * @param keyWords * @return */ public String sendCommand(String cmd,boolean excludeCommandCheck) throws Exception { os.write((cmd + "\r\n").getBytes()); if(!excludeCommandCheck){ return readKeyWords(cmd,maxReadTimeout,commondEndTags); }else{ return readKeyWords(commondEndTags); } } /** * 命令中不要包括回车、换行 默认搜索条件为$、#、> * 不包含执行命令中的关键字 * @param cmd * @param timeOut * @param keyWords * @return */ public String sendCommand(String cmd, long timeOut) throws Exception { return sendCommand(cmd,timeOut, false); } /** * 命令中不要包括回车、换行 默认搜索条件为$、#、> * 是否包含执行命令中的关键字 * @param cmd * @param timeOut * @param keyWords * @return */ public String sendCommand(String cmd, long timeOut,boolean excludeCommandCheck) throws Exception { os.write((cmd + "\r\n").getBytes()); if(!excludeCommandCheck){ return readKeyWords(cmd,timeOut, commondEndTags); }else{ return readKeyWords(timeOut, commondEndTags); } } /** * 命令中不要包括回车、换行 * * @param cmd * @param timeOut * @param keyWords * @return */ public String sendCmd(String cmd, long timeOut, String... keyWords) throws Exception { return sendCmd(cmd,false,timeOut, keyWords); } /** * 命令中不要包括回车、换行 * * @param cmd * @param timeOut * @param keyWords * @return */ public String sendCmd(String cmd, boolean excludeCommandCheck,long timeOut, String... keyWords) throws Exception { os.write((cmd + "\r\n").getBytes()); if(!excludeCommandCheck){ return readKeyWords(cmd,timeOut, keyWords); }else{ return readKeyWords(timeOut, keyWords); } } /** * * 关闭telnet连接 */ public void close() { if (is != null) { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } if (os != null) { try { os.close(); } catch (IOException e) { e.printStackTrace(); } } if (client != null) { try { client.close(); } catch (IOException e) { e.printStackTrace(); } } } /** * * 读取期望值,使用默认超时时间5秒 * * @param keyWords * * @return */ public String readKeyWords(String... keyWords) { return this.readKeyWords(maxReadTimeout, keyWords); } /** * * 读取期望值 * * @param timeOut * 超时时间 * * @param keyWords * * @return * * @throws CmdException */ public String readKeyWords(long timeOut, String... keyWords) { String rv = ""; long nextTime = 0; long endTime = System.currentTimeMillis() + timeOut; do { try { String _rv = this.recieveEcho(); rv += _rv; } catch (IOException e) { nextTime = endTime - System.currentTimeMillis(); } } while (!this.findKeyWord(keyWords, rv) && nextTime >= 0); if (nextTime < 0) System.err.println("Read TimeOut...Echo:\n" + rv); return rv; } /** * * 读取期望值 排除command中含有的关键字 * * @param timeOut * 超时时间 * * @param keyWords * * @return * * @throws CmdException */ public String readKeyWords(String command,long timeOut, String... keyWords) { String rv = ""; long nextTime = 0; long endTime = System.currentTimeMillis() + timeOut; do { try { String _rv = this.recieveEcho(); rv += _rv; } catch (IOException e) { nextTime = endTime - System.currentTimeMillis(); } } while (!this.findKeyWord(command,keyWords, rv) && nextTime >= 0); if (nextTime < 0) System.err.println("Read TimeOut...Echo:\n" + rv); return rv; } /** * * 查找关键字 * * @param keyWords * * @param str * * @return */ public boolean findKeyWord(String[] keyWords, String str) { if (str == null || "".equals(str)) return false; if (keyWords == null || keyWords.length == 0) return true; for (int i = 0; i < keyWords.length; i++) { if (str.indexOf(keyWords[i]) != -1) return true; } return false; } /** * * 查找关键字 排除command中含有的关键字 * * @param keyWords * * @param str * * @return */ public boolean findKeyWord(String command,String[] keyWords, String str) { if (str == null || "".equals(str)) return false; if (keyWords == null || keyWords.length == 0) return true; System.out.println(str); if(-1 != str.indexOf(command)){ str=str.substring(str.indexOf(command)+command.length()); for (int i = 0; i < keyWords.length; i++) { if (str.indexOf(keyWords[i]) != -1) return true; } } return false; } /** * 最短读阻塞时间 * * @return */ public int getMiniReadIntervalMillSec() { return miniReadIntervalMillSec; } public void setMiniReadIntervalMillSec(int miniReadIntervalMillSec) { this.miniReadIntervalMillSec = miniReadIntervalMillSec; } /** * 连接超时时间 * * @return */ public int getConnectTimeout() { return connectTimeout; } public void setConnectTimeout(int connectTimeout) { this.connectTimeout = connectTimeout; } /** * 最大读阻塞时间 * * @return */ public int getMaxReadTimeout() { return maxReadTimeout; } public void setMaxReadTimeout(int maxReadTimeout) { this.maxReadTimeout = maxReadTimeout; } }