三Netty--Netty入门应用
三Netty--Netty入门应用
Netty的开发过程,需要注意对比和NIO开发流程的对应关系。
仍旧以TimeServer为例,简单介绍Netty开发流程。
3.2 Server端开发
3.2.1 TimeServer代码
public class TimeServer {
//server端bind接口,就是开启服务
public void bind(int port) throws InterruptedException {
/** EventLoopGroup是个线程组,包含一组NIO线程,用于网络事件处理,本质是reactor线程组
* bossgroup用来处理网络连接;workerGroup用来处理读写请求--
* */
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
//ServerBootstrap是用来启动NIO的辅助启动类,包含了一系列的配置
ServerBootstrap b = new ServerBootstrap();
//group,配置两个NIO线程组
b.group(bossGroup, workerGroup)
//设置创建的channel类型
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
/**childHandler用于绑定IO事件的处理类,用于处理网络IO事件
* 类似于NIO中的handler类
* 该ChildChannelHandler,需要实现ChannelInitializer类,用于初始化IO的handler处理类
* 其实现方法initChannel,初始化channel,并向ch.pipeiine上添加对IO的具体处理类
* 该处理类可以自己定制实现,netty也提供了日志记录、编解码等组件类*/
.childHandler((ChannelHandler) new ChildChannelHandler());
//绑定端口,并同步等待绑定成功
ChannelFuture f = b.bind(port).sync();
//等待服务端监听端口关闭
f.channel().closeFuture().sync();
}
//当需要释放资源时,由于必须要finally,因此使用try、finally的组合
finally {
//try finally优雅退出,释放线程池资源(group为线程池)
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
//处理client端的SocketChannel的IO请求
private class ChildChannelHandler extends ChannelInitializer {
@Override
protected void initChannel(Channel ch) throws Exception {
//添加自定义的网络事件IO处理类,完成逻辑实现
ch.pipeline().addLast(new TimeServerHandler());
}
}
public static void main(String[] args) throws InterruptedException {
int port = 8080;
new TimeServer().bind(port);
}
}
编程技巧:当需要释放资源时,由于必须要finally,因此使用try、finally的组合。
需要注意的两个类:
(1)EventLoopGroup:
EventLoopGroup是个线程组,包含一组NIO线程,用于网络事件处理,本质是reactor线程组
bossgroup用来处理网络连接;workerGroup用来处理读写请求
(2)private class ChildChannelHandler extends ChannelInitializer :channel初始化处理类
childHandler用于绑定IO事件的处理类,用于处理网络IO事件
* 类似于NIO中的handler类;
* 该ChildChannelHandler,需要实现ChannelInitializer类,用于初始化IO的handler处理类;
* 其实现方法initChannel,初始化channel,并向ch.pipeiine上添加对IO的具体处理类;
* 该处理类可以自己定制实现,netty也提供了日志记录、编解码等组件类*/
(3)ch.pipeline().addLast(new TimeServerHandler()):网络IO处理类——自定义类
自定义网络IO的逻辑业务实现,需要实现ChannelHandlerAdapter类,然后加入channel的管道pipeline中,实现对网络IO的流水线处理。该类使用了adapter的适配器模式,实现了骨架。

每个方法,都fire触发了channelHandler网络管道处理类的对应方法(因此,NIO中向selector的注册各种监听,和轮询就绪channel,就在这里实现)。
public class ChannelInboundHandlerAdapter extends ChannelHandlerAdapter implements ChannelInboundHandler {
/**——————————————————————————————————————handler实现链式调用的关键—————————————————————————————————————— */
//当channel读取read到数据时,会调用channelHandlerContext的fireXXX方法,执行channelPipeline链上下一个ChannelInboundHandler的执行,以实现链式调用。
//读取channel发送过来的ByteBuf
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ctx.fireChannelRead(msg);
}
//当读取完毕后,调用此方法---用来在前一个读取完毕后,进行ctx.flush()操作
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.fireChannelReadComplete();
}
//处理异常
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.fireExceptionCaught(cause);
}
//用在建立连接后,channelActive活动状态,进行操作(如client建立联系,发送请求消息内容)
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.fireChannelActive();
}
3.2.2 IO处理类(ChannelHandlerAdapter)
channelHandlerAdapter为接口适配器模式。该类是抽象类abstract class,两个子类实现该抽象类,然后创建channelHandler类,可以接口多态模式,创建对象,但是对应方法,可以是子类中的方法。例如,这里创建server端的channelhandler类,但是其既需要inbound方法,又需要outbound方法,因此,使用该adapter类,可以创建一个适配器抽象类。继承该类,就可以实现一个既有inbound又有outbound方法的handler。

public class TimeServerHandler extends ChannelHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//这里的msg是client通过channel传过来的ByteBuf形式数据,需要转换为string型
ByteBuf buf = (ByteBuf) msg;
byte[] req = new byte[buf.readableBytes()];
buf.readBytes(req);
String body = new String(req);
//………………IO处理
//省略server端对client传递的请求的处理
String resp = "返回处理结果";
ByteBuf respBB = Unpooled.copiedBuffer(resp.getBytes(StandardCharsets.UTF_8));
/** 注意:*/
ctx.write(respBB);
}
@Override
//读取channel发送msg完毕后操作
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
/** flush,将消息发送队列中的消息写入SocketChannel中,并发送给对方。
* 从性能角度考虑,为了不频繁唤醒Selector进行消息发送,netty的write方法,不是直接写入SocketChannel中,
* 调用write方法只把待发送消息放到发送缓冲数组中。贼读取完毕后,再通过flush(),将发送区中的消息全部写到SocketChannel*/
ctx.flush();
}
//对异常出现时,关闭
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
}
注意:
netty中write,writeAndFlush,flush的关系。
3.3 Client端开发
3.3.1 TimeClient客户端开发
public class TimeClient {
public void connect(int port, String host) {
//配置客户端NIO线程组
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group)
.option(ChannelOption.TCP_NODELAY,Boolean.TRUE)
//添加网络IO处理类(其实是个channel初始化类,负责添加具体的IO处理业务类)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
//添加具体业务处理类
ch.pipeline().addLast(new TimeClientHandler());
}
});
}finally {
group.shutdownGracefully();
}
}
public static void main(String[] args) {
new TimeClient().connect(80,"111");
}
}
注意:(回顾)
handler(new ChannelInitializer
当创建NioSocketChannel成功后,进行初始化时,将ChannelHandler设置到ChannelPipeline中(就是自定义IO业务处理类)。
3.3.2 IO处理类
//adapter适配器模式
public class TimeClientHandler extends ChannelHandlerAdapter {
//待发送请求
private final ByteBuf request;
// 构造函数,定义第一条请求,在初始化TimeClientHandler时,构造request请求
public TimeClientHandler() {
byte[] req = "第一条请求".getBytes(StandardCharsets.UTF_8);
request = Unpooled.buffer(req.length);
request.writeBytes(req);
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
//发送消息
ctx.writeAndFlush(request);
}
/** 该处就是client读取server返回的结果*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//首先将ByteBuf的msg转换为string
ByteBuf buf = (ByteBuf) msg;
byte[] req = new byte[buf.readableBytes()];
buf.readBytes(req);
String body = new String(req);
//处理结果
System.out.println(body);
}
@Override
//出现exception时,释放ChannelHandlerContext资源
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
}

浙公网安备 33010602011771号