Netty入门(NIO编程和Netty客户端服务端示例)
2022/2/3 17:12:53
本文主要是介绍Netty入门(NIO编程和Netty客户端服务端示例),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
1.NIO编程
1.1 什么是NIO编程
一种理解是New I/O ,原因是相较之前的I/O类库是新增的。更多的人喜欢称之为非阻塞I/O(Non-block I/O),由于非阻塞I/O更能体现NIO的特点,所以后续NIO都指的是非阻塞I/O
1.2NIO类库介绍
1.缓冲区Buffer
在面向流的I/O中,可以直接写入或者将数据直接读取到Stream对象中,在NIO库中,所有数据都是用缓冲区来处理的。在写入数据的时候,也是写入到缓冲区的。
缓冲区实质是一个数组,通常是一个字节数组,缓冲区提供了对数据的结构化访问以及维护读写limit等信息
2.通道channel
通道与流不同之处在于通道是双向的,流知识在一个方向上面移动(一个流必须是InputStream或者OutputStream的子类),而通道可以用于读、写或者二者同时进行。 因为CHannel是双全工的,所以它比流更好的映射底层操作系统的API。特别是在UNIX网络编程模型中,底层的操作系统都是全双工的,同时支持读写操作。 Channel可以分为两大类:用于网络读写的SelectableChannel和用于文件操作的FileChannel。ServerSocket和SocketChannel都是SelectableChannel的子类。
3.多路复用器Selector
多路复用器提供选择已经就绪的任务的能力。简单的说,Selector会不断的轮询地注册在上面的Channel,如果某个 Channel上面发生读或者写事件,这个Channel就会处于就绪状态,会被Selector轮询出来
4.NIO client和NIO server举例
public class NioClient { public static void client() throws IOException { // 获取通道 SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898)); FileChannel inChannel = FileChannel.open(Paths.get("C:\\Users\\renyun\\Desktop\\图片\\logo2.png"), StandardOpenOption.READ); // 分配指定大小的缓冲区 ByteBuffer buf = ByteBuffer.allocate(1024); // 读取本地文件,并发送到服务端 while (inChannel.read(buf) != -1) { // 切换到读数据模式 buf.flip(); // 将缓冲区的数据写入管道 sChannel.write(buf); // 清空缓冲区 buf.clear(); } //关闭通道 inChannel.close(); sChannel.close(); } public static void main(String[] args) { new Thread(() -> { try { server(); } catch (IOException e) { e.printStackTrace(); } }, "t1").start(); try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(() -> { try { client(); } catch (IOException e) { e.printStackTrace(); } }, "t2").start(); } }
public class NioServer { /** * 服务端 */ public static void server() throws IOException { // 获取通道 ServerSocketChannel ssChannel = ServerSocketChannel.open(); FileChannel fileChannel = FileChannel.open(Paths.get("C:\\Users\\renyun\\Desktop\\图片\\logo2.png"), StandardOpenOption.WRITE, StandardOpenOption.CREATE); // 绑定端口号 ssChannel.bind(new InetSocketAddress(9898)); // 获取客户端连接的通道 SocketChannel socketChannel = ssChannel.accept(); // 分配指定大小的缓冲区 ByteBuffer buf = ByteBuffer.allocate(1024); // 读取客户端的数据,并保存到本地 while(socketChannel.read(buf) != -1) { // 切换成读模式 buf.flip(); // 写入 fileChannel.write(buf); // 清空缓冲区 buf.clear(); } // 关闭通道 ssChannel.close(); socketChannel.close(); fileChannel.close(); } }
5为什么不选择原生NIO编程的原因
1.NIO的类库和API繁杂,使用麻烦
2.需要具备其他额外技能,例如熟悉多线程编程,NIO涉及到Reactor模式,必须对多线程和网络编程非常熟悉。
3.可高兴能力不起,工作量和难度都非常大
4.JDK NIO存在bug
2 Netty NIO入门指南
有Netty4和Netty两个版本,最好还是选择Netty4。第一步搭建工程,可以直接引用Maven的依赖包,也可以直接卸载jar包复制到lib目录里。
<!--netty依赖--> <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.20.Final</version> </dependency> </dependencies>
2.1Netty入门服务端开发
public class NettyServer { public static void main(String[] args) throws InterruptedException { EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup worker = new NioEventLoopGroup(); ServerBootstrap bootstrap = new ServerBootstrap(); try { bootstrap.group(bossGroup, worker) //设置两个线程组 .channel(NioServerSocketChannel.class) //作为服务器的通道实现 .option(ChannelOption.SO_BACKLOG,128) //设置线程队列等待连接的个数 .childOption(ChannelOption.SO_KEEPALIVE,true) //设置保持活动连接状态 .childHandler(new ChannelInitializer<SocketChannel>() { //创建一个通道初始化对象 //给pipline 设置处理器 @Override protected void initChannel(SocketChannel channel) throws Exception { channel.pipeline().addLast(new NettyServerHandler()); } }); // System.out.println("服务器已经准备好了"); //绑定一个端口并且同步处理,生成一个ChannelFuture对象 ChannelFuture cf = bootstrap.bind(6668).sync(); //对关闭通道进行监听 异步模型 channelFuture cf.channel().closeFuture().sync(); }finally { bossGroup.shutdownGracefully(); worker.shutdownGracefully(); } } }
介绍重点类
NioEventLoopGroup 专门用于网络事件的处理,实际上就是Reator线程组,创建两个的原因一个用于服务端接受客户端的连接,另一个用于SocketChannel的网络读写。
ServerBootstrap是Netty用于启动服务端的辅助启动类,目的降低服务端的开发复杂程度,通过调用group方法吧两个线程组当做入参传递到ServerBootstrap里面
NioServerSocketChannel对应的JDK NIO类库里面的ServerSocketChannel类
public class NettyServerHandler extends ChannelInboundHandlerAdapter { //读取数据实际(读取客户端消息) ChannelHandlerContext上下文对象 // Object msg 客户端发送的消息 默认是object形式的 @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { System.out.println("server ctx ="+ctx); ByteBuf buf = (ByteBuf) msg; System.out.println("客户端发送消息"+buf.toString(CharsetUtil.UTF_8)); System.out.println("客户端地址"+ctx.channel().remoteAddress()); // super.channelRead(ctx, msg); //将msg转成bytebuffer } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { ctx.writeAndFlush(Unpooled.copiedBuffer("hello 客户端",CharsetUtil.UTF_8)); } //处理异常,一般是关闭通道 @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { ctx.close(); // super.exceptionCaught(ctx, cause); } }
NettyServerHandler 继承自ChannelInboundHandlerAdapter ,对网络事件进行读写操作;
重点关注channelRead和exceptionCaught方法。
ByteBuf buf = (ByteBuf) msg;将msg转为Netty 的ByteBuf对象。ByteBuf的方法可以获取缓冲区的字节数
通过上述的代码可以看出,相比较于传统的JDK NIO原生类库的服务端,代码量大大减少,开发量减少了很多。
2.2Netty客户端开发
public class NettyClient { public static void main(String[] args) throws InterruptedException { //客户端需要仅一个事件循环组 EventLoopGroup eventExecutors = new NioEventLoopGroup(); try{ //创建客户端启动对象 Bootstrap bootstrap = new Bootstrap(); bootstrap.group(eventExecutors) .channel(NioSocketChannel.class) //设置客户端通道的处理 .handler(new ChannelInitializer<SocketChannel>() { //创建一个通道初始化对象 //给pipline 设置处理器 @Override protected void initChannel(SocketChannel channel) throws Exception { channel.pipeline().addLast(new NettyClientHandler()); } }); System.out.println("客户端已经好了"); ChannelFuture cf = bootstrap.connect("127.0.0.1", 6668).sync(); cf.channel().closeFuture().sync(); }finally { eventExecutors.shutdownGracefully(); } } }
public class NettyClientHandler extends ChannelInboundHandlerAdapter { //通道就绪就会触发该方法 @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { System.out.println("client" +ctx); ctx.writeAndFlush(Unpooled.copiedBuffer("HELLO SERVER", CharsetUtil.UTF_8)); } //当通道有读取事件时会触发 @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { ByteBuf buf = (ByteBuf) msg; System.out.println("服务器回复的消息"+buf.toString(CharsetUtil.UTF_8)); System.out.println("服务器端的地址"+ctx.channel().remoteAddress()); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } }
客户端的服务相较于服务端较为简单;
2.3运行和调试
需要指出的是,本例程没有考虑读半包的处理,对于功能演示或者测试没有问题,但是如果进行压力测试,就不能正常工作,就需要处理半包相关的知识点。利用一些方法来解决TCP粘包和拆包。
这篇关于Netty入门(NIO编程和Netty客户端服务端示例)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-10-05小米13T Pro系统合集:性能与摄影的极致融合,值得你升级的系统ROM
- 2024-10-01基于Python+Vue开发的医院门诊预约挂号系统
- 2024-10-01基于Python+Vue开发的旅游景区管理系统
- 2024-10-01RestfulAPI入门指南:打造简单易懂的API接口
- 2024-10-01初学者指南:了解和使用Server Action
- 2024-10-01Server Component入门指南:搭建与配置详解
- 2024-10-01React 中使用 useRequest 实现数据请求
- 2024-10-01使用 golang 将ETH账户的资产平均分散到其他账户
- 2024-10-01JWT用户校验课程:从入门到实践
- 2024-10-01Server Component课程入门指南