Handler在netty中,占据非常重要地位,与Servlet中的Filter很想,通过Handler可以完成通讯报文的解码和编码、拦截指定的报文、统一对日志错误进行处理、统一对请求进行计数、控制Handler执行与否。
Handler注册要注意先后顺序:
Handler按照输入输出区分,分为ChannelInboundHandler和ChannelOutboundHandler两类。ChannelInboundHandler对输入数据进行按照注册的先后顺序处理;而ChannelOutboundHandler对输出数据进行按照注册的先后顺序的逆序处理,如下图所示:
自定义Handler:
其中ServerHandler比ClientHandler简单,只需要负责接收消息、显示和返回应答就好了,代码如下所示:
Server端:
public class DiscardServerHandler extends SimpleChannelInboundHandler<String> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg)
throws Exception {
// 收到客户端的消息
String toConsole = ctx.channel().remoteAddress() + " said: " + msg;
System.out.println(toConsole);
// 分发消息给客户端
ctx.writeAndFlush(msg + " received!\r\n");
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
/**
* 本方法channelActive在channel被accept时候(即连接建立的时候被触发)
* @throws Exception
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
String toConsole = ctx.channel().remoteAddress() + " active!";
System.out.println(toConsole);
ctx.writeAndFlush("Welcome to " + InetAddress.getLocalHost().getHostName() + " service!\r\n");
super.channelActive(ctx);
}
}
上面的类继承了
Class SimpleChannelInboundHandler<T>
其中T是泛型表达形式,在这里最简单的系统上我们设置为String,当然也可以设置为其他自定义的类,这会在后续博客中详细说明
需要覆盖的最基本的方法:
1)channelRead0:当服务器接收到channel发送来的消息的处理方法
2)channelActive():当服务器accept客户端的连接请求后触发的方法
3)其他:包括一些异常处理等
Client端:
public class DiscardClientHandler extends SimpleChannelInboundHandler<String> {
// 用于保存消息
// private ByteBuf content;
private String content;
// 用于回调的handler
private ChannelHandlerContext ctx;
// 监听器
private final ChannelFutureListener trafficGenerator = new ChannelFutureListener() {
/*
* 监听函数,用于在异步返回channelfuture的时候启动
*/
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
System.out.println("Client is in listening!");
generateTraffic();
} else {
future.cause().printStackTrace();
future.channel().close();
}
}
};
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg)
throws Exception {
String toConsole = "Server say: " + msg;
System.out.println(toConsole);
// Initialize and send the initial message
generateTraffic();
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
String toConsole = "Client active!";
System.out.println(toConsole);
this.ctx = ctx;
super.channelActive(ctx);
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
String toConsole = "Client closed!";
System.out.println(toConsole);
super.channelInactive(ctx);
}
private void generateTraffic() throws IOException {
// Flush the outbound buffer to the socket
// Once flushed, generate the same amount of traffic again
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
content = br.readLine();
// ctx.writeAndFlush(content + "\r\n").addListener(trafficGenerator);
ctx.writeAndFlush(content + "\r\n");
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
// Close the connection when an exception is raised
cause.printStackTrace();
ctx.close();
}
}
需要覆盖的最基本的方法:
1)channelRead0:当客户端接收到Server发送来的消息的处理方法,这里需要注意到的是,如果
2)channelActive():当服务器accept客户端的连接请求后触发的方法
3)channelInActive():当服务器断开与客户端的连接以后触发的方法
3)其他:包括一些异常处理等
自定义方法:1)generateTraffic()
这个方法可以放在channelActive(若放在这里,需要添加Listener,因为Active方法只执行一次)或者channelRead0(若放在这里,必须不能添加Listener,不然Read方法将只能执行一次)
自定义类对象:
1)ChannelFutureListener
这个类用只有一个方法:当ChannelFuture的操作完成以后,需要做什么工作,是一个监听类,详细说明在后续的博客中