初步看了一下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框架初步学习相关推荐

  1. netty框架的学习

    netty框架的学习 1.netty环境的搭建 2.netty的特点 2.1什么是netty 2.2为什么要使用netty 3.netty框架的搭建 3.1创建一个maven项目 3.2导入依赖 3. ...

  2. 基于Netty框架的多人聊天室

    因为产品升级,需要将原生的Java Socket通信改为Netty框架所以学习了Netty,这是一个多人聊天室,就算是第一个小作品吧. 使用的时候先开启服务端,之后开启任意多个客户端,即可实现多人聊天 ...

  3. netty框架学习及springboot整合集成

    netty框架学习及springboot整合集成 1. Netty基本概念 2. Netty框架 2.1 Netty框架结构 2.1 Netty NIO 2.2 Reactor线程模型 3. Spri ...

  4. Netty框架整体架构及源码知识点

    Netty概述 Netty是一个高性能.异步事件驱动的NIO框架,它提供了对TCP.UDP和文件传输的支持.作为当前最流行的NIO框架,Netty在互联网领域.大数据分布式计算领域.游戏行业.通信行业 ...

  5. HTML5与Phonegap框架初步

    [ back ]  微课名称:     HTML5与Phonegap框架初步  立即学习此微课: 方法-A:下载视频来播放 方法-B:ADT学院为您播放(免费播放中) 微课目标: 本微课从WebVie ...

  6. Netty框架中的@Skip使用说明

    最近在学习Netty框架,对着教程上写了个简单的netty应用,可是死活调试不成功,对着程序跟教程上看了几遍也找不到原因,后来又重新写了一遍,服务端程序终于调试成功,原因出在了那个@Skip注释上了, ...

  7. 关于Unity3D的初步学习研究周记

    关于Unity3D的初步学习研究周记(1) 学习总结: 本次学习Unity3D其实算是对基本的复习,因为在以前自己玩过一段时间,我也有一点C#的基础,所以我是直接开始做的一些小测试,因为事件原因,两个 ...

  8. Ogre个人初步学习总结

    Ogre个人初步学习总结 目录 1.开发环境 2.环境配置 2.1Ogre环境配置 2.2ParticleUniverse 环境配置 3.OGRE的学习札记 3.1Ogre的学前准备 3.1.1Ogr ...

  9. LinQ的初步学习与总结

    嘿嘿,说起来ORM和LinQ,就感觉离我好遥远的,在学校是没有学习的,所以总感觉学习了LinQ就是大神,现在嘛,终于也体会一点,感觉LinQ只是初步学习,没有太难,当然以后使用在项目中就没有这样的简单 ...

最新文章

  1. java8日期加本地,日期时间API(JDK8新增)
  2. 1054. 求平均值 (20)
  3. Python常见问题(1):来历与简介General Python FAQ
  4. javascript:jquery.history.js使用方法
  5. 为什么计算机有信息记忆功能,为什么计算机有记忆能力
  6. junit与spring-data-redis 版本对应成功的
  7. 流处理开源框架Flink原理简介和使用
  8. maven项目部署到Repository(Nexus)
  9. 怎么让某段css代码只在Chrome 火狐 edge 浏览器生效
  10. 15-struct(构造函数,重载)
  11. 06-20210308华为海思Hi3516DV300鸿蒙系统的uboot编译
  12. (附源码)ssm网上零食销售系统 毕业设计 180826
  13. usb禁止重定向_USB虚拟化与重定向(一)
  14. 测试一下你真的理解数据库左连接了吗?
  15. 在你学习计算机的路上,哪些书籍对你的帮助最大?
  16. 安装企业级的dokuwiki文档系统
  17. amap高德地图应用(el-amap-marker坐标点;el-amap-info-window信息窗体;el-amap-polyline折线、折线颜色,宽度、实虚线等)
  18. Unicable技术在卫星接收方案设计中的应用
  19. 企业开源办公虚拟专用网工具
  20. 局域网内通过ip获取主机名

热门文章

  1. cocos2dx系列--颜色混合BlendFunc
  2. vm 虚拟机设置共享文件夹
  3. Spark 的宽依赖和窄依赖
  4. pptx文件怎么打开
  5. private static final long serialVersionUID=1L 是什么意思
  6. 关于7.0去掉Chrome后,平台默认浏览器报错webview not installed的问题
  7. sol文件解析AS2.0——小游戏上古神器2存档通过Python解析
  8. GD32汽车诊断KWP 协议/ ISO-14230测试
  9. bzoj1758+WC2010
  10. Java图形编程实验总结_JAVA实验报告简单绘图程序