Netty是一个高性能的NIO通信框架,提供异步的、事件驱动的网络编程模型。使用Netty可以方便用户开发各种常用协议的网络程序。例如:TCP、UDP、HTTP等等。
Netty的最新版本是3.2.7,官网地址是: http://www.jboss.org/netty
本文的主要目的是基于Netty实现一个通用二进制协议的高效数据传输。协议是通用的二进制协议,高效并且扩展性很好。
一个好的协议有两个标准:
(1)生成的传输数据要少,即数据压缩比要高。这样可以减少网络开销。
(2)传输数据和业务对象之间的转换速度要快。
一、协议的定义
无论是请求还是响应,报文都由一个通用报文头和实际数据组成。报文头在前,数据在后。
(1)报文头:由数据解析类型,数据解析方法,编码,扩展字节,包长度组成,共16个字节:
编码方式(1byte)、加密(1byte)、扩展1(1byte)、扩展2(1byte)、会话ID(4byte)、命令或者结果码(4byte)、数据包长(4byte)
(2)数据:由数据包长指定。请求或回复数据。类型对应为JAVA的Map<String,String>
数据格式定义:
字段1键名长度 字段1键名 字段1值长度 字段1值
字段2键名长度 字段2键名 字段2值长度 字段2值
字段3键名长度 字段3键名 字段3值长度 字段3值
… … … …
长度为整型,占4个字节
代码中用两个Vo对象来表示:XLRequest和XLResponse。
XLReponse.java
package org.jboss.netty.example.xlsvr.vo;
import java.util.HashMap;
import java.util.Map;
/**
* @author hankchen
* 2012-2-3 下午02:46:52
*/
/**
* 响应数据
*/
/**
* 通用协议介绍
*
* 通用报文格式:无论是请求还是响应,报文都由一个通用报文头和实际数据组成。报文头在前,数据在后
* (1)报文头:由数据解析类型,数据解析方法,编码,扩展字节,包长度组成,共16个字节:
* 编码方式(1byte)、加密(1byte)、扩展1(1byte)、扩展2(1byte)、会话ID(4byte)、命令或者结果码(4byte)、包长(4byte)
* (2)数据:由包长指定。请求或回复数据。类型对应为JAVA的Map<String,String>
* 数据格式定义:
* 字段1键名长度 字段1键名 字段1值长度 字段1值
* 字段2键名长度 字段2键名 字段2值长度 字段2值
* 字段3键名长度 字段3键名 字段3值长度 字段3值
* … … … …
* 长度为整型,占4个字节
*/
public class XLResponse {
private byte encode;// 数据编码格式。已定义:0:UTF-8,1:GBK,2:GB2312,3:ISO8859-1
private byte encrypt;// 加密类型。0表示不加密
private byte extend1;// 用于扩展协议。暂未定义任何值
private byte extend2;// 用于扩展协议。暂未定义任何值
private int sessionid;// 会话ID
private int result;// 结果码
private int length;// 数据包长
private Map<String,String> values=new HashMap<String, String>();
private String ip;
public void setValue(String key,String value){
values.put(key, value);
}
public String getValue(String key){
if (key==null) {
return null;
}
return values.get(key);
}
public byte getEncode() {
return encode;
}
public void setEncode(byte encode) {
this.encode = encode;
}
public byte getEncrypt() {
return encrypt;
}
public void setEncrypt(byte encrypt) {
this.encrypt = encrypt;
}
public byte getExtend1() {
return extend1;
}
public void setExtend1(byte extend1) {
this.extend1 = extend1;
}
public byte getExtend2() {
return extend2;
}
public void setExtend2(byte extend2) {
this.extend2 = extend2;
}
public int getSessionid() {
return sessionid;
}
public void setSessionid(int sessionid) {
this.sessionid = sessionid;
}
public int getResult() {
return result;
}
public void setResult(int result) {
this.result = result;
}
public int getLength() {
return length;
}
public void setLength(int length) {
this.length = length;
}
public Map<String, String> getValues() {
return values;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public void setValues(Map<String, String> values) {
this.values = values;
}
@Override
public String toString() {
return "XLResponse [encode=" + encode + ", encrypt=" + encrypt + ", extend1=" + extend1 + ", extend2=" + extend2
+ ", sessionid=" + sessionid + ", result=" + result + ", length=" + length + ", values=" + values + ", ip=" + ip + "]";
}
}
XLRequest.java
package org.jboss.netty.example.xlsvr.vo;
import java.util.HashMap;
import java.util.Map;
/**
* @author hankchen
* 2012-2-3 下午02:46:41
*/
/**
* 请求数据
*/
/**
* 通用协议介绍
*
* 通用报文格式:无论是请求还是响应,报文都由一个通用报文头和实际数据组成。报文头在前,数据在后
* (1)报文头:由数据解析类型,数据解析方法,编码,扩展字节,包长度组成,共16个字节:
* 编码方式(1byte)、加密(1byte)、扩展1(1byte)、扩展2(1byte)、会话ID(4byte)、命令或者结果码(4byte)、包长(4byte)
* (2)数据:由包长指定。请求或回复数据。类型对应为JAVA的Map<String,String>
* 数据格式定义:
* 字段1键名长度 字段1键名 字段1值长度 字段1值
* 字段2键名长度 字段2键名 字段2值长度 字段2值
* 字段3键名长度 字段3键名 字段3值长度 字段3值
* … … … …
* 长度为整型,占4个字节
*/
public class XLRequest {
private byte encode;// 数据编码格式。已定义:0:UTF-8,1:GBK,2:GB2312,3:ISO8859-1
private byte encrypt;// 加密类型。0表示不加密
private byte extend1;// 用于扩展协议。暂未定义任何值
private byte extend2;// 用于扩展协议。暂未定义任何值
private int sessionid;// 会话ID
private int command;// 命令
private int length;// 数据包长
private Map<String,String> params=new HashMap<String, String>(); //参数
private String ip;
public byte getEncode() {
return encode;
}
public void setEncode(byte encode) {
this.encode = encode;
}
public byte getEncrypt() {
return encrypt;
}
public void setEncrypt(byte encrypt) {
this.encrypt = encrypt;
}
public byte getExtend1() {
return extend1;
}
public void setExtend1(byte extend1) {
this.extend1 = extend1;
}
public byte getExtend2() {
return extend2;
}
public void setExtend2(byte extend2) {
this.extend2 = extend2;
}
public int getSessionid() {
return sessionid;
}
public void setSessionid(int sessionid) {
this.sessionid = sessionid;
}
public int getCommand() {
return command;
}
public void setCommand(int command) {
this.command = command;
}
public int getLength() {
return length;
}
public void setLength(int length) {
this.length = length;
}
public Map<String, String> getParams() {
return params;
}
public void setValue(String key,String value){
params.put(key, value);
}
public String getValue(String key){
if (key==null) {
return null;
}
return params.get(key);
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public void setParams(Map<String, String> params) {
this.params = params;
}
@Override
public String toString() {
return "XLRequest [encode=" + encode + ", encrypt=" + encrypt + ", extend1=" + extend1 + ", extend2=" + extend2
+ ", sessionid=" + sessionid + ", command=" + command + ", length=" + length + ", params=" + params + ", ip=" + ip + "]";
}
}
二、协议的编码和解码
对于自定义二进制协议,编码解码器往往是Netty开发的重点。这里直接给出相关类的代码。
XLServerEncoder.java
package org.jboss.netty.example.xlsvr.codec;
import java.nio.ByteBuffer;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelDownstreamHandler;
import org.jboss.netty.example.xlsvr.util.ProtocolUtil;
import org.jboss.netty.example.xlsvr.vo.XLResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author hankchen
* 2012-2-3 上午10:48:15
*/
/**
* 服务器端编码器
*/
public class XLServerEncoder extends SimpleChannelDownstreamHandler {
Logger logger=LoggerFactory.getLogger(XLServerEncoder.class);
@Override
public void writeRequested(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
XLResponse response=(XLResponse)e.getMessage();
ByteBuffer headBuffer=ByteBuffer.allocate(16);
/**
* 先组织报文头
*/
headBuffer.put(response.getEncode());
headBuffer.put(response.getEncrypt());
headBuffer.put(response.getExtend1());
headBuffer.put(response.getExtend2());
headBuffer.putInt(response.getSessionid());
headBuffer.putInt(response.getResult());
/**
* 组织报文的数据部分
*/
ChannelBuffer dataBuffer=ProtocolUtil.encode(response.getEncode(),response.getValues());
int length=dataBuffer.readableBytes();
headBuffer.putInt(length);
/**
* 非常重要
* ByteBuffer需要手动flip(),ChannelBuffer不需要
*/
headBuffer.flip();
ChannelBuffer totalBuffer=ChannelBuffers.dynamicBuffer();
totalBuffer.writeBytes(headBuffer);
logger.info("totalBuffer size="+totalBuffer.readableBytes());
totalBuffer.writeBytes(dataBuffer);
logger.info("totalBuffer size="+totalBuffer.readableBytes());
Channels.write(ctx, e.getFuture(), totalBuffer);
}
}
XLClientDecoder.java
package org.jboss.netty.example.xlsvr.codec;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.example.xlsvr.util.ProtocolUtil;
import org.jboss.netty.example.xlsvr.vo.XLResponse;
import org.jboss.netty.handler.codec.frame.FrameDecoder;
/**
* @author hankchen
* 2012-2-3 上午10:47:54
*/
/**
* 客户端解码器
*/
public class XLClientDecoder extends FrameDecoder {
@Override
protected Object decode(ChannelHandlerContext context, Channel channel, ChannelBuffer buffer) throws Exception {
if (buffer.readableBytes()<16) {
return null;
}
buffer.markReaderIndex();
byte encode=buffer.readByte();
byte encrypt=buffer.readByte();
byte extend1=buffer.readByte();
byte extend2=buffer.readByte();
int sessionid=buffer.readInt();
int result=buffer.readInt();
int length=buffer.readInt(); // 数据包长
if (buffer.readableBytes()<length) {
buffer.resetReaderIndex();
return null;
}
ChannelBuffer dataBuffer=ChannelBuffers.buffer(length);
buffer.readBytes(dataBuffer, length);
XLResponse response=new XLResponse();
response.setEncode(encode);
response.setEncrypt(encrypt);
response.setExtend1(extend1);
response.setExtend2(extend2);
response.setSessionid(sessionid);
response.setResult(result);
response.setLength(length);
response.setValues(ProtocolUtil.decode(encode, dataBuffer));
response.setIp(ProtocolUtil.getClientIp(channel));
return response;
}
}
ProtocolUtil.java
package org.jboss.netty.example.xlsvr.util;
import java.net.SocketAddress;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.Channel;
/**
* @author hankchen
* 2012-2-4 下午01:57:33
*/
public class ProtocolUtil {
/**
* 编码报文的数据部分
* @param encode
* @param values
* @return
*/
public static ChannelBuffer encode(int encode,Map<String,String> values){
ChannelBuffer totalBuffer=null;
if (values!=null && values.size()>0) {
totalBuffer=ChannelBuffers.dynamicBuffer();
int length=0,index=0;
ChannelBuffer [] channelBuffers=new ChannelBuffer[values.size()];
Charset charset=XLCharSetFactory.getCharset(encode);
for(Entry<String,String> entry:values.entrySet()){
String key=entry.getKey();
String value=entry.getValue();
ChannelBuffer buffer=ChannelBuffers.dynamicBuffer();
buffer.writeInt(key.length());
buffer.writeBytes(key.getBytes(charset));
buffer.writeInt(value.length());
buffer.writeBytes(value.getBytes(charset));
channelBuffers[index++]=buffer;
length+=buffer.readableBytes();
}
for (int i = 0; i < channelBuffers.length; i++) {
totalBuffer.writeBytes(channelBuffers[i]);
}
}
return totalBuffer;
}
/**
* 解码报文的数据部分
* @param encode
* @param dataBuffer
* @return
*/
public static Map<String,String> decode(int encode,ChannelBuffer dataBuffer){
Map<String,String> dataMap=new HashMap<String, String>();
if (dataBuffer!=null && dataBuffer.readableBytes()>0) {
int processIndex=0,length=dataBuffer.readableBytes();
Charset charset=XLCharSetFactory.getCharset(encode);
while(processIndex<length){
/**
* 获取Key
*/
int size=dataBuffer.readInt();
byte [] contents=new byte [size];
dataBuffer.readBytes(contents);
String key=new String(contents, charset);
processIndex=processIndex+size+4;
/**
* 获取Value
*/
size=dataBuffer.readInt();
contents=new byte [size];
dataBuffer.readBytes(contents);
String value=new String(contents, charset);
dataMap.put(key, value);
processIndex=processIndex+size+4;
}
}
return dataMap;
}
/**
* 获取客户端IP
* @param channel
* @return
*/
public static String getClientIp(Channel channel){
/**
* 获取客户端IP
*/
SocketAddress address = channel.getRemoteAddress();
String ip = "";
if (address != null) {
ip = address.toString().trim();
int index = ip.lastIndexOf(':');
if (index < 1) {
index = ip.length();
}
ip = ip.substring(1, index);
}
if (ip.length() > 15) {
ip = ip.substring(Math.max(ip.indexOf("/") + 1, ip.length() - 15));
}
return ip;
}
}
三、服务器端实现
服务器端提供的功能是:
1、接收客户端的请求(非关闭命令),返回XLResponse类型的数据。
2、如果客户端的请求是关闭命令:shutdown,则服务器端关闭自身进程。
为了展示多协议的运用,这里客户端的请求采用的是基于问本行(\n\r)的协议。
具体代码如下:
package org.jboss.netty.example.xlsvr;
import java.net.InetSocketAddress;
import java.util.concurrent.Executors;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.group.ChannelGroup;
import org.jboss.netty.channel.group.ChannelGroupFuture;
import org.jboss.netty.channel.group.DefaultChannelGroup;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.example.xlsvr.codec.XLServerEncoder;
import org.jboss.netty.handler.codec.frame.DelimiterBasedFrameDecoder;
import org.jboss.netty.handler.codec.frame.Delimiters;
import org.jboss.netty.handler.codec.string.StringDecoder;
import org.jboss.netty.util.CharsetUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author hankchen
* 2012-1-30 下午03:21:38
*/
public class XLServer {
public static final int port =8080;
public static final Logger logger=LoggerFactory.getLogger(XLServer.class);
public static final ChannelGroup allChannels=new DefaultChannelGroup("XLServer");
private static final ServerBootstrap serverBootstrap=new ServerBootstrap(new NioServerSocketChannelFactory(Executors.newCachedThreadPool(), Executors.newCachedThreadPool()));
public static void main(String [] args){
try {
XLServer.startup();
} catch (Exception e) {
e.printStackTrace();
}
}
public static boolean startup() throws Exception{
/**
* 采用默认ChannelPipeline管道
* 这意味着同一个XLServerHandler实例将被多个Channel通道共享
* 这种方式对于XLServerHandler中无有状态的成员变量是可以的,并且可以提高性能!
*/
ChannelPipeline pipeline=serverBootstrap.getPipeline();
/**
* 解码器是基于文本行的协议,\r\n或者\n\r
*/
pipeline.addLast("frameDecoder", new DelimiterBasedFrameDecoder(80, Delimiters.lineDelimiter()));
pipeline.addLast("stringDecoder", new StringDecoder(CharsetUtil.UTF_8));
pipeline.addLast("encoder", new XLServerEncoder());
pipeline.addLast("handler", new XLServerHandler());
serverBootstrap.setOption("child.tcpNoDelay", true); //注意child前缀
serverBootstrap.setOption("child.keepAlive", true); //注意child前缀
/**
* ServerBootstrap对象的bind方法返回了一个绑定了本地地址的服务端Channel通道对象
*/
Channel channel=serverBootstrap.bind(new InetSocketAddress(port));
allChannels.add(channel);
logger.info("server is started on port "+port);
return false;
}
public static void shutdown() throws Exception{
try {
/**
* 主动关闭服务器
*/
ChannelGroupFuture future=allChannels.close();
future.awaitUninterruptibly();//阻塞,直到服务器关闭
//serverBootstrap.releaseExternalResources();
} catch (Exception e) {
e.printStackTrace();
logger.error(e.getMessage(),e);
}
finally{
logger.info("server is shutdown on port "+port);
System.exit(1);
}
}
}
XLServerHandler.java
package org.jboss.netty.example.xlsvr;
import java.util.Random;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelHandler.Sharable;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
import org.jboss.netty.example.xlsvr.vo.XLResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author hankchen
* 2012-1-30 下午03:22:24
*/
@Sharable
public class XLServerHandler extends SimpleChannelHandler {
private static final Logger logger=LoggerFactory.getLogger(XLServerHandler.class);
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
logger.info("messageReceived");
if (e.getMessage() instanceof String) {
String content=(String)e.getMessage();
logger.info("content is "+content);
if ("shutdown".equalsIgnoreCase(content)) {
//e.getChannel().close();
XLServer.shutdown();
}else {
sendResponse(ctx);
}
}else {
logger.error("message is not a String.");
e.getChannel().close();
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
logger.error(e.getCause().getMessage(),e.getCause());
e.getCause().printStackTrace();
e.getChannel().close();
}
@Override
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
logger.info("channelConnected");
sendResponse(ctx);
}
@Override
public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
logger.info("channelClosed");
//删除通道
XLServer.allChannels.remove(e.getChannel());
}
@Override
public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
logger.info("channelDisconnected");
super.channelDisconnected(ctx, e);
}
@Override
public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
logger.info("channelOpen");
//增加通道
XLServer.allChannels.add(e.getChannel());
}
/**
* 发送响应内容
* @param ctx
* @param e
* @return
*/
private ChannelFuture sendResponse(ChannelHandlerContext ctx){
Channel channel=ctx.getChannel();
Random random=new Random();
XLResponse response=new XLResponse();
response.setEncode((byte)0);
response.setResult(1);
response.setValue("name","hankchen");
response.setValue("time", String.valueOf(System.currentTimeMillis()));
response.setValue("age",String.valueOf(random.nextInt()));
/**
* 发送接收信息的时间戳到客户端
* 注意:Netty中所有的IO操作都是异步的!
*/
ChannelFuture future=channel.write(response); //发送内容
return future;
}
}
四、客户端实现
客户端的功能是连接服务器,发送10次请求,然后发送关闭服务器的命令,最后主动关闭客户端。
关键代码如下:
/**
* Copyright (C): 2012
* @author hankchen
* 2012-1-30 下午03:21:26
*/
/**
* 服务器特征:
* 1、使用专用解码器解析服务器发过来的数据
* 2、客户端主动关闭连接
*/
public class XLClient {
public static final int port =XLServer.port;
public static final String host ="localhost";
private static final Logger logger=LoggerFactory.getLogger(XLClient.class);
private static final NioClientSocketChannelFactory clientSocketChannelFactory=new NioClientSocketChannelFactory(Executors.newCachedThreadPool(),Executors.newCachedThreadPool());
private static final ClientBootstrap clientBootstrap=new ClientBootstrap(clientSocketChannelFactory);
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
ChannelFuture future=XLClient.startup();
logger.info("future state is "+future.isSuccess());
}
/**
* 启动客户端
* @return
* @throws Exception
*/
public static ChannelFuture startup() throws Exception {
/**
* 注意:由于XLClientHandler中有状态的成员变量,因此不能采用默认共享ChannelPipeline的方式
* 例如,下面的代码形式是错误的:
* ChannelPipeline pipeline=clientBootstrap.getPipeline();
* pipeline.addLast("handler", new XLClientHandler());
*/
clientBootstrap.setPipelineFactory(new XLClientPipelineFactory()); //只能这样设置
/**
* 请注意,这里不存在使用“child.”前缀的配置项,客户端的SocketChannel实例不存在父级Channel对象
*/
clientBootstrap.setOption("tcpNoDelay", true);
clientBootstrap.setOption("keepAlive", true);
ChannelFuture future=clientBootstrap.connect(new InetSocketAddress(host, port));
/**
* 阻塞式的等待,直到ChannelFuture对象返回这个连接操作的成功或失败状态
*/
future.awaitUninterruptibly();
/**
* 如果连接失败,我们将打印连接失败的原因。
* 如果连接操作没有成功或者被取消,ChannelFuture对象的getCause()方法将返回连接失败的原因。
*/
if (!future.isSuccess()) {
future.getCause().printStackTrace();
}else {
logger.info("client is connected to server "+host+":"+port);
}
return future;
}
/**
* 关闭客户端
* @param future
* @throws Exception
*/
public static void shutdown(ChannelFuture future) throws Exception{
try {
/**
* 主动关闭客户端连接,会阻塞等待直到通道关闭
*/
future.getChannel().close().awaitUninterruptibly();
//future.getChannel().getCloseFuture().awaitUninterruptibly();
/**
* 释放ChannelFactory通道工厂使用的资源。
* 这一步仅需要调用 releaseExternalResources()方法即可。
* 包括NIO Secector和线程池在内的所有资源将被自动的关闭和终止。
*/
clientBootstrap.releaseExternalResources();
} catch (Exception e) {
e.printStackTrace();
logger.error(e.getMessage(),e);
}
finally{
System.exit(1);
logger.info("client is shutdown to server "+host+":"+port);
}
}
}
XLClientPipelineFactory.java
public class XLClientPipelineFactory implements ChannelPipelineFactory{
@Override
public ChannelPipeline getPipeline() throws Exception {
ChannelPipeline pipeline=Channels.pipeline();
/**
* 使用专用的解码器,解决数据分段的问题
* 从业务逻辑代码中分离协议处理部分总是一个很不错的想法。
*/
pipeline.addLast("decoder", new XLClientDecoder());
/**
* 有专门的编码解码器,这时处理器就不需要管数据分段和数据格式问题,只需要关注业务逻辑了!
*/
pipeline.addLast("handler", new XLClientHandler());
return pipeline;
}
}
XLClientHandler.java
/**
* Copyright (C): 2012
* @author hankchen
* 2012-1-30 下午03:21:52
*/
/**
* 服务器特征:
* 1、使用专用的编码解码器,解决数据分段的问题
* 2、使用POJO替代ChannelBuffer传输
*/
public class XLClientHandler extends SimpleChannelHandler {
private static final Logger logger=LoggerFactory.getLogger(XLClientHandler.class);
private final AtomicInteger count=new AtomicInteger(0); //计数器
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
processMethod1(ctx, e); //处理方式一
}
/**
* @param ctx
* @param e
* @throws Exception
*/
public void processMethod1(ChannelHandlerContext ctx, MessageEvent e) throws Exception{
logger.info("processMethod1……,count="+count.addAndGet(1));
XLResponse serverTime=(XLResponse)e.getMessage();
logger.info("messageReceived,content:"+serverTime.toString());
Thread.sleep(1000);
if (count.get()<10) {
//从新发送请求获取最新的服务器时间
ctx.getChannel().write(ChannelBuffers.wrappedBuffer("again\r\n".getBytes()));
}else{
//从新发送请求关闭服务器
ctx.getChannel().write(ChannelBuffers.wrappedBuffer("shutdown\r\n".getBytes()));
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
logger.info("exceptionCaught");
e.getCause().printStackTrace();
ctx.getChannel().close();
}
@Override
public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
logger.info("channelClosed");
super.channelClosed(ctx, e);
}
}