服务器端代码
public class TimeServer {
public void bind(int port) throws Exception{
//创建两NioEventLoopGroup实例。
//NioEventLoopGroup是个线程组。
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try{
//创建ServerBootstrap对象,它是Netty用于启动NIO服务端的辅助启动类,目的是降低服务端的开发复杂度。
ServerBootstrap b = new ServerBootstrap();
//调用ServerBootstrap的group方法,将两个NIO线程组当做入参传递到ServerBootstrap中。
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
.childHandler(new ChannelInitializer() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new TimeServerHandler());
}
});
ChannelFuture f = b.bind(port).sync();
//等待服务锻炼路关闭之后main函数才退出。
f.channel().closeFuture().sync();
}finally {
//优雅退出,释放线程池资源。
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception{
int port = 8088;
new TimeServer().bind(port);
}
}
Netty服务器端Handler端代码
public class TimeServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//类型转换:将msg转成ByteBuf对象
ByteBuf buf = (ByteBuf) msg;
byte[] req = new byte[buf.readableBytes()];
buf.readBytes(req);
String body = new String(req , "UTF-8");
System.out.println("The time server receive order:" + body);
String currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body) ? new java.util.Date(
System.currentTimeMillis()).toString() : "BAD ORDER" ;
ByteBuf resp = Unpooled.copiedBuffer(currentTime.getBytes());
ctx.write(resp);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
// TODO Auto-generated method stub
super.channelReadComplete(ctx);
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
// TODO Auto-generated method stub
super.exceptionCaught(ctx, cause);
ctx.close();
}
}
先对服务器端的代码进行一下解析:
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
创建两个NioEventLoopGroup实例,NioEventLoopGroup是一个线程组,专门用于网络事件的处理。
这里创建两个的原因是一个用于服务器端接受客户端的连接,另一个用于进行SocketChannel的网络读写。
ServerBootstrap b = new ServerBootstrap();
创建ServerBootstrap用于启动NIO服务端的辅助启动类,目的是降低服务端的开发复杂度。
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
.childHandler(new ChannelInitializer() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new TimeServerHandler());
}
});
调用ServerBootstrap的group方法将两个NIO线程组传递到ServerBootstrap中,设置创建的channel为NioSeverSocketChannel,功能是对应JDK NIO类库中的ServerSocketChannel类。然后配置NioServerSocektChannel的TCP参数。最后绑定处理类,主要用于处理网络I/O事件。
f.channel().closeFuture().sync();
阻塞方法,等待服务端链路关闭之后main函数才退出。
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
优雅退出,释放相关资源。
ByteBuf buf = (ByteBuf) msg;
byte[] req = new byte[buf.readableBytes()];
buf.readBytes(req);
String body = new String(req , "UTF-8");
将msg转换成netty的ByteBuf对象。通过ByteBuf的readableBytes方法可以获取缓冲区可读字节数,根据可读字节数创建byte数组,通过ByteBuf的readBytes方法将缓冲区中的字节数组复制到新建的byte数组中。
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
// TODO Auto-generated method stub
super.exceptionCaught(ctx, cause);
ctx.close();
}
当发生异常时,关闭ChannelHandlerContext,释放和channelHandlerContext相关联的句柄资源。
Netty客户端TimeClient
public class TimeClient {
public void connent(int port , String host) throws Exception {
EventLoopGroup group = new NioEventLoopGroup();
try{
Bootstrap b = new Bootstrap();
b.group(group).channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitializer() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new TimeClientHandler());
}
});
ChannelFuture f = b.connect(host , port).sync();
f.channel().closeFuture().sync();
}finally {
group.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception{
int port = 8088;
new TimeClient().connent(port, "127.0.0.1");
}
}
Netty客户端TimeClientHandler 代码
public class TimeClientHandler extends ChannelInboundHandlerAdapter {
private static final Logger logger = Logger.getLogger(TimeClientHandler.class.getName());
private byte[] req ;
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
this.req = "QUERY TIME ORDER".getBytes();
ByteBuf message = Unpooled.buffer(req.length);
message.writeBytes(req);
ctx.writeAndFlush(message);
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf = (ByteBuf) msg;
byte[] req = new byte[buf.readableBytes()];
buf.readBytes(req);
String body = new String(req , "UTF-8");
System.out.println("Now is : "+ body);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
logger.warning("Unexpected exception from downstream : "+cause.getMessage());
ctx.close();
}
}
重点在channelActive, channelRead 和 execptionCaught 三个方法上。当客户端和服务器端TCP链路建立成功之后,Netty的NIO线程会调用channelActive方法,发送查询指令给服务器端,调用ChannelHandlerContext的writeAndFlush方法将请求消息发送给客户端。 当服务端返回应答时,channelRead方法被调用。