netty服务端返数据失败_浅谈Netty的一些基础知识

本文介绍了Netty的基础知识,包括客户端和服务端的Netty逻辑,Netty与传统Socket的区别,以及ByteBuf的重要API。还讨论了自定义通信协议、服务端和客户端连接、Pipeline和ChannelHandler,特别关注了粘包拆包问题及其解决方案,如固定长度拆包器、行拆包器、分隔符拆包器和基于长度域的拆包器。
摘要由CSDN通过智能技术生成
客户端使用Netty逻辑
  1. 客户端根据用户输入创建指令对象(发送消息,群聊)

  2. TCP通信需要的数据格式为二进制,客户端通过自定义二进制协议将指令对象封装成二进制格式(协议的编码)

  3. 服务端收到数据后,截取出完整的二进制数据包(拆包粘包)

  4. 服务端通过解析二进制数据包获取指令对象,并转到对应的逻辑处理器进行处理

服务端使用Netty逻辑
  1. 服务端创建指令对象

  2. 转成二进制

  3. 客户端收到数据后,解析指令,进行处理

Netty和传统Socket比较

IO编程

  1. 线程资源受限

  2. 线程切换效率低下

  3. 数据读写以字节流为单位(一次性只能从流中读取一个或者多个字节,并且读完之后流无法再读取,你需要自己缓存数据)

NIO编程

  1. 线程绑定轮询器(selector),例如:selector1负责轮询是否有新的连接,selector2负责轮询连接是否有数据可读。

  2. 服务端监测到新的连接之后,不再创建一个新的线程,而是直接将新连接绑定到selector1上,这样就不用 IO 模型中1w个连接,创建1w个while循环在死等。

  3. selector1被一个 while 死循环包裹着,如果在某一时刻有多条连接有数据可读,那么通过 selector1.select(1)方法可以轮询出来,进而批量处理

  4. 数据的读写面向 Buffer(可以随意读取里面任何一个字节数据,不需要自己缓存数据,只需要移动读写指针)

Netty编程

  1. 封装了JDK的NIO,解决了很多NIO编程的bug(空轮询)

  2. 可随意切换IO和NIO模型

  3. 自带拆包解包,异常检测机制,各种协议栈

  4. 底层对线程,selector做了很多细小的优化,精心设计的reactor线程模型能做到非常高效的并发处理

ByteBuf

6ac297e565e33ec2f6c329f34924ba4f.png

常见API
容量API
  • capacity():容量

  • maxCapacity():允许扩容的最大字节数

  • readableBytes() 与 isReadable():获取可读写的字节数

  • writableBytes()、 isWritable() 与 maxWritableBytes():获取当前可写的字节数

读写指针相关的API
  • readerIndex() 与 readerIndex(int):返回当前读指针,后者设置当前读指针

  • writeIndex() 与 writeIndex(int):返回当前写指针,后者设置当前写指针

  • markReaderIndex() 与 resetReaderIndex():保存读指针,以及恢复到之前保存的指针

  • markWriterIndex() 与 resetWriterIndex():保存写指针,以及恢复到之前保存的指针

读写API
  • writeByte(byte b) 与 buffer.readByte(),writeBytes(byte[] src) 与 buffer.readBytes(byte[] dst)系列(writeBoolean()、writeChar()、writeShort()、writeInt()、writeLong()、writeFloat()、writeDouble() 与 readBoolean()、readChar()、readShort()、readInt()、readLong()、readFloat()、readDouble())
    getBytes、getByte() 与 setBytes()、setByte() 系列
    write,read和get,set区别:get/set 不会改变读写指针,而 read/write 会改变读写指针。

  • release() 与 retain():Netty 使用了堆外内存,需要手动释放,Netty 的 ByteBuf 是通过引用计数的方式管理的,如果一个 ByteBuf 没有地方被引用到,需要回收底层内存。每次调用 retain() 方法, 它的引用就加一, release() 方法原理是将引用计数减一,减完之后如果发现引用计数为0,则直接回收 ByteBuf 底层的内存。
    在一个函数体里面,只要增加了引用计数(包括 ByteBuf 的创建和手动调用 retain() 方法),就必须调用 release() 方法。

  • slice()、duplicate()、copy():
    slice和duplicate不会改变ByteBuf的引用计数,需要手动调用一次retain()(也可以调用组合方法:retainedSlice() 与 retainedDuplicate())
    三个方法各自维护着自己的读写指针,与原始ByteBuf读写指针无关,slice() 方法与 duplicate() 方法不会拷贝数据,slice()只截取从 readerIndex 到 writerIndex 之间的数据,而duplicate()是整个数据共享。

自定义通信协议

6f5c4c1604d6bc1f52ad2e1f63c4c18d.png

魔数,通常情况下为固定的几个字节,服务端收到的是一个标准的 HTTP 协议数据包,会按照事先约定好的协议来处理 HTTP 协议,显然,这是会解析出错的。而有了这个魔数之后,服务端首先取出前面四个字节进行比对,能够在第一时间识别出这个数据包并非是遵循自定义协议的,也就是无效数据包,为了安全考虑可以直接关闭连接以节省资源。
序列化算法表示如何把 Java

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值