如何知道一个网站用什么建设的,网站上传可以通过,翠屏区网站建设,网站建设方案公司功能概述
TelnetCodec用于实现在终端执行telnet指定的编解码功能。
功能分析
核心类TelnetCodec分析
主要成员变量分析
private static final byte[] UP new byte[] {27, 91, 65}; //向上指令private static final byte[] DOWN new byte[] {27, 91, 66}; //向下指令priv…功能概述
TelnetCodec用于实现在终端执行telnet指定的编解码功能。
功能分析
核心类TelnetCodec分析
主要成员变量分析
private static final byte[] UP new byte[] {27, 91, 65}; //向上指令private static final byte[] DOWN new byte[] {27, 91, 66}; //向下指令private static final List? ENTER Arrays.asList( //换行指令 参照ASCII码对照表new byte[] {\r, \n} /* Windows Enter */,new byte[] {\n} /* Linux Enter */);private static final List? EXIT Arrays.asList( //退出对应的字节数组是个二维数组new byte[] {3} /* Windows CtrlC */,new byte[] {-1, -12, -1, -3, 6} /* Linux CtrlC */,new byte[] {-1, -19, -1, -3, 6} /* Linux Pause */);主要成员方法分析
获取字符编码
private static Charset getCharset(Channel channel) {if (channel ! null) {Object attribute channel.getAttribute(CHARSET_KEY); //获取配置的字符集名称if (attribute instanceof String) { //判断是String类型还是Charset类型try {return Charset.forName((String) attribute); //尝试获取指定字符串的字符编码} catch (Throwable t) {logger.warn(t.getMessage(), t);}} else if (attribute instanceof Charset) {return (Charset) attribute;}URL url channel.getUrl(); //远程urlif (url ! null) {String parameter url.getParameter(CHARSET_KEY);if (StringUtils.isNotEmpty(parameter)) {try {return Charset.forName(parameter);} catch (Throwable t) {logger.warn(t.getMessage(), t);}}}}try {return Charset.forName(DEFAULT_CHARSET); //获取默认字符编码“UTF-8”} catch (Throwable t) {logger.warn(t.getMessage(), t);}return Charset.defaultCharset();
}获取字符集的逻辑 从通道Channel的设置的属性值获取。若没有从通道的Url中获取。若还没有则取默认的字符集默认字符集为UTF-8。
响应内容编码
public void encode(Channel channel, ChannelBuffer buffer, Object message) throws IOException { //对响应的内容进行编码if (message instanceof String) { //字符串类型处理if (isClientSide(channel)) {message message \r\n; //客户端输入的内容拼接上换行符}byte[] msgData ((String) message).getBytes(getCharset(channel).name()); //若是字符串直接根据字符集获取字节数组buffer.writeBytes(msgData);} else { //对象类型处理交由父类来处理super.encode(channel, buffer, message);}
}请求内容解码
protected Object decode(Channel channel, ChannelBuffer buffer, int readable, byte[] message) throws IOException { //对许多特殊字符如换行符、退位符进行处理if (isClientSide(channel)) { //若是客户端直接将字节数组转换为字符串return toString(message, getCharset(channel)); //获取字符集并将字符数组转换为字符串}checkPayload(channel, readable);if (message null || message.length 0) { //消息内容为空时不再进行后续处理return DecodeResult.NEED_MORE_INPUT;}if (message[message.length - 1] \b) { // Windows backspace echo \b的值为8try {boolean doublechar message.length 3 message[message.length - 3] 0; // double byte char 判断逻辑消息的长度大于3并且倒数第三个元素数值小于0channel.send(new String(doublechar ? new byte[] {32, 32, 8, 8} : new byte[] {32, 8}, getCharset(channel).name())); //32对应的字符为空格} catch (RemotingException e) {throw new IOException(StringUtils.toString(e));}return DecodeResult.NEED_MORE_INPUT; //需要输入更多的字符}for (Object command : EXIT) {if (isEquals(message, (byte[]) command)) { //判断是否包含退出指令若包含则关闭channelif (logger.isInfoEnabled()) {logger.info(new Exception(Close channel channel on exit command: Arrays.toString((byte[]) command)));}channel.close(); //执行退出指令时会将通道channel关闭return null;}}boolean up endsWith(message, UP);boolean down endsWith(message, DOWN);if (up || down) { //上下键处理对历史记录的处理LinkedListString history (LinkedListString) channel.getAttribute(HISTORY_LIST_KEY);if (CollectionUtils.isEmpty(history)) {return DecodeResult.NEED_MORE_INPUT;}Integer index (Integer) channel.getAttribute(HISTORY_INDEX_KEY); //取出历史记录索引Integer old index;if (index null) {index history.size() - 1; //若没设置索引则取列表中的最后一条} else {if (up) { //执行向上操作index index - 1;if (index 0) {index history.size() - 1; //如果索引小于0则轮询到最后一条}} else { //执行向下操作index index 1;if (index history.size() - 1) {//如果所以大于最后一条则轮询到第一条index 0;}}}if (old null || !old.equals(index)) { //表示old不为空或old与index不相等channel.setAttribute(HISTORY_INDEX_KEY, index);String value history.get(index);if (old ! null old 0 old history.size()) {String ov history.get(old);StringBuilder buf new StringBuilder();for (int i 0; i ov.length(); i) {buf.append(\b);}for (int i 0; i ov.length(); i) {buf.append( );}for (int i 0; i ov.length(); i) {buf.append(\b);}value buf.toString() value;}try {channel.send(value);} catch (RemotingException e) {throw new IOException(StringUtils.toString(e));}}return DecodeResult.NEED_MORE_INPUT;}for (Object command : EXIT) {if (isEquals(message, (byte[]) command)) { //若是结束符判断是否与结束符相等if (logger.isInfoEnabled()) {logger.info(new Exception(Close channel channel on exit command command));}channel.close();return null;}}byte[] enter null;for (Object command : ENTER) {if (endsWith(message, (byte[]) command)) {//若是换行符判断是否是以换行符结尾enter (byte[]) command; //将换行符存下来break;}}if (enter null) { //需要有换行符结尾没有的话就不往下进行return DecodeResult.NEED_MORE_INPUT;}LinkedListString history (LinkedListString) channel.getAttribute(HISTORY_LIST_KEY);Integer index (Integer) channel.getAttribute(HISTORY_INDEX_KEY);channel.removeAttribute(HISTORY_INDEX_KEY); //使用过后将HISTORY_INDEX_KEY历史记录所以移除if (CollectionUtils.isNotEmpty(history) index ! null index 0 index history.size()) {String value history.get(index);if (value ! null) {byte[] b1 value.getBytes();byte[] b2 new byte[b1.length message.length];System.arraycopy(b1, 0, b2, 0, b1.length);System.arraycopy(message, 0, b2, b1.length, message.length);message b2;}}String result toString(message, getCharset(channel));if (result.trim().length() 0) {if (history null) {history new LinkedListString();channel.setAttribute(HISTORY_LIST_KEY, history); //指令正常执行后就会写入通道的历史指令列表}if (history.isEmpty()) {history.addLast(result); //写入历史指令列表} else if (!result.equals(history.getLast())) {history.remove(result);history.addLast(result);if (history.size() 10) {history.removeFirst();}}}return result;
}上下键本意上是对历史指令的支持但是不同平台的支持不一样比如Mac就会附加的UP的字节数组为[27,91,65,13,10]把换行符加上了就导致不是以UP结尾就失效了。
辅助类AbstractCodec分析
主要成员方法分析
检查负载大小
protected static void checkPayload(Channel channel, long size) throws IOException { //检查负载大小请求体的数据大小int payload Constants.DEFAULT_PAYLOAD; //默认负载大小为8 * 1024 * 1024即8M;if (channel ! null channel.getUrl() ! null) {payload channel.getUrl().getParameter(Constants.PAYLOAD_KEY, Constants.DEFAULT_PAYLOAD);}if (payload 0 size payload) { //超过负载则抛出异常ExceedPayloadLimitException e new ExceedPayloadLimitException(Data length too large: size , max payload: payload , channel: channel);logger.error(e);throw e;}
}判断是否是客户端
protected boolean isClientSide(Channel channel) { //判断是否是客户端String side (String) channel.getAttribute(SIDE_KEY);if (CLIENT_SIDE.equals(side)) { //判断通道中属性SIDE_KEY的缓存值return true;} else if (SERVER_SIDE.equals(side)) {return false;} else {InetSocketAddress address channel.getRemoteAddress();URL url channel.getUrl(); //远程url即服务端的urlboolean isClient url.getPort() address.getPort() NetUtils.filterLocalHost(url.getIp()).equals(NetUtils.filterLocalHost(address.getAddress().getHostAddress())); //比较逻辑当通道中的远程地址与url地址比较如果远程是服务端那么当前就是客户端反之类推也可以按localAddress来判断channel.setAttribute(SIDE_KEY, isClient ? CLIENT_SIDE: SERVER_SIDE);return isClient;}
}辅助类TelnetHandlerAdapter分析
主要成员方法分析
指令调用
public String telnet(Channel channel, String message) throws RemotingException { //Telnet指令处理String prompt channel.getUrl().getParameterAndDecoded(Constants.PROMPT_KEY, Constants.DEFAULT_PROMPT);boolean noprompt message.contains(--no-prompt);message message.replace(--no-prompt, ); //telnet提示键如果做了配置将不显示dubboStringBuilder buf new StringBuilder();message message.trim();String command;if (message.length() 0) { //message不为空字符串时解析指令和执行的内容输入回车键会收到空字符串int i message.indexOf( ); //接收指令时收到的message不包含提示符如dubbo ls收到的message为lsif (i 0) { //拆分命令和参数command message.substring(0, i).trim();message message.substring(i 1).trim();} else {command message;message ;}} else {command ;}if (command.length() 0) { //telnet输入回车时写到通道的内容为空字符串不会进入此处逻辑if (extensionLoader.hasExtension(command)) { //将命令名作为SPI的扩展名if (commandEnabled(channel.getUrl(), command)) {try {String result extensionLoader.getExtension(command).telnet(channel, message); //获取指定命令的实例并将结果写到channelif (result null) {return null;}buf.append(result);} catch (Throwable t) {buf.append(t.getMessage());}} else {buf.append(Command: );buf.append(command);buf.append( disabled);}} else {buf.append(Unsupported command: );buf.append(command);}}if (buf.length() 0) {buf.append(\r\n); //\r 回车符\n 换行符用回车符换行符结束指令}if (StringUtils.isNotEmpty(prompt) !noprompt) { //提示键处理若提示键内容不为空且没有禁用则作对应展示buf.append(prompt);}return buf.toString(); //响应给telnet客户端的内容
}
问题点答疑 TelnetHandler处理接口有多个实现类即telnet命令会有多个如ListTelnetHandler列表指令InvokeTelnetHandler调用指令那么不同指令是怎么分发的 解答通过实现类TelnetHandlerAdapter将输入的字符串message进行解析获取到指令名以及执行内容再把指令名称作为SPI的扩展名根据SPI机制获取到对应的实例。 telnet中的提示键可以设置自定义提示符吗怎么设置 解答可以自定义提示符通过dubbo:parameter/参数配置设置。
归纳总结
Telnet协议是TCP/IP协议族中的一员是Internet远程登陆服务的标准协议。Telnet协议的目的是提供一个相对通用的双向的面向八位字节的通信方法允许界面终端设备和面向终端的过程能通过一个标准过程进行互相交互。应用Telnet协议能够把本地用户所使用的计算机变成远程主机系统的一个终端