Netty入门 && 第一个Netty程序
1 为什么要用 Netty
(1) 虽然 JAVA NIO 框架提供了 多路复用 IO 的支持,但是并没有提供上层“信息格式”的良好封装。例如前两者并没有提供针对 Protocol Buffer、JSON 这些信息格式的封装,但是Netty 框架提供了这些数据格式封装(基于责任链模式的编码和解码功能)。
(2) NIO 的类库和 API 相当复杂,使用它来开发,需要非常熟练地掌握 Selector、ByteBuffer、ServerSocketChannel、SocketChannel 等,需要很多额外的编程技能来辅助使用 NIO,例如,因为 NIO 涉及了 Reactor 线程模型,所以必须必须对多线程和网络编程非常熟悉才能写出高质量的 NIO 程序。
(3) 要编写一个可靠的、易维护的、高性能的 NIO 服务器应用。除了框架本身要兼容实现各类操作系统的实现外。更重要的是它应该还要处理很多上层特有服务,例如:客户端的权限、还有上面提到的信息格式封装、简单的数据读取,断连重连,半包读写,心跳等等,
这些 Netty 框架都提供了响应的支持。
2 Netty简介
2.1 EventLoop(Group) 、Channel
Channel 是 Java NIO 的一个基本构造。它代表一个到实体(如一个硬件设备、一个文件、一个网络套接字或者一个能够执行一
个或者多个不同的 I/O 操作的程序组件)的开放连接,如读操作和写操作。目前,可以把 Channel 看作是传入(入站)或者传出(出站)数据的载体。因此,它可以被打开或者被关闭,连接或者断开连接。EventLoop 暂时可以看成一个线程、EventLoopGroup 自然就可以看成线程组。
2.2 事件和 ChannelHandler、ChannelPipeline
Netty 使用不同的事件来通知我们状态的改变或者是操作的状态。这使得我们能够基于已经发生的事件来触发适当的动作。Netty 事件是按照它们与入站或出站数据流的相关性进行分类的。可能由入站数据或者相关的状态更改而触发的事件包括:连接已被激活或者连接失活;数据读取;用户事件;错误事件。
出站事件是未来将会触发的某个动作的操作结果,这些动作包括:打开或者关闭到远程节点的连接;将数据写到或者冲刷到套接字。每个事件都可以被分发给 ChannelHandler 类中的某个用户实现的方法。Netty 提供了大量预定义的可以开箱即用的 ChannelHandler 实现,包括用于各种协议(如 HTTP 和 SSL/TLS)的 ChannelHandler。
3 第一个 Netty 程序
3.1 EchoServerHandler
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf byteBuf = (ByteBuf) msg;
System.out.println("Server accept: " + byteBuf.toString(CharsetUtil.UTF_8));
ctx.writeAndFlush(byteBuf);
ctx.close();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
3.2 EchoServer
public class EchoServer {
public static void main(String[] args) throws InterruptedException {
//服务器启动
ServerBootstrap bootstrap = new ServerBootstrap();
//线程组
EventLoopGroup group = new NioEventLoopGroup();
try {
//配置启动器
bootstrap.group(group)
//channel
.channel(NioServerSocketChannel.class)
//启动端口
.localAddress(new InetSocketAddress(8888))
//handler
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new EchoServerHandler());
}
});
//绑定服务器,sync阻塞完成
ChannelFuture channelFuture = bootstrap.bind().sync();
System.out.println("EchoServer started");
//阻塞线程知道服务器channel close
channelFuture.channel().closeFuture().sync();
} finally {
//退出,释放线程池
group.shutdownGracefully();
}
}
}
3.3 EchoClientHandler
public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf) throws Exception {
System.out.println("client accept " + byteBuf.toString(CharsetUtil.UTF_8));
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.writeAndFlush(Unpooled.copiedBuffer("Hello Netty", CharsetUtil.UTF_8));
}
}
3.4 EchoClient
public class EchoClient {
public static void main(String[] args) {
//客户端启动
Bootstrap bootstrap = new Bootstrap();
//线程组
EventLoopGroup group = new NioEventLoopGroup();
try {
bootstrap.group(group)
.channel(NioSocketChannel.class)
.remoteAddress(new InetSocketAddress("127.0.0.1", 8888))
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new EchoClientHandler());
}
});
//等待链接完成
ChannelFuture channelFuture = bootstrap.connect().sync();
//阻塞当前线程,直到channel关闭
channelFuture.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//退出,释放线程池
group.shutdownGracefully();
}
}
}
3.5 测试结果
本文地址:https://blog.csdn.net/qq_34125999/article/details/110901170
上一篇: 正则表达式\w元字符使用介绍