下面用于分析源码的例子摘自《Netty权威指南 第二版》
本源码版本为5.0.0.Alpha1
server
package com.netty.server;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
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 io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
public class TimeServer {
public void bind(int port) {
//NIO线程组,Reactor线程组,一个用于接受客户端的请求,另一个用于进行SocketChannel的操作
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
//用于NIO服务端启动的辅助类
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
.childHandler(new ChildChannelHandler());
//绑定端口,同步等待成功,一直等待到绑定端口成功,返回一个ChannelFuture,类似JDK中的java.util.concurrent.Future
//用于异步的通知回调
ChannelFuture f = b.bind(port).sync();
//进行阻塞,等待服务器链路关闭,就退出
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//优雅退出,释放线程资源
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
private class ChildChannelHandler extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel arg0) throws Exception {
arg0.pipeline().addLast(new LineBasedFrameDecoder(1024));//加入换行符解码器
arg0.pipeline().addLast(new StringDecoder());//加入字符串解码器,将会把msg直接变成String类型的信息
arg0.pipeline().addLast(new TimeServerHandler());
}
}
public static void main(String[] args) {
int port = 8080;
if(args != null && args.length > 0) {
try {
port = Integer.valueOf(args[0]);
} catch (NumberFormatException e) {
}
}
new TimeServer().bind(port);
}
}
package com.netty.server;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
public class TimeServerHandler extends ChannelHandlerAdapter {
private int counter;
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//因为在channel中添加了StringDecoder解码器,所以收取到值是String类型的
String body = (String) msg;
System.out.println("The time server receive order : " + body + " ; the counter is : " + ++counter);
String currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body) ? new java.util.Date(System.currentTimeMillis()).toString() : "BAD ORDER";
currentTime += System.lineSeparator();
ByteBuf resp = Unpooled.copiedBuffer(currentTime.getBytes());
//先写到缓存中,需要flush
ctx.write(resp);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
//刷新
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
}
client
package com.netty.client;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
public class TimeClient {
public void connect(int port, String host) throws Exception {
// 配合客户端NIO线程组
EventLoopGroup group = new NioEventLoopGroup();
try {
//客户端启动类
Bootstrap b = new Bootstrap();
b.group(group).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitializer<io.netty.channel.socket.SocketChannel>() {
@Override
protected void initChannel(io.netty.channel.socket.SocketChannel ch) throws Exception {
ch.pipeline().addLast(new LineBasedFrameDecoder(1024));//加入换行符解码器
ch.pipeline().addLast(new StringDecoder());//加入字符串解码器
ch.pipeline().addLast(new TimeClientHandler());
}
});
//发起异步连接操作sync()表示在链接成功之前一直等待
ChannelFuture f = b.connect(host, port).sync();
//等待客户端链路关闭,sync()在关闭之前一直阻塞
f.channel().closeFuture().sync();
} catch (Exception e) {
} finally {
//优雅的关闭,释放资源
group.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
int port = 8080;
if(args != null && args.length > 0) {
try {
port = Integer.valueOf(args[0]);
} catch(NumberFormatException e) {
//采用默认值
}
}
new TimeClient().connect(port, "localhost");
}
}
package com.netty.client;
import java.util.logging.Logger;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
public class TimeClientHandler extends ChannelHandlerAdapter {
private static final Logger logger = Logger.getLogger(TimeClientHandler.class.getName());
private byte[] req = null;
private int counter;
public TimeClientHandler() {
req = ("QUERY TIME ORDER" + System.lineSeparator()).getBytes();
}
//通道准备就绪,就直接发送信息到服务器
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ByteBuf message = null;
//向服务端发送一百条消息,由于存在tcp的粘包,拆包的问题,服务段可能会显示收取到的信息没有一百条
for(int i = 0; i < 100; i++) {
message = Unpooled.buffer(req.length);
message.writeBytes(req);
ctx.writeAndFlush(message);
}
}
/**
* 当服务器发送信息回来时会调用这个方法
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//因为在channel中添加了StringDecoder解码器,所以收取到值是String类型的
String body = (String) msg;
System.out.println("Now is : " + body + " ; the counter is : " + ++counter);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
//释放资源
logger.warning("Unexpected exception from dodwnstream : " + cause.getMessage());
ctx.close();
}
}