Netty框架初步学习
初步看了一下Netty的代码构成,发现还是挺有意思的。
先看看如下几段代码:
服务端
package ServerNetty;import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.util.SelfSignedCertificate;
import io.netty.handler.timeout.IdleStateHandler;import java.util.concurrent.TimeUnit;/*** @author PinkFloyd*/
public class ServerNetty {static final int PORT = Integer.parseInt(System.getProperty("port", "9999"));public void runServer() {//Group:群组,Loop:循环,Event:事件//Netty内部都是通过线程在处理各种数据,EventLoopGroup就是用来管理调度他们的,注册Channel,管理他们的生命周期。//NioEventLoopGroup是一个处理I/O操作的多线程事件循环//bossGroup作为boss,接收传入连接//因为bossGroup仅接收客户端连接,不做复杂的逻辑处理,为了尽可能减少资源的占用,取值越小越好// 用来接收进来的连接EventLoopGroup bossGroup = new NioEventLoopGroup(1);// 用来处理已经被接收的连接,一旦bossGroup接收到连接,就会把连接信息注册到workerGroup上EventLoopGroup workerGroup = new NioEventLoopGroup();final ServerNettyHandler serverHandler = new ServerNettyHandler();try{// 引导类,用于配置参数ServerBootstrap serverBootstrap = new ServerBootstrap();// 设置引导类的相关参数serverBootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)// 设置线程队列等待连接的个数.option(ChannelOption.SO_BACKLOG, 128 )//保持连接.childOption(ChannelOption.SO_KEEPALIVE,true)// 设置这样做好的好处就是禁用nagle算法// Nagle算法试图减少TCP包的数量和结构性开销, 将多个较小的包组合成较大的包进行发送.// 但这不是重点, 关键是这个算法受TCP延迟确认影响, 会导致相继两次向连接发送请求包,// 读数据时会有一个最多达500毫秒的延时..childOption(ChannelOption.TCP_NODELAY, true)//给我们的 WorkerGroup 的 EventLoop 对应的管道设置处理器.childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel socketChannel) {socketChannel.pipeline().addLast(new IdleStateHandler(3,5,7, TimeUnit.MINUTES));socketChannel.pipeline().addLast(serverHandler);}});System.out.println("server 开启--------------");// 通过绑定一个端口并实现同步,生成一个 ChannelFuture 对象// 启动并绑定 channelChannelFuture future = serverBootstrap.bind(PORT).sync();// Wait until the server socket is closed,// In this example, this does not happen,// but you can do that to gracefully shut down your server.// sync()会同步等待连接操作结果,用户线程将在此wait(),直到连接操作完成之后,线程被notify(),用户代码继续执行// closeFuture()当Channel关闭时返回一个ChannelFuture,用于链路检测future.channel().closeFuture().sync();}catch (Exception e){e.printStackTrace();}finally {bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}public static void main(String[] args) {new ServerNetty().runServer();}
}
package ServerNetty;
import Common.Message;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.ReferenceCountUtil;public class ServerNettyHandler extends ChannelInboundHandlerAdapter {// 读取数据@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) {try{ByteBuf bb = (ByteBuf) msg;int len = bb.readableBytes();byte[] bytes = new byte[len];bb.readBytes(bytes);String value = new String(bytes);//打印客户端传递过来的值System.out.println("server 接收到客户端的请求: " + value);//返回给客户端一个字符串来自服务器的响应ByteBuf result = Unpooled.copiedBuffer("来自服务器的响应".getBytes());ctx.writeAndFlush(result);System.out.println("已发送服务器响应");}finally {ReferenceCountUtil.release(msg);}}// 数据读取完毕的处理@Overridepublic void channelReadComplete(ChannelHandlerContext ctx) throws Exception {System.err.println("服务端读取数据完毕");}// 出现异常的处理@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {cause.printStackTrace();System.err.println("server 读取数据出现异常");ctx.close();}
}
客户端
package ClientNetty;import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.Unpooled;
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.NioSocketChannel;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;import Common.Message;public class ClientNetty {// 要请求的服务器的ip地址private final String ip;// 服务器的端口private int port;public ClientNetty(String ip, int port){this.ip = ip;this.port = port;}// 请求端主题private void action() throws InterruptedException {EventLoopGroup bossGroup = new NioEventLoopGroup();try{Bootstrap bs = new Bootstrap();bs.group(bossGroup).channel(NioSocketChannel.class).option(ChannelOption.SO_KEEPALIVE, true).handler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel socketChannel) throws Exception {// marshalling 序列化对象的解码// socketChannel.pipeline().addLast(MarshallingCodefactory.buildDecoder());// marshalling 序列化对象的编码// socketChannel.pipeline().addLast(MarshallingCodefactory.buildEncoder());// 处理来自服务端的响应信息socketChannel.pipeline().addLast(new ClientNettyHandler());}});// 客户端开启ChannelFuture cf = bs.connect(ip, port).sync();Message msg = new Message();String reqStr = "我是客户端请求1saioduiasoudioasuiasouaisoaiouasioasiduaio";msg.setContent(reqStr);// 发送客户端的请求cf.channel().writeAndFlush(Unpooled.copiedBuffer(reqStr, Charset.forName("utf-8")));// 等待直到连接中断cf.channel().closeFuture().sync();}finally {bossGroup.shutdownGracefully();}}public static void main(String[] args) throws UnsupportedEncodingException, InterruptedException {new ClientNetty("localhost",9999 ).action();}}
package ClientNetty;import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.ReferenceCountUtil;import java.nio.charset.Charset;/*** @author PinkFloyd*/
public class ClientNettyHandler extends ChannelInboundHandlerAdapter {@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {super.channelRead(ctx,msg);try {ByteBuf bb = (ByteBuf)msg;byte[] respByte = new byte[bb.readableBytes()];bb.readBytes(respByte);String respStr = new String(respByte, Charset.forName("utf-8"));System.out.println("client--收到响应:" + respStr);// 直接转成对象
// handlerObject(ctx, msg);} finally{System.out.println("Done");// 必须释放msg数据->似乎ChannelInboundHandlerAdapter会自动释放?// SimpleChannelInboundHandler作为Handler处理事务,// 使用AbstractChannelInboundHandler是不会主动释放内容的,这个时候需要你自己手动释放一次。
// ReferenceCountUtil.release(msg);}}// 数据读取完毕的处理@Overridepublic void channelReadComplete(ChannelHandlerContext ctx) throws Exception {System.err.println("客户端读取数据完毕");}// 出现异常的处理@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {cause.printStackTrace();System.err.println("client 读取数据出现异常");ctx.close();}}
启动服务端和客户端之后,服务端消息接收正常,但是客户端却出现了如下的报错:
io.netty.util.IllegalReferenceCountException: refCnt: 0at io.netty.buffer.AbstractByteBuf.ensureAccessible(AbstractByteBuf.java:1464)at io.netty.buffer.AbstractByteBuf.checkReadableBytes0(AbstractByteBuf.java:1448)at io.netty.buffer.AbstractByteBuf.checkReadableBytes(AbstractByteBuf.java:1434)at io.netty.buffer.AbstractByteBuf.readBytes(AbstractByteBuf.java:903)at io.netty.buffer.AbstractByteBuf.readBytes(AbstractByteBuf.java:911)at ClientNetty.ClientNettyHandler.channelRead(ClientNettyHandler.java:20)at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352)at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1421)at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:930)at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163)at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:697)at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:632)at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:549)at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:511)at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:918)at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)at java.base/java.lang.Thread.run(Thread.java:832)
分析了一下应该是bb为空,向上分析一下,bb是强制转化成ByteBuf的msg。
那么再针对msg分析,发现我们在重写channelRead的时候调用了super.channelRead(ctx,msg)
super.channelRead(ctx,msg);
向上追踪
ctx.fireChannelRead(msg);
fireChannelRead(msg)
(17条消息) netty中的发送有序与channel使用_silver9886的专栏-CSDN博客_firechannelread
在这篇文章中,我了解了:
这个代码在ctx.fireChannelRead(msg);的时候,最终会调用到tail的inboundhandle。tail inboundhandle是netty默认的最后一个处理msg的handle。将msg进行realse。那么当 ctx.flush();的时候,msg已经被释放掉,再读取msg,就会报错。
所以如果直接写 ctx.write(msg);复用msg的时候,坚决不能释放msg的引用。那么msg的引用什么时间会被释放呢?
当flush调用成功后,真正写入channel以后,DefaultChannelPipeline$HeadContext,最终会调用ChannelOutboundBuffer的
remove方法,将已经写入的内容从待写的链表中删除。
————————————————
版权声明:本文为CSDN博主「silver9886」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/silver9886/article/details/81475512
答案一目了然
另外我觉得Netty的数组传输方式也值得一提:
Netty源码之ByteBuf(一) - 简书 (jianshu.com) Mark一下
Netty框架初步学习相关推荐
- netty框架的学习
netty框架的学习 1.netty环境的搭建 2.netty的特点 2.1什么是netty 2.2为什么要使用netty 3.netty框架的搭建 3.1创建一个maven项目 3.2导入依赖 3. ...
- 基于Netty框架的多人聊天室
因为产品升级,需要将原生的Java Socket通信改为Netty框架所以学习了Netty,这是一个多人聊天室,就算是第一个小作品吧. 使用的时候先开启服务端,之后开启任意多个客户端,即可实现多人聊天 ...
- netty框架学习及springboot整合集成
netty框架学习及springboot整合集成 1. Netty基本概念 2. Netty框架 2.1 Netty框架结构 2.1 Netty NIO 2.2 Reactor线程模型 3. Spri ...
- Netty框架整体架构及源码知识点
Netty概述 Netty是一个高性能.异步事件驱动的NIO框架,它提供了对TCP.UDP和文件传输的支持.作为当前最流行的NIO框架,Netty在互联网领域.大数据分布式计算领域.游戏行业.通信行业 ...
- HTML5与Phonegap框架初步
[ back ] 微课名称: HTML5与Phonegap框架初步 立即学习此微课: 方法-A:下载视频来播放 方法-B:ADT学院为您播放(免费播放中) 微课目标: 本微课从WebVie ...
- Netty框架中的@Skip使用说明
最近在学习Netty框架,对着教程上写了个简单的netty应用,可是死活调试不成功,对着程序跟教程上看了几遍也找不到原因,后来又重新写了一遍,服务端程序终于调试成功,原因出在了那个@Skip注释上了, ...
- 关于Unity3D的初步学习研究周记
关于Unity3D的初步学习研究周记(1) 学习总结: 本次学习Unity3D其实算是对基本的复习,因为在以前自己玩过一段时间,我也有一点C#的基础,所以我是直接开始做的一些小测试,因为事件原因,两个 ...
- Ogre个人初步学习总结
Ogre个人初步学习总结 目录 1.开发环境 2.环境配置 2.1Ogre环境配置 2.2ParticleUniverse 环境配置 3.OGRE的学习札记 3.1Ogre的学前准备 3.1.1Ogr ...
- LinQ的初步学习与总结
嘿嘿,说起来ORM和LinQ,就感觉离我好遥远的,在学校是没有学习的,所以总感觉学习了LinQ就是大神,现在嘛,终于也体会一点,感觉LinQ只是初步学习,没有太难,当然以后使用在项目中就没有这样的简单 ...
最新文章
- java8日期加本地,日期时间API(JDK8新增)
- 1054. 求平均值 (20)
- Python常见问题(1):来历与简介General Python FAQ
- javascript:jquery.history.js使用方法
- 为什么计算机有信息记忆功能,为什么计算机有记忆能力
- junit与spring-data-redis 版本对应成功的
- 流处理开源框架Flink原理简介和使用
- maven项目部署到Repository(Nexus)
- 怎么让某段css代码只在Chrome 火狐 edge 浏览器生效
- 15-struct(构造函数,重载)
- 06-20210308华为海思Hi3516DV300鸿蒙系统的uboot编译
- (附源码)ssm网上零食销售系统 毕业设计 180826
- usb禁止重定向_USB虚拟化与重定向(一)
- 测试一下你真的理解数据库左连接了吗?
- 在你学习计算机的路上,哪些书籍对你的帮助最大?
- 安装企业级的dokuwiki文档系统
- amap高德地图应用(el-amap-marker坐标点;el-amap-info-window信息窗体;el-amap-polyline折线、折线颜色,宽度、实虚线等)
- Unicable技术在卫星接收方案设计中的应用
- 企业开源办公虚拟专用网工具
- 局域网内通过ip获取主机名
热门文章
- cocos2dx系列--颜色混合BlendFunc
- vm 虚拟机设置共享文件夹
- Spark 的宽依赖和窄依赖
- pptx文件怎么打开
- private static final long serialVersionUID=1L 是什么意思
- 关于7.0去掉Chrome后,平台默认浏览器报错webview not installed的问题
- sol文件解析AS2.0——小游戏上古神器2存档通过Python解析
- GD32汽车诊断KWP 协议/ ISO-14230测试
- bzoj1758+WC2010
- Java图形编程实验总结_JAVA实验报告简单绘图程序