Netty
快来分享你的内容吧~
- 关于netty 服务端 收 C语言客户端 连接 接收数据问题Bug 描述在 arm 平台 ,使用 netty 作为 服务端 监听 接收数据,使用的 FixedLengthFrameDecoder(4096) 定长编解码 数据包程序运行一段时间后,出现服务端接收不到数据问题,客户端是 c语言开发的 采用短连接 。业务来了,每次是建立连接,发送自定义业务数据请求,响应 ,然后c语言客户端主动关闭连接。复现时机Bug 是偶发。目前是研发阶段,我们是服务器整机交付...查看全文编程导航小智:这个问题可能有多种原因导致,以下是一些可能的解决方案:1. 确保你的netty版本与你的Java版本兼容。Netty 4.1.91需要至少Java 8才能正常工作。2. 检查你的netty配置,确保你正确地添加了"FixedLengthFrameDecoder"和你的自定义解码器到你的channel pipeline中。确保它们的顺序是正确的。3. 检查你的netty服务器是否正确地绑定到正确的地
如果想做一个完善的根据标签匹配如果想做一个完善的根据标签匹配聊天的系统,后期用户量大是不是一开始用netty做两端连接比较好...查看全文程序员鱼皮:要看你这里的 “完善” 具体是指什么?netty 可以用于做高性能的双工通信,更适用于实时匹配的场景(比如游戏组队匹配),匹配成功后更好通知双方。而如果是想提升匹配的准确度,还是要从算法层面去改良。比如给目前的余弦相似度算法引入更多的特征 tag。
2022-11-12·Java后端这是 Netty 系列文章的第二篇, Netty 是一款网络编程框架,在介绍 Netty 技术之前,先来聊聊网络编程都干啥? 后端开发不用网络编程? 我们平常的开发的 Java Web 程序,都运行在 Tomcat 等 web 容器当中。所以虽然 web 应用是网络应用,但网络编程对我们来说是透明的,不被感知的。 在 servlet 中有全局域对象:request、response,用于获取 Ht查看全文菜卷:公众号阅读地址:https://mp.weixin.qq.com/s?__biz=Mzg2NjM4NzY3Mw==&mid=2247483916&idx=1&sn=db564b4c47dce3f52958ac1358e8d039&chksm=ce4ad1baf93d58acfbea8dbb8b251144a439d3cc407329f9cf40380595ded709c1620523112b&to2411分享- 2022-11-06·Java后端一、Netty 简介 为什么写这篇文章? 这篇文章是对 Netty 的基础介绍,偏概念和理论。主要从概念上介绍什么是 Netty、应用领域 、特性、社区活跃度、发展趋势等。 内容大多来自网络,搜集后自己整理,对于之前没有接触过 Netty 的同学做一点基础介绍。不会在我提到 Netty 的时候问:“Netty 是大数据计算组件吗?” 介绍 Netty 是一个 Java NIO 的高性能网络编程应用查看全文菜卷:之前发过一次,重新排版了一下。公众号阅读体验好一点:https://mp.weixin.qq.com/s/PZN0qU6WLqb6CfPz9YJyjQ2011分享
关于netty 服务端 收 C语言客户端 连接 接收数据问题
### Bug 描述 在 arm 平台 ,使用 netty 作为 服务端 监听 接收数据,使用的 FixedLengthFrameDecoder(4096) 定长编解码 数据包 程序运行一段时间后,出现服务端接收不到数据问题, 客户端是 c语言开发的 采用短连接 。业务来了,每次是建立连接,发送自定义业务数据请求,响应 ,然后c语言客户端主动关闭连接。 ### 复现时机 Bug 是偶发。目前是研发阶段,我们是服务器整机交付,每天服务器是断正常断电,这个bug 可能几周出现一次 。重启jar 后又可以正常接收请求了 。 系统是 arm 平台。 比如:“Java 8、netty 4.1.91 hutool 工具 5.18.18” ### 期望结果 期望定位下 为什么没有触发 channelActive 方法,以及需要怎么配置才能间断收数据。 ### 错误信息 无 ### 自己的思路和解决方案 通过 tcpdump 抓包,tcp 三次握手成功 的 3个数据包,c语言客户端发送第一包数据4096个字节。在抓包的 pcap日志文件中都有,然后就是netty服务端没有响应了。 但是 netty 服务端 handler 实现的 SimpleChannelInboundHandler 里面的channelActive方法里面 日志没有打印,也就是 channel 没有激活, 自定义的实现的解码器 实现 ByteToMessageDecoder 中的 decode 方法开始的地方 日志也没有打印。 exceptionCaught() 方法也没有打印 异常日志 。 ### 相关资料 相关的 server 启动监听配置 ,自定义handler ,自定义 decoder https://codecopy.cn/post/qh8v78
通过Netty手写Web服务器
最近通过原生netty写了一个web服务器,实现了一些在SpringBoot中常用的功能(我觉得对初学者有很好的开拓效果,能够清楚的感知市面大部分web服务器的技术细节),还需要大家多给一些意见。。。 https://gitee.com/wlx0902/netty-server.git
如果想做一个完善的根据标签匹配
如果想做一个完善的根据标签匹配聊天的系统,后期用户量大是不是一开始用netty做两端连接比较好
【技术分享】网络编程工作原理
<html> <head></head> <body> <div class="content ql-editor"> <blockquote> <div class="blockquote-item"> 这是 Netty 系列文章的第二篇, Netty 是一款网络编程框架,在介绍 Netty 技术之前,先来聊聊网络编程都干啥? </div> </blockquote> <p><br></p> <h2><strong style="color: rgb(51, 51, 51);">后端开发不用网络编程?</strong></h2> <p><br></p> <p><br></p> <p>我们平常的开发的 Java Web 程序,都运行在 Tomcat 等 web 容器当中。所以虽然 web 应用是网络应用,但网络编程对我们来说是透明的,不被感知的。</p> <p>在 servlet 中有全局域对象:request、response,用于获取 Http 请求和响应的信息;spring 中提供了 HttpServletRequest、HttpServletResponse,借助 web 容器和框架,因此我们无需直接编写网络编程和协议处理的代码,通过使用框架提供的封装好的类库,也能很方便的开发自己的业务。</p> <p><br></p> <p><br></p> <h2><strong style="color: rgb(51, 51, 51);">网络编程怎么做?</strong></h2> <p><br></p> <p><br></p> <p>如果没有 web 容器,要怎样实现一个简单的 http GET 请求和并返回响应呢?</p> <p><strong>talk is cheap, show your code.</strong> 上代码:</p> <div class="ql-code-block-container"> <div class="ql-code-block"><span class="ql-token hljs-comment">/**</span> </div> <div class="ql-code-block"><span class="ql-token hljs-comment"> * Http 服务端</span> </div> <div class="ql-code-block"><span class="ql-token hljs-comment"> */</span> </div> <div class="ql-code-block"><span class="ql-token hljs-meta">@Slf4j</span> </div> <div class="ql-code-block"><span class="ql-token hljs-keyword">public</span> <span class="ql-token hljs-keyword">class</span> <span class="ql-token hljs-title">SocketHttpServer</span> { </div> <div class="ql-code-block"> </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">public</span> <span class="ql-token hljs-keyword">static</span> <span class="ql-token hljs-keyword">void</span> <span class="ql-token hljs-title">main(String[] args)</span> { </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">new</span> <span class="ql-token hljs-title">SocketHttpServer</span>().start(); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> </div> <div class="ql-code-block"> </div> <div class="ql-code-block"> <span class="ql-token hljs-comment">/**</span> </div> <div class="ql-code-block"><span class="ql-token hljs-comment"> * 启动服务器</span> </div> <div class="ql-code-block"><span class="ql-token hljs-comment"> */</span> </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">public</span> <span class="ql-token hljs-keyword">void</span> <span class="ql-token hljs-title">start()</span> { </div> <div class="ql-code-block"> <span class="ql-token hljs-type">int</span> <span class="ql-token hljs-variable">port</span> <span class="ql-token hljs-operator">=</span> <span class="ql-token hljs-number">8080</span>; </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">try</span> { </div> <div class="ql-code-block"> <span class="ql-token hljs-comment">// 0.绑定端口</span> </div> <div class="ql-code-block"> <span class="ql-token hljs-type">ServerSocket</span> <span class="ql-token hljs-variable">serverSocket</span> <span class="ql-token hljs-operator">=</span> <span class="ql-token hljs-keyword">new</span> <span class="ql-token hljs-title">ServerSocket</span>(port); </div> <div class="ql-code-block"> log.info(<span class="ql-token hljs-string">"服务端开始启动,端口 port: {}"</span>, port); </div> <div class="ql-code-block"> Socket socket; </div> <div class="ql-code-block"> <span class="ql-token hljs-comment">// 1.循环监听 and 2.建立连接</span> </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">while</span> (Objects.nonNull((socket = serverSocket.accept()))) { </div> <div class="ql-code-block"> log.info(<span class="ql-token hljs-string">"建立连接,客户端 IP:[{}], 端口 port:[{}]"</span>, socket.getInetAddress().getHostAddress(), socket.getPort()); </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">try</span> (<span class="ql-token hljs-type">BufferedInputStream</span> <span class="ql-token hljs-variable">inputStream</span> <span class="ql-token hljs-operator">=</span> <span class="ql-token hljs-keyword">new</span> <span class="ql-token hljs-title">BufferedInputStream</span>(socket.getInputStream()); </div> <div class="ql-code-block"> <span class="ql-token hljs-type">BufferedOutputStream</span> <span class="ql-token hljs-variable">outputStream</span> <span class="ql-token hljs-operator">=</span> <span class="ql-token hljs-keyword">new</span> <span class="ql-token hljs-title">BufferedOutputStream</span>(socket.getOutputStream())) { </div> <div class="ql-code-block"> </div> <div class="ql-code-block"> <span class="ql-token hljs-comment">// 3.input : 读取数据</span> </div> <div class="ql-code-block"> <span class="ql-token hljs-type">byte</span>[] buffer = <span class="ql-token hljs-keyword">new</span> <span class="ql-token hljs-title">byte</span>[<span class="ql-token hljs-number">1024</span> * <span class="ql-token hljs-number">256</span>]; </div> <div class="ql-code-block"> <span class="ql-token hljs-type">int</span> <span class="ql-token hljs-variable">length</span> <span class="ql-token hljs-operator">=</span> inputStream.read(buffer); </div> <div class="ql-code-block"> </div> <div class="ql-code-block"> <span class="ql-token hljs-comment">// 4.compute : 计算</span> </div> <div class="ql-code-block"> <span class="ql-token hljs-comment">// 4.1 编码</span> </div> <div class="ql-code-block"> <span class="ql-token hljs-type">HttpDecoder</span> <span class="ql-token hljs-variable">httpDecoder</span> <span class="ql-token hljs-operator">=</span> <span class="ql-token hljs-keyword">new</span> <span class="ql-token hljs-title">HttpDecoder</span>(); </div> <div class="ql-code-block"> Map<String, String> request = httpDecoder.encode(buffer, length); </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">if</span> (Objects.isNull(request)) { </div> <div class="ql-code-block"> log.info(<span class="ql-token hljs-string">"-------------- 断开连接 --------------\n"</span>); </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">continue</span>; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> <span class="ql-token hljs-comment">// 4.2 业务处理</span> </div> <div class="ql-code-block"> log.info(<span class="ql-token hljs-string">"url: {}"</span>, request.get(<span class="ql-token hljs-string">"Url"</span>)); </div> <div class="ql-code-block"> log.info(<span class="ql-token hljs-string">"method: {}"</span>, request.get(<span class="ql-token hljs-string">"Method"</span>)); </div> <div class="ql-code-block"> log.info(<span class="ql-token hljs-string">"data: {}"</span>, request.get(<span class="ql-token hljs-string">"Data"</span>)); </div> <div class="ql-code-block"> <span class="ql-token hljs-type">String</span> <span class="ql-token hljs-variable">responseContent</span> <span class="ql-token hljs-operator">=</span> <span class="ql-token hljs-string">"这是我的 http web 服务器 ^-^"</span>; </div> <div class="ql-code-block"> <span class="ql-token hljs-comment">// 4.3 编码</span> </div> <div class="ql-code-block"> <span class="ql-token hljs-type">HttpEncoder</span> <span class="ql-token hljs-variable">httpEncoder</span> <span class="ql-token hljs-operator">=</span> <span class="ql-token hljs-keyword">new</span> <span class="ql-token hljs-title">HttpEncoder</span>(); </div> <div class="ql-code-block"> <span class="ql-token hljs-type">byte</span>[] response = httpEncoder.encode(responseContent); </div> <div class="ql-code-block"> </div> <div class="ql-code-block"> <span class="ql-token hljs-comment">// 5.output : 返回数据</span> </div> <div class="ql-code-block"> outputStream.write(response); </div> <div class="ql-code-block"> outputStream.flush(); </div> <div class="ql-code-block"> </div> <div class="ql-code-block"> <span class="ql-token hljs-comment">// 6.断开连接</span> </div> <div class="ql-code-block"> log.info(<span class="ql-token hljs-string">"-------------- 断开连接 --------------\n"</span>); </div> <div class="ql-code-block"> socket.close(); </div> <div class="ql-code-block"> } <span class="ql-token hljs-keyword">catch</span> (IOException e) { </div> <div class="ql-code-block"> log.error(<span class="ql-token hljs-string">"发生异常:"</span>, e); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } <span class="ql-token hljs-keyword">catch</span> (IOException e) { </div> <div class="ql-code-block"> log.error(<span class="ql-token hljs-string">"socket simple 服务端启动异常,{}"</span>, e.getMessage()); </div> <div class="ql-code-block"> e.printStackTrace(); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> </div> </div> <p>这里使用大部分人最熟悉的 Java BIO , 这段代码能够体现网络编程服务端的过程:</p> <ol> <li data-list="ordered"><span class="ql-ui"></span>创建 socket 绑定IP(服务端 IP 是 localhost)、端口;</li> </ol> <div class="ql-code-block-container"> <div class="ql-code-block"> ServerSocket serverSocket = new ServerSocket(port) </div> </div> <ol> <li data-list="ordered"><span class="ql-ui"></span>循环监听;</li> </ol> <div class="ql-code-block-container"> <div class="ql-code-block"> while (socket = serverSocket.accept()) != NULL){...} </div> </div> <ol> <li data-list="ordered"><span class="ql-ui"></span>有客户端连接上来创建连接 socket 建立连接(连接事件);</li> </ol> <div class="ql-code-block-container"> <div class="ql-code-block"> socket = serverSocket.accept() </div> </div> <ol> <li data-list="ordered"><span class="ql-ui"></span>读取数据,然后执业务处理:</li> </ol> <div class="ql-code-block-container"> <div class="ql-code-block"> byte[] buffer = new byte[1024 * 256]; </div> <div class="ql-code-block"> int length = inputStream.read(buffer); </div> </div> <p>这里自定义了一个 http 协议解码类 HttpDecoder,从网络传输的数据是经过序列化的 “0101”这样的二进制字节流,所以要先反序列化:<strong>将字节数组转成 Java 中的对象 String</strong>,然后按 Http 协议的格式进行解析,将请求信息(请求行、method、uri、请求头、请求体数据)获取到一个 Map 中。</p> <div class="ql-code-block-container"> <div class="ql-code-block"> Map<String, String> request = httpDecoder.deEncode(buffer, length); </div> </div> <p>下面是 HTTP 协议请求数据格式:</p> <p><span class="ql-font-monospace"> </span></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>HttpDecoder 代码</li> </ol> <div class="ql-code-block-container"> <div class="ql-code-block"><span class="ql-token hljs-meta">@Slf4j</span> </div> <div class="ql-code-block"><span class="ql-token hljs-keyword">public</span> <span class="ql-token hljs-keyword">class</span> <span class="ql-token hljs-title">HttpDecoder</span> { </div> <div class="ql-code-block"> </div> <div class="ql-code-block"> <span class="ql-token hljs-comment">/**</span> </div> <div class="ql-code-block"><span class="ql-token hljs-comment"> * 解码</span> </div> <div class="ql-code-block"><span class="ql-token hljs-comment"> *</span> </div> <div class="ql-code-block"><span class="ql-token hljs-comment"> * @param buffer 字节数组</span> </div> <div class="ql-code-block"><span class="ql-token hljs-comment"> * @param length 数据长度</span> </div> <div class="ql-code-block"><span class="ql-token hljs-comment"> * @return 转换后的 Java Map 对象</span> </div> <div class="ql-code-block"><span class="ql-token hljs-comment"> */</span> </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">public</span> Map<String, String> <span class="ql-token hljs-title">decode(byte[] buffer, int length)</span> { </div> <div class="ql-code-block"> </div> <div class="ql-code-block"> <span class="ql-token hljs-type">StringBuffer</span> <span class="ql-token hljs-variable">httpContent</span> <span class="ql-token hljs-operator">=</span> <span class="ql-token hljs-keyword">new</span> <span class="ql-token hljs-title">StringBuffer</span>(); </div> <div class="ql-code-block"> httpContent.append(<span class="ql-token hljs-keyword">new</span> <span class="ql-token hljs-title">String</span>(buffer, <span class="ql-token hljs-number">0</span>, length)); </div> <div class="ql-code-block"> </div> <div class="ql-code-block"> log.debug(<span class="ql-token hljs-string">"服务端接收请求:\n{}"</span>, httpContent); </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">if</span> (httpContent.length() <= <span class="ql-token hljs-number">0</span>) { </div> <div class="ql-code-block"> log.info(<span class="ql-token hljs-string">"数据不符合 http 格式,处理结束!"</span>); </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">return</span> <span class="ql-token hljs-literal">null</span>; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> </div> <div class="ql-code-block"> </div> <div class="ql-code-block"> String[] content = httpContent.toString().split(EMPTY_LINE); </div> <div class="ql-code-block"> String[] lines = content[<span class="ql-token hljs-number">0</span>].split(LINE_BREAK); </div> <div class="ql-code-block"> Map<String, String> request = <span class="ql-token hljs-keyword">new</span> <span class="ql-token hljs-title">HashMap</span><>(); </div> <div class="ql-code-block"> <span class="ql-token hljs-comment">// 请求行</span> </div> <div class="ql-code-block"> <span class="ql-token hljs-type">String</span> <span class="ql-token hljs-variable">header</span> <span class="ql-token hljs-operator">=</span> lines[<span class="ql-token hljs-number">0</span>]; </div> <div class="ql-code-block"> request.put(<span class="ql-token hljs-string">"Header"</span>, header); </div> <div class="ql-code-block"> String[] headerInfo = header.split(SPACE); </div> <div class="ql-code-block"> </div> <div class="ql-code-block"> request.put(<span class="ql-token hljs-string">"Method"</span>, headerInfo[<span class="ql-token hljs-number">0</span>]); </div> <div class="ql-code-block"> request.put(<span class="ql-token hljs-string">"Uri"</span>, headerInfo[<span class="ql-token hljs-number">1</span>]); </div> <div class="ql-code-block"> request.put(<span class="ql-token hljs-string">"Protocol"</span>, headerInfo[<span class="ql-token hljs-number">2</span>]); </div> <div class="ql-code-block"> </div> <div class="ql-code-block"> <span class="ql-token hljs-comment">// 请求头</span> </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">for</span> (<span class="ql-token hljs-type">int</span> <span class="ql-token hljs-variable">i</span> <span class="ql-token hljs-operator">=</span> <span class="ql-token hljs-number">1</span>; i < lines.length; i++) { </div> <div class="ql-code-block"> String[] line = lines[i].split(LINER_SPLIT); </div> <div class="ql-code-block"> request.put(line[<span class="ql-token hljs-number">0</span>], line[<span class="ql-token hljs-number">1</span>]); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> <span class="ql-token hljs-type">String</span> <span class="ql-token hljs-variable">url</span> <span class="ql-token hljs-operator">=</span> request.get(<span class="ql-token hljs-string">"Host"</span>) + request.get(<span class="ql-token hljs-string">"Uri"</span>); </div> <div class="ql-code-block"> request.put(<span class="ql-token hljs-string">"Url"</span>, url); </div> <div class="ql-code-block"> </div> <div class="ql-code-block"> <span class="ql-token hljs-comment">// 数据</span> </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">if</span> (content.length > <span class="ql-token hljs-number">1</span>) { </div> <div class="ql-code-block"> request.put(<span class="ql-token hljs-string">"Data"</span>, content[<span class="ql-token hljs-number">1</span>]); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">return</span> request; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> </div> </div> <p>抽取的公共常量(协议内容分割符):</p> <div class="ql-code-block-container"> <div class="ql-code-block"> public static final String LINE_BREAK = "\r\n"; </div> <div class="ql-code-block"> public static final String LINER_SPLIT = ": "; </div> <div class="ql-code-block"> public static final String EMPTY_LINE = "\r\n\r\n"; </div> <div class="ql-code-block"> public static final String SPACE = "\\s"; </div> </div> <ol> <li data-list="bullet"><span class="ql-ui"></span>然后进行业务处理</li> </ol> <p>打印请求的 url, method 和 请求体数据</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>准备给浏览器的响应数据</li> </ol> <div class="ql-code-block-container"> <div class="ql-code-block"> HttpEncoder httpEncoder = new HttpEncoder(); </div> <div class="ql-code-block"> byte[] response = httpEncoder.encode(responseContent); </div> </div> <p>这里自定义了一个的 http 协议编码类 HttpEecoder,encode 方法将响应给浏览器的数据 Java 字符串,按 Http 协议的格式进行组装,添加上状态行、消息包头部。</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>http 响应格式</li> </ol> <p><span class="ql-font-monospace"> </span></p> <p><br></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>Java 对象序列化</li> </ol> <p>组装完返回给客户端的 response 对象,要通过网络传输,所以得序列化成字节流。</p> <div class="ql-code-block-container"> <div class="ql-code-block"> response.toString().getBytes(StandardCharsets.UTF_8) </div> </div> <ol> <li data-list="bullet"><span class="ql-ui"></span>协议编码类:HttpEncoder</li> </ol> <div class="ql-code-block-container"> <div class="ql-code-block"><span class="ql-token hljs-meta">@Slf4j</span> </div> <div class="ql-code-block"><span class="ql-token hljs-keyword">public</span> <span class="ql-token hljs-keyword">class</span> <span class="ql-token hljs-title">HttpEncoder</span> { </div> <div class="ql-code-block"> </div> <div class="ql-code-block"> Map<String, String> response = <span class="ql-token hljs-keyword">new</span> <span class="ql-token hljs-title">HashMap</span><>(); </div> <div class="ql-code-block"> </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">public</span> <span class="ql-token hljs-type">byte</span>[] encode(String content) { </div> <div class="ql-code-block"> <span class="ql-token hljs-type">StringBuffer</span> <span class="ql-token hljs-variable">response</span> <span class="ql-token hljs-operator">=</span> <span class="ql-token hljs-keyword">new</span> <span class="ql-token hljs-title">StringBuffer</span>(); </div> <div class="ql-code-block"> <span class="ql-token hljs-comment">// 状态行</span> </div> <div class="ql-code-block"> <span class="ql-token hljs-type">String</span> <span class="ql-token hljs-variable">statusLine</span> <span class="ql-token hljs-operator">=</span> <span class="ql-token hljs-string">"HTTP/1.1 200 OK"</span>; </div> <div class="ql-code-block"> response.append(statusLine).append(LINE_BREAK); </div> <div class="ql-code-block"> </div> <div class="ql-code-block"> <span class="ql-token hljs-comment">// 消息包头</span> </div> <div class="ql-code-block"> response.append(<span class="ql-token hljs-string">"Server"</span>).append(LINER_SPLIT).append(<span class="ql-token hljs-string">"CaijuanCat"</span>).append(LINE_BREAK); </div> <div class="ql-code-block"> response.append(<span class="ql-token hljs-string">"Content-Type"</span>).append(LINER_SPLIT).append(<span class="ql-token hljs-string">"text/plain;charset=UTF-8"</span>).append(LINE_BREAK); </div> <div class="ql-code-block"> </div> <div class="ql-code-block"> <span class="ql-token hljs-comment">// 返回数据</span> </div> <div class="ql-code-block"> response.append(EMPTY_LINE); </div> <div class="ql-code-block"> response.append(content); </div> <div class="ql-code-block"> </div> <div class="ql-code-block"> log.debug(<span class="ql-token hljs-string">"服务端发送响应:\n{}"</span>, response); </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">return</span> response.toString().getBytes(StandardCharsets.UTF_8); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> </div> <ol> <li data-list="ordered"><span class="ql-ui"></span>写数据</li> </ol> <div class="ql-code-block-container"> <div class="ql-code-block"> outputStream.write(response); </div> <div class="ql-code-block"> outputStream.flush(); </div> </div> <ol> <li data-list="ordered"><span class="ql-ui"></span>关闭连接</li> </ol> <div class="ql-code-block-container"> <div class="ql-code-block"> socket.close(); </div> </div> <p>上面是用代码描述网络编程服务端的流程,如果用图示描述如下:<span class="ql-font-monospace"> </span></p> <p><br></p> <p><br></p> <h3><strong style="color: rgb(51, 51, 51);">客户端和服务端完整流程</strong></h3> <p><br></p> <p><br></p> <p>下图是服务端和客户端使用 socket 通信的完整过程:</p> <p><span class="ql-font-monospace"> </span></p> <p>客户端和服务端类似,区别在于:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>服务端创建 socket 只需要指定端口,IP 就是本机,服务端 I/O 是等待连接 accept 事件;客户端 socket 要指定待连接的服务器 IP 和端口,客户端的端口是随机的,客户端 I/O 是发起连接 connect 事件;</li> <li data-list="bullet"><span class="ql-ui"></span>服务端要处理若干个客户端的请求,服务端和客户端是 1:n 的关系,所在在指定端口循环监听;客户端不需要循环监听,发起请求到会话结束就关闭连接。实际场景中也是客户端有需要的时候上线,服务端 7 x 24 小时在线提供服务。</li> </ol> <p><br></p> <p><br></p> <h2><strong style="color: rgb(51, 51, 51);">什么是 socket?</strong></h2> <p><br></p> <p><br></p> <p>那 socket 是个啥?</p> <p>某SDN: <a href="https://blog.csdn.net/github_34606293/article/details/78230456" target="_blank" style="color: rgb(65, 131, 196);">https://blog.csdn.net/github_34606293/article/details/78230456</a></p> <p>实际上socket是对TCP/IP协议的封装,它的出现只是使得程序员更方便地使用TCP/IP协议栈而已。socket本身并不是协议,它是应用层与TCP/IP协议族通信的中间软件抽象层,是一组调用接口(TCP/IP网络的API函数)</p> <p><span class="ql-font-monospace"> </span></p> <p>《网络是怎样链接的》:</p> <p>Socket 库是用于调用网络功能的程序组件集合。</p> <p><span class="ql-font-monospace"> </span></p> <p>某乎:<a href="https://www.zhihu.com/tardis/sogou/art/339230587" target="_blank" style="color: rgb(65, 131, 196);">https://www.zhihu.com/tardis/sogou/art/339230587</a></p> <p>每一个 socket 都用一个半相关描述{协议、本地地址、本地端口}来表示;一个完整的套接字则用一 个相关描述{协议、本地地址、本地端口、远程地址、远程端口}来表示。socket 也有一个类似于打开文 件的函数调用,该函数返回一个整型的 socket 描述符,随后的连接建立、数据传输等操作都是通过 socket 来实现的;</p> <p><br></p> <p><br></p> <h2><strong style="color: rgb(51, 51, 51);">总结</strong></h2> <p><br></p> <p><br></p> <ol> <li data-list="bullet"><span class="ql-ui"></span><strong>网络编程</strong></li> </ol> <p>网络编程最主要的工作就是:在发送端把信息通过规定好的协议进行组装包,在接收端按照规定好的协议把包进行解析,从而提取出对应的信息,达到通信的目的。<strong>网络编程的实质是跨主机跨网络的线程间通信</strong>。</p> <ol> <li data-list="bullet"><span class="ql-ui"></span><strong>socket 组件</strong></li> </ol> <p>socket是应用层与TCP/IP协议族通信的中间软件抽象层,是为了程序员开发的一组编程接口。</p> <p>socket 在收发数据的操作分为若干个阶段,客户端可以大致总结为以下 4 个:</p> <p>(1)创建套接字(创建套接字阶段)</p> <p>(2)将管道连接到服务器端的套接字上(连接阶段)</p> <p>(3)收发数据(通信阶段)</p> <p>(4)断开管道并删除套接字(断开阶段)</p> <p>服务器是将阶段(2)改成了等待连接,具体如下:</p> <p>(1)创建套接字(创建套接字阶段)</p> <p>(2-1)将套接字设置为等待连接状态(等待连接阶段)</p> <p>(2-2)接受连接(接受连接阶段)</p> <p>(3)收发数据(收发阶段)</p> <p>(4)断开管道并删除套接字(断开阶段)</p> <ol> <li data-list="bullet"><span class="ql-ui"></span><strong>为什么要讲这个?</strong></li> </ol> <p><strong>问题是解决方案出发的原点</strong>,技术是解决问题的工具,要明白技术为什么要那么设计和实现,就必须先明确它要解决的问题。</p> <p><br></p> <p><br></p> <h2><strong style="color: rgb(51, 51, 51);">参考</strong></h2> <p><br></p> <p><a href="https://blog.csdn.net/github_34606293/article/details/78230456" target="_blank" style="color: rgb(65, 131, 196);">Socket到底是什么?</a></p> <p><a href="https://www.jianshu.com/p/8fe93a14754c" target="_blank" style="color: rgb(65, 131, 196);">HTTP协议格式详解</a></p> <p>《网络是怎样连接的》</p> <p><br></p> </div> </body> </html>
菜卷的 Netty 笔记(一):Netty 简介
<html> <head></head> <body> <div class="content ql-editor"> <p><br></p> <h1><strong style="color: rgb(81, 81, 81);">一、Netty 简介</strong></h1> <blockquote> <div class="blockquote-item"> 为什么写这篇文章? </div> </blockquote> <p>这篇文章是对 Netty 的基础介绍,偏概念和理论。主要从概念上介绍什么是 Netty、应用领域 、特性、社区活跃度、发展趋势等。</p> <p>内容大多来自网络,搜集后自己整理,对于之前没有接触过 Netty 的同学做一点基础介绍。不会在我提到 Netty 的时候问:“Netty 是大数据计算组件吗?”</p> <p><br></p> <h2><strong style="color: rgb(81, 81, 81);">介绍</strong></h2> <p><br></p> <p>Netty 是一个 Java NIO 的高性能网络编程应用框架,是目前 Java 领域最主流的网络编程框架。</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1619331527399800834/xw30h59p.jpeg" style="display: block; margin: auto;" align="center"></p> <p>为什么做 web 服务端开发的程序员对网络编程没啥感知?</p> <p>提到网络编程库,对于做业务开发的同学可能没啥感知,比如我一个 web 服务端开发 —— Java CRUD BOY 完全不知道网络编程要干啥。大部分人都会接触的网络编程是 Java BIO,就是 Socket 这块儿,还有的同学可能看过 Java1.4推出的 NIO,Channel、Buffer、Selector这些。</p> <p>网络编程都在用在哪儿呢?只要涉及网络传输的网络应用都使用了网络编程。但在平常的 web 服务开发时,我们使用了Tomcat、JBoss、Jetty等支持 servlet规范的 web 服务器,网络连接的建立、数据的读写、序列化、协议的编解码、都由底层的 web 容器完成,我们只需要专注于业务代码编写。</p> <p>相比于 web 容器,Netty 是面向<strong style="color: black;">网络传输层协议(Socket)</strong> 的网络编程框架,也可以用来开发 Web 服务器。Netty 不仅支持 HTTP 协议,还支持 SSH、TLS/SSL 等多种应用层的协议,它支持的协议更广泛,还可以自定义通信协议。</p> <p><br></p> <p><br></p> <h2><strong style="color: rgb(81, 81, 81);">特性</strong></h2> <p><br></p> <p>对于一款网络编程框架,下面是几个我们绕不开的几个关注点:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>I/O 模型</li> <li data-list="bullet"><span class="ql-ui"></span>线程模型</li> <li data-list="bullet"><span class="ql-ui"></span>事件处理机制</li> <li data-list="bullet"><span class="ql-ui"></span>对数据协议、序列化的支持</li> <li data-list="bullet"><span class="ql-ui"></span>API 的易用性</li> </ol> <p>在后续的文章,我将介绍为啥这些我们要关注这些东西。Netty 采用 I/O 多路复用模型、主从 Reactor 线程模型,具有高性能、低延时的特征;API 设计简单易用,相比于 Java NIO 繁琐的 API ,极大地降低了开发者的心智负担和编程难度;Netty 设计扩展性好,;并且对 http、websocket等许多协议提供丰富的支持。正是因为在这些方面表现突出,所以 Netty 成为 Java 在网络编程领域的首选。</p> <p><br></p> <p><br></p> <h2><strong style="color: rgb(81, 81, 81);">社区活跃度</strong></h2> <p><br></p> <p><br></p> <p>Netty 社区活跃,迭代周期短且文档比较齐全,遇到任何问题通过 issue 或者邮件都可以得到非常及时的答复。目前,Netty 官方提供 3.x、4.x 的稳定版本,之前一直处于测试阶段的 5.x 版本已被作者放弃维护,当下主流的版本是 Netty 4.x。</p> <p>社区地址:<a href="https://netty.io" target="_blank">https://netty.io/</a></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1619331527399800834/ne6tysux.jpeg"></p> <p><br></p> <p><br></p> <h2><strong style="color: rgb(81, 81, 81);">发展趋势</strong></h2> <p><br></p> <p><br></p> <p>Netty 是目前最流行的一款高性能 Java 网络编程框架,它被广泛使用在中间件、直播、社交、游戏等领域。使用 Netty 的大厂也不在少数:</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1619331527399800834/ugq0dyn1.jpeg" style="display: block; margin: auto;" align="center"></p> <p class="ql-align-center">图片来自网络</p> <p><br></p> <p>截止至 2020 年 7 月,Netty 在 github 上获取2.4w+ star,一共被 4w+ 的项目所使用。目前,许多知名的开源软件也都将 Netty 用作网络通信的底层框架,Netty 经过很多出名产品在线上的大规模验证,其健壮性和稳定性都被业界认可,其中典型的产品有:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>服务治理:Apache Dubbo、gRPC。</li> <li data-list="bullet"><span class="ql-ui"></span>大数据:Hbase、Spark、Flink、Storm。</li> <li data-list="bullet"><span class="ql-ui"></span>搜索引擎:Elasticsearch。</li> <li data-list="bullet"><span class="ql-ui"></span>消息队列:RocketMQ、ActiveMQ。</li> <li data-list="bullet"><span class="ql-ui"></span>更多查看官方 wiki:<a href="https://netty.io/wiki/related-projects.html" target="_blank">https://netty.io/wiki/related-projects.html</a></li> </ol> <p><br></p> <p><br></p> <h2><strong style="color: rgb(81, 81, 81);">后记</strong></h2> <p><br></p> <p>这是我写东西以来,第一次写技术文章,虽然文章写的没啥技术含量。上班入职后,公司新人要做技术分享,借此契机开始写技术文章。虽然也没人看,但错误和不足之处,希望读者朋友指出,如果觉得不错就点个赞吧。</p> <p><br></p> </div> </body> </html>
Netty 编程入门
<html> <head></head> <body> <div class="content ql-editor"> <h1>一、Netty 简介</h1> <p><br></p> <h2>介绍</h2> <p>Netty 是一个 <code>Java NIO</code> 的高性能网络编程应用框架,是目前 Java 领域最主流的网络编程框架。</p> <blockquote> <div class="blockquote-item"> 为什么做 web 服务端开发的程序员对网络编程没啥感知? </div> </blockquote> <p>提到网络编程库,对于做业务开发的同学可能没啥感知,比如我一个服务端开发 —— Java CRUD BOY 完全不知道网络编程要干啥。大部分人都会接触的网络编程是 <code>Java BIO,就是 Socket 这块儿,还有的同学可能看过 Java1.4推出的 NIO,Channel、Buffer、Selector这些。网络编程都在用在哪儿呢?只要涉及网络传输的网络应用都使用了网络编程。但在平常的 web 服务开发时,我们使用了Tomcat、JBoss、Jetty等支持 servlet</code> 规范的 web 服务器,HTTP 协议的编解码、网络连接的建立、数据的网络收发都由底层的 web 容器完成,我们只需要专注于业务代码编写。 Netty 是一款面向<strong>网络传输层协议(Socket)</strong> 的网络编程框架,也可以用来开发 Web 服务器。Netty 不仅支持 HTTP 协议,还支持 SSH、TLS/SSL 等多种应用层的协议,它支持的协议更广泛,还可以自定义通信协议。</p> <p><br></p> <h2>特性</h2> <p>对于一款网络编程框架,下面是几个我们绕不开的几个关注点:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>I/O 模型</li> <li data-list="bullet"><span class="ql-ui"></span>线程模型</li> <li data-list="bullet"><span class="ql-ui"></span>事件处理机制</li> <li data-list="bullet"><span class="ql-ui"></span>对数据协议、序列化的支持</li> <li data-list="bullet"><span class="ql-ui"></span>API 的易用性</li> </ol> <p><br></p> <p>Netty 在上述的方面表现突出,具有以下特征:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>对 <code>Java NIO</code> 的高级封装,API 易用</li> <li data-list="bullet"><span class="ql-ui"></span>高性能、低延时</li> <li data-list="bullet"><span class="ql-ui"></span>对序列化和网络协议的支持</li> </ol> <p><br></p> <h2>社区活跃度</h2> <p>Netty 如此成功离不开社区的精心运营,迭代周期短且文档比较齐全,如果你遇到任何问题通过 issue 或者邮件都可以得到非常及时的答复。目前,Netty 官方提供 3.x、4.x 的稳定版本,之前一直处于测试阶段的 5.x 版本已被作者放弃维护。</p> <p><br></p> <h2>趋势</h2> <p>Netty 是目前最流行的一款高性能 Java 网络编程框架,它被广泛使用在中间件、直播、社交、游戏等领域。截止至 2020 年 7 月,Netty 在 github 上获取2.4w+ star,一共被 4w+ 的项目所使用。目前,许多知名的开源软件也都将 Netty 用作网络通信的底层框架,Netty 经过很多出名产品在线上的大规模验证,其健壮性和稳定性都被业界认可,其中典型的产品有:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>服务治理:Apache Dubbo、gRPC。</li> <li data-list="bullet"><span class="ql-ui"></span>大数据:Hbase、Spark、Flink、Storm。</li> <li data-list="bullet"><span class="ql-ui"></span>搜索引擎:Elasticsearch。</li> <li data-list="bullet"><span class="ql-ui"></span>消息队列:RocketMQ、ActiveMQ。</li> </ol> <p><br></p> <p><a href="https://netty.io/wiki/related-projects.html" target="_blank">更多查看官方 wiki</a></p> </div> </body> </html>
【每日知识点精讲】9月2日 Netty 中的数据传输载体 ByteBuf
<html> <head></head> <body> <div class="content ql-editor"> <p>大家好,我是 Carrey。今天精讲的是 Netty 中很重要的 <strong>ByteBuf</strong>,它是 Netty 中数据交互的基本单位。</p> <p><br></p> <h4><strong style="color: rgb(51, 51, 51);">ByteBuf结构解析</strong></h4> <p>光靠文字不好理解,先上图</p> <p><br></p> <p><span class="ql-font-monospace"> </span><img src="https://pic.code-nav.cn/planet_post_image/1608019764955865090/zhx28g3m.jpeg"></p> <p><br></p> <p>上面就是 ByteBuf 的结构图了,接下来进入精讲</p> <p><br></p> <p>ByteBuf 是一个<strong>字节容器</strong>,容器主要分为以下部分</p> <ol> <li data-list="bullet"><span class="ql-ui"></span><strong>已经废弃的字节</strong>,这部分是无效数据</li> <li data-list="bullet"><span class="ql-ui"></span><strong>可读字节</strong>,这部分是 ByteBuf 的主体数据,是从 ByteBuf 读数据的部分</li> <li data-list="bullet"><span class="ql-ui"></span><strong>可写字节</strong>,所有新写入ByteBuf的数据都会写到这部分中</li> <li data-list="bullet"><span class="ql-ui"></span><strong>可扩容部分</strong>。图中最后一部分得虚线表示该 ByteBuf 还能扩容的字节数</li> </ol> <p>除了可扩容部分,<strong>包含数据的部分</strong>是通过<strong>两个指针</strong>进行划分的</p> <ol> <li data-list="bullet"><span class="ql-ui"></span><strong>读指针 readerIndex</strong> 每次从 ByteBuf 中读一个字节,读指针右移一位,此时 ByteBuf 中还有 writerIndex - readerIndex 个字节可读</li> <li data-list="bullet"><span class="ql-ui"></span><strong>写指针 writerIndex</strong> 每次向 ByteBuf 中写一个字节,写指针右移一位,此时 ByteBuf 中还有 capacity- writerIndex 个字节可写</li> </ol> <p>当容量 capacity 等于 writerIndex,ByteBuf 不可写,就会进行扩容。 <strong>扩容机制</strong>如下:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>假设 ByteBuf 初始容量是10。 如果写入后数据大小未超过512个字节,则选择下一个16的整数倍进行扩容。 比如写入数据后大小为12,则扩容后的capacity是16。</li> <li data-list="bullet"><span class="ql-ui"></span>如果写入后数据大小超过512个字节,则选择下一个2的n次方。比如写入后大小是512字节,则扩容后的capacity是2的10次方=1024。(因为2的9次方=512,长度已经不够了)</li> <li data-list="bullet"><span class="ql-ui"></span>扩容不能超过max capacity,否则会报错。</li> </ol> <p>关于扩容机制的源码解读,可以看这篇:<a href="https://blog.csdn.net/qq_38796327/article/details/104209159" target="_blank" style="color: rgb(65, 131, 196);">(132条消息) 记录一下netty ByteBuf扩容机制_chenm1xuexi的博客-CSDN博客</a></p> <p>总之,ByteBuf 是 Netty 对于二进制数据的抽象,这个数据结构可以有效区分可读数据和可写数据。</p> <p><br></p> <p>接下来介绍 ByteBuf 中一些常见的 API</p> <p><strong style="font-size: 1em;font-style;font-variant-ligatures;font-variant-caps; color: rgb(51, 51, 51);">ByteBuf 常见 API</strong></p> <p><strong style="font-size: 0.83em;font-style;font-variant-ligatures;font-variant-caps; color: rgb(51, 51, 51);">读写指针相关</strong></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>readerIndex() 与 readerIndex(int) 前者表示返回当前的读指针 readerIndex, 后者表示设置读指针</li> <li data-list="bullet"><span class="ql-ui"></span>writeIndex() 与 writeIndex(int) 前者表示返回当前的写指针 writerIndex, 后者表示设置写指针</li> <li data-list="bullet"><span class="ql-ui"></span>markReaderIndex() 与 resetReaderIndex() 前者表示把当前的读指针保存起来,后者表示把当前的读指针恢复到之前保存的值,代码实例如下:</li> </ol> <div class="ql-code-block-container"> <div class="ql-code-block"> // 保存 </div> <div class="ql-code-block"> buffer.markReaderIndex(); </div> <div class="ql-code-block"> // 恢复 </div> <div class="ql-code-block"> buffer.resetReaderIndex(); </div> </div> <ol> <li data-list="bullet"><span class="ql-ui"></span>markWriterIndex() 与 resetWriterIndex() 前者表示把当前的写指针保存起来,后者表示把当前的写指针恢复到之前保存的值。</li> </ol> <p><br></p> <h5><strong style="color: rgb(51, 51, 51);">读写相关</strong></h5> <ol> <li data-list="bullet"><span class="ql-ui"></span>writeBytes(byte[] src) 表示把字节数组 src 里面的数据全部写到 ByteBuf,src 字节数组大小的长度通常小于等于writableBytes()</li> <li data-list="bullet"><span class="ql-ui"></span> readBytes(byte[] dst) writeBytes() 表示把 ByteBuf 里面的数据全部读取到 dst,这里 dst 字节数组的大小通常等于 readableBytes()</li> <li data-list="bullet"><span class="ql-ui"></span>writeByte() 表示往 ByteBuf 中写一个字节</li> <li data-list="bullet"><span class="ql-ui"></span>readByte() 表示从 ByteBuf 中读取一个字节</li> <li data-list="bullet"><span class="ql-ui"></span>writeBoolean()、writeChar()、writeShort()、writeInt()、writeLong()、writeFloat()、writeDouble() 与 readBoolean()、readChar()、readShort()、readInt()、readLong()、readFloat()、readDouble() 等,见名知意</li> <li data-list="bullet"><span class="ql-ui"></span>getBytes、getByte() 与 setBytes()、setByte() 也是读字节和写字节,区别是 <strong>get/set 不会改变读写指针,而 read/write 会改变读写指针</strong>,在解析数据时要注意。</li> <li data-list="bullet"><span class="ql-ui"></span>release() 与 retain() 前者是让该 ByteBuf 引用计数减1,后者则相反</li> <li data-list="bullet" class="ql-indent-1"><span class="ql-ui"></span>什么是<strong>引用计数</strong>?Netty中使用了不被 JVM 直接管理的堆外内存,需要手动回收,而 Netty 中 ButeBuf是通过<strong>引用计数</strong>进行管理的。创建时引用为1,然后每次调用retain方法引用加1,调用release方法则引用计数减1。当引用计数为 0 时,ByteBuf 底层的内存会被直接回收。</li> <li data-list="bullet"><span class="ql-ui"></span>slice()、duplicate()、copy() 切片、副本和拷贝</li> <li data-list="bullet" class="ql-indent-1"><span class="ql-ui"></span>slice() 从原始 ByteBuf 中截取一段从 <strong>readerIndex 到 writeIndex</strong> 的数据,底层内存以及引用计数与原始的 ByteBuf <strong>共享</strong>,但维持着与原始 ByteBuf 相同的内存引用计数和<strong>不同的读写指针</strong></li> <li data-list="bullet" class="ql-indent-1"><span class="ql-ui"></span>duplicate() 把<strong>整个 ByteBuf</strong> 都截取出来,包括所有的数据,指针信息。底层内存以及引用计数与原始的 ByteBuf <strong>共享</strong>,但维持着与原始 ByteBuf 相同的内存引用计数和<strong>不同的读写指针</strong></li> <li data-list="bullet" class="ql-indent-1"><span class="ql-ui"></span>copy() 会直接从原始的 ByteBuf 中<strong>拷贝所有的信息</strong>,包括读写指针以及<strong>底层对应的数据</strong>。所以往 copy() 返回的 ByteBuf 中写数据不会影响到原始的 ByteBuf。</li> <li data-list="bullet" class="ql-indent-1"><span class="ql-ui"></span><strong>这三个方法均维护着自己的读写指针</strong>,和原 ByteBuf 之间互不影响。</li> <li data-list="bullet"><span class="ql-ui"></span>retainedSlice() 与 retainedDuplicate() 在截取内存片段的同时,增加内存的引用计数。因为slice() 和 duplicate() <strong>不会改变 ByteBuf 的引用计数</strong>,所以他们依赖的 ByteBuf 调用 release() 之后发现引用计数为零,会释放内存,调用这两个方法返回的 ByteBuf 也会被释放,此时再对它们进行读写,就会报错。可能有人总粗心忘记截取片段的同时增加引用计数,就有了 <strong>retainedSlice() 与 retainedDuplicate()</strong> 这两个方法。</li> </ol> <p><br></p> <h5><strong style="color: rgb(51, 51, 51);">容量相关</strong></h5> <p>以下 API 见名知意,容易理解,只简单介绍</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>capacity() 容量,包括废弃的字节、可读字节、可写字节</li> <li data-list="bullet"><span class="ql-ui"></span>maxCapacity() 底层最大能够占用多少字节的内存,扩容超过 maxCapacity就会报错</li> <li data-list="bullet"><span class="ql-ui"></span>readableBytes() 、 isReadable() 可读字节数和是否可读</li> <li data-list="bullet"><span class="ql-ui"></span>writableBytes()、 isWritable() 可写字节数和是否可写</li> <li data-list="bullet"><span class="ql-ui"></span>maxWritableBytes() 可写的最大字节数,等于 maxCapacity-writerIndex</li> </ol> <p><br></p> <h4><strong style="color: rgb(51, 51, 51);">总结</strong></h4> <p>今天主要讲了 Netty 中数据传输单位 ByteBuf 的底层结构和常见API。里面有一些细节如读写双指针的应用、扩容机制、引用计数机制、常见API细节等。希望能对大家有所帮助。</p> <p><br></p> <p>明日精讲知识点预告:Redis的应用(分布式锁、延时队列等)或者归并排序精讲+常见面试题。敬请期待!</p> <p><br></p> </div> </body> </html>
