Netty学习记录
最近在看《Netty实战》这本书,今天读完,感觉还是很有收获,在此mark一下。
话不多说,上代码。
// EchoServerHandler.java 服务端handler
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;
@ChannelHandler.Sharable
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
public void channelRead(ChannelHandlerContext ctx, Object msg){
ByteBuf in = (ByteBuf) msg;
System.out.println("Server received: " + in.toString(CharsetUtil.UTF_8));
ctx.write(in);
}
public void channelReadComplete(ChannelHandlerContext ctx){
ctx.writeAndFlush(Unpooled.EMPTY_BUFFER)
.addListener(ChannelFutureListener.CLOSE);
}
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause){
cause.printStackTrace();
ctx.close();
}
}
这里服务器会收到来自客户端发来的消息,所以继承了ChannelInboundHandlerAdapter接口,并以自己的逻辑重写了channelRead()、channelReadComplete()、exceptionCaught()这3个方法。
// EchoServer.java 服务端启动程序
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import java.net.InetSocketAddress;
public class EchoServer {
private final int port;
public EchoServer(int port){
this.port = port;
}
public static void main(String[] args) throws Exception {
if(args.length != 1){
System.err.println("Usage: " + EchoServer.class.getSimpleName() + " <port>");
return;
}
int port = Integer.parseInt(args[0]);
new EchoServer(port).start();
}
public void start() throws Exception{
final EchoServerHandler serverHandler = new EchoServerHandler();
EventLoopGroup group = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(group)
.channel(NioServerSocketChannel.class)
.localAddress(new InetSocketAddress(port))
.childHandler(new ChannelInitializer<SocketChannel>(){
public void initChannel(SocketChannel ch) throws Exception{
ch.pipeline().addLast(serverHandler);
}
});
ChannelFuture f= b.bind().sync();
f.channel().closeFuture().sync();
}finally {
group.shutdownGracefully().sync();
}
}
}
服务端启动程序按照固定套路,Bootstrap 绑定 EventLoopGroup , 再绑定 channel, 设置地址及端口,绑定handler,此处handler绑定采用ChannelInitializer初始化的方式,在pipeline里动态添加刚写的ServerHandler
// EchoClientHandler.java 客户端handler
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.CharsetUtil;
@ChannelHandler.Sharable
public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
public void channelActive(ChannelHandlerContext ctx){
ctx.writeAndFlush(Unpooled.copiedBuffer("Netty rocks", CharsetUtil.UTF_8));
}
public void channelRead0(ChannelHandlerContext ctx, ByteBuf in){
System.out.println("Client received: " + in.toString(CharsetUtil.UTF_8));
}
public void exceptionCause(ChannelHandlerContext ctx, Throwable cause){
cause.printStackTrace();
ctx.close();
}
}
客户端的handler写法与服务端基本一致,需要什么样的逻辑处理,重写对应的方法即可。
// EchoClient.java 客户端启动程序
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import java.net.InetSocketAddress;
public class EchoClient {
private final String host;
private final int port;
public EchoClient(String host, int port){
this.host = host;
this.port = port;
}
public void start() throws Exception{
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.remoteAddress(new InetSocketAddress(host, port))
.handler(new ChannelInitializer<SocketChannel>() {
public void initChannel(SocketChannel ch) throws Exception{
ch.pipeline().addLast(new EchoClientHandler());
}
});
ChannelFuture f = b.connect().sync();
f.channel().closeFuture().sync();
}finally {
group.shutdownGracefully().sync();
}
}
public static void main(String[] args) throws Exception {
if(args.length != 2){
System.err.println("Usage: " + EchoClient.class.getSimpleName() + "<host> <port>");
return;
}
String host = args[0];
int port = Integer.parseInt(args[1]);
new EchoClient(host, port).start();
}
}
引导程序的流程也基本一致,差异大部分是EventLoopGroup、bootstrap等的选型
总结
bootstrap --> EventLoopGroup --> channel --> address --> handler --> connect --> futrue.channel.close --> group.shutdown