最近在重新看netty,在这里总结一些netty的一些常用的使用方式,类似于模板,方便速查。

以netty 4.1.x的API作记录,不同版本可能API有略微差异,需要注意netty5被废弃掉了(辨别方式是看SimpleChannelInboundHandler是否有一个messageReceived方法 有的话就是5),netty3是以org.jboss开头为包名。

统一模板

大多数情况下使用netty的步骤是定义好EventLoopGroup,定义好Bootstrap(ServerBootstrap)以及使用的channel类型(一般就是NioSocketChannel,服务端是NioServerSocketChannel)。

剩下是业务相关,使用的ChannelInitializer和具体的handler。

主要就是2+N(N为自定义的handler数量)个类。

服务端启动模板(也可以不区分boss和worker 用一个):

  1. public static void main(String[] args) throws InterruptedException {
  2. EventLoopGroup bossGroup = new NioEventLoopGroup();
  3. EventLoopGroup workerGroup = new NioEventLoopGroup();
  4. try {
  5. ServerBootstrap serverBootstrap = new ServerBootstrap();
  6. serverBootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
  7. .childHandler(new MyChannelInitializer());
  8. ChannelFuture future = serverBootstrap.bind(8999).sync();
  9. future.channel().closeFuture().sync();
  10. } finally {
  11. bossGroup.shutdownGracefully();
  12. workerGroup.shutdownGracefully();
  13. }
  14. }

客户端启动模板:

  1. EventLoopGroup group = new NioEventLoopGroup();
  2. try {
  3. Bootstrap bootstrap = new Bootstrap()
  4. .group(group)
  5. .channel(NioSocketChannel.class)
  6. .handler(new MyChannelInitializer());
  7. ChannelFuture future = bootstrap.connect("localhost", 8888).sync();
  8. future.channel().closeFuture().sync();
  9. } finally {
  10. group.shutdownGracefully();
  11. }

ChannelInitializer模板(继承ChannelInitializer即可):

  1. public class MyChannelInitializer extends ChannelInitializer<SocketChannel> {
  2. @Override
  3. protected void initChannel(SocketChannel ch) throws Exception {
  4. ChannelPipeline pipeline = ch.pipeline();
  5. pipeline.addLast(...);
  6. }
  7. }

接下来的例子就以这个模板为骨架,主要涉及到初始化器的代码,启动代码一致。

处理Http请求

netty自带了对用的codec类比较方便。

pipeline.addLast("httpServerCodec", new HttpServerCodec());

自己实现的handler最简单的方式用SimpleChannelInboundHandler接收HttpRequest方法即可

class MyHttpHandler extends SimpleChannelInboundHandler<HttpRequest>

这里简单说下SimpleChannelInboundHandler这个类,他是简化处理接受信息并处理的一个类,主要做两件事。

第一件事是根据泛型决定是否处理这个消息,能够处理就自己处理,不行就交给下一个(可以参考acceptInboundMessagechannelRead方法)。

第二件事是消息的自动回收(有构造函数支持 默认是true),消息的引用计数会减一(所以在其他地方还要使用记得再retain一下)。

使用它可以节省很多冗余代码的编写。

一个简单例子:

  1. public class MyHttpHandler extends SimpleChannelInboundHandler<HttpRequest> {
  2. @Override
  3. protected void channelRead0(ChannelHandlerContext ctx, HttpRequest msg) throws Exception {
  4. System.out.println(msg.getClass());
  5. System.out.println(msg.uri());
  6. System.out.println(msg.method().name());
  7. System.out.println(ctx.channel().remoteAddress());
  8. System.out.println("headers:");
  9. msg.headers().forEach(System.out::println);
  10. ByteBuf buf = Unpooled.copiedBuffer("Hello World", CharsetUtil.UTF_8);
  11. FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, buf);
  12. response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain");
  13. response.headers().set(HttpHeaderNames.CONTENT_LENGTH, buf.readableBytes());
  14. ctx.writeAndFlush(response);
  15. // ctx.channel().close();
  16. }
  17. }

不过只用这个handler并不能拿到content,还需要配合ChunkedWriteHandlerHttpObjectAggregator得到FullHttpRequest对象。

处理WebSocket请求

只需要在上面的基础上增加一个WebSocketServerProtocolHandler即可,完整如下:

  1. pipeline.addLast("httpServerCodec", new HttpServerCodec());
  2. pipeline.addLast(new ChunkedWriteHandler());
  3. pipeline.addLast(new HttpObjectAggregator(8096));
  4. pipeline.addLast(new WebSocketServerProtocolHandler("/ws"));

自己的处理器可以接收并处理WebSocketFrame的子类。

自定义文本协议

netty提供了几个比较方便的用于自定义文本协议的编解码器。

基于长度

  1. pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4));
  2. pipeline.addLast(new LengthFieldPrepender(4));
  3. pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));
  4. pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
  5. pipeline.addLast(new MyHandler());

上述参数的意义直接搬了文档:

  1. lengthFieldOffset = 0
  2. lengthFieldLength = 2
  3. lengthAdjustment = 0
  4. initialBytesToStrip = 2 (= the length of the Length field)
  5. BEFORE DECODE (14 bytes) AFTER DECODE (12 bytes)
  6. +--------+----------------+ +----------------+
  7. | Length | Actual Content |----->| Actual Content |
  8. | 0x000C | "HELLO, WORLD" | | "HELLO, WORLD" |
  9. +--------+----------------+ +----------------+

基于分隔符

  1. ChannelPipeline pipeline = ch.pipeline();
  2. pipeline.addLast(new DelimiterBasedFrameDecoder(4096, Delimiters.lineDelimiter()));
  3. pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));
  4. pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));

第二个参数需要一个ByteBuf[],自己指定下分隔符即可。

注意使用这种发消息的时候要带上那个分隔符,不然会处理失败(被当做未结束)。

广播实现

利用了netty提供的一个很便利的类,ChannelGroup.

首先要了解一下Channel生命周期函数的调用:

channel added -> channel registered -> channel active -> channel inactive -> channel unregistered -> channel removed

我们可以定义一个静态(保证共享和唯一)的ChannelGroup在channel added的时候把对应channel增加到ChannelGroup中即可(但不需要自己移除,这一点他自己实现了)

然后利用它的写方法就可以实现广播,或者forEach做下过滤做多播也可以。

这个实现和用什么协议无关,主要涉及到ChannelGroup使用。

心跳

netty自带的IdleStateHandler,超时后会向下一个handler发出IdleStateEvent消息,接收并处理即可。

  1. @Override
  2. public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
  3. super.userEventTriggered(ctx, evt);
  4. if (evt instanceof IdleStateEvent) {
  5. ctx.channel().writeAndFlush("超时").addListener((ChannelFuture ch) -> ch.channel().close());
  6. }
  7. }

它细分为3中类型的超时,读、写、读写,通过IdleStateEvent的state属性可以获取,可以单独判断。

其他的一些模式会在之后复习和使用过程中不断补完~

2017年10月08日00点53分 init

2017年10月10日10点02分 更新websocket

netty常用使用方式的更多相关文章

  1. jQuery中ajax的4种常用请求方式

    jQuery中ajax的4种常用请求方式: 1.$.ajax()返回其创建的 XMLHttpRequest 对象. $.ajax() 只有一个参数:参数 key/value 对象,包含各配置及回调函数 ...

  2. iOS代码加密常用加密方式

    iOS代码加密常用加密方式 iOS代码加密常用加密方式,常见的iOS代码加密常用加密方式算法包括MD5加密.AES加密.BASE64加密,三大算法iOS代码加密是如何进行加密的,且看下文 MD5 iO ...

  3. DataGridView 中添加CheckBox和常用处理方式 .

    DataGridView 中添加CheckBox和常用处理方式 文章1 转载:http://blog.csdn.net/pinkey1987/article/details/5267934 DataG ...

  4. python常用执行方式&变量&input函数

    linux系统中执行py文件方式:  ./a.py 需要执行权限 chmod -R 777(最大权限) 常用执行方式: 1. ./a.py2. python a.py 文件内部头加上 #!/usr/b ...

  5. Linux 常用分区方式

    1 分两个区 主目录:/ 交换分区:swap 2 常用分区方式,以使用100G空间安装linux为例 引导分区: 挂载点/boot,分区格式ext4,500M以内即可 交换分区: 无挂载点,分区格式选 ...

  6. python-django-ORM,常用查询方式

    介绍django model 的一些常用查询方式 首先是一些文档性的帮助 __exact 精确等于 like ‘aaa’ __iexact 精确等于 忽略大小写 ilike ‘aaa’ __conta ...

  7. 【转】Verilog HDL常用建模方式——《Verilog与数字ASIC设计基础》读书笔记(四)

    Verilog HDL常用建模方式——<Verilog与数字ASIC设计基础>读书笔记(四) Verilog HDL的基本功能之一是描述可综合的硬件逻辑电路.所谓综合(Synthesis) ...

  8. 前后端常用通讯方式-- ajax 、websocket

    一.前后端常用通讯方式 1. ajax  浏览器发起请求,服务器返回数据,服务器不能主动返回数据,要实现实时数据交互只能是ajax轮询(让浏览器隔个几秒就发送一次请求,然后更新客户端显示.这种方式实际 ...

  9. SoapUI 的几种常用参数化方式

    今天给大家来梳理下soapui这款工具关于参数化的几种方式以及具体的应用场景 1.properties 官方文档:https://www.soapui.org/docs/functional-test ...

随机推荐

  1. spring 内部工作机制(二)

    本章节讲Spring容器从加载配置文件到创建出一个完整Bean的作业流程及参与的角色. Spring 启动时读取应用程序提供的Bean配置信息,并在Spring容器中生成一份相应的Bean配置注册表, ...

  2. java递归的应用和实例

    使用计算机计算组合数: 1.使用组合数公式利用n!来计算 设计思想 (1)首先解决求n!的函数 (2)再结合组合数公式,求组合数 程序流程图 源程序代码 package Zuote; import j ...

  3. SQL监测语句

    SELECT top 20 qs.creation_time,last_execution_time,total_physical_reads,total_logical_reads,total_lo ...

  4. IDL 创建数组

    1.赋值创建 通过方括号[]赋值创建数组,示例代码如下 IDL> arr=[1,2,3] IDL> help,arr ARR INT = Array[3] IDL> arr=[[1, ...

  5. Ubuntu16.04下Office替代品Office Online

    Ubuntu16.04下Office替代品 Ubuntu16.04下的office Libreoffice 这个是Ubuntu自带的Office,总是存在各种问题,如果用来阅读还是不错的,但是编辑就不 ...

  6. win10 uwp clone

    clone 可以用MemberwiseClone来复制一个类 但这个复制是浅复制,创建一个新的object然后复制值字段,对于引用就直接复制引用,不复制引用的本身,指向同样引用 如果要复制引用,可以使 ...

  7. win10 uwp 随着数字变化颜色控件

    我朋友在做一个控件,是显示异常,那么异常多就变为颜色,大概就是下面的图,很简单 首先是一个Ellipse,然后把他的颜色绑定到Int,需要一个转换,UWP的转换和WPF差不多,因为我现在还不会转换,就 ...

  8. Android基础知识04—Activity活动之间传递数据

    ------活动之间传递数据------ 向下一个活动传递数据: Intent中提供了一系列的putExtra()方法,可以把数据暂存到Intent中,启动另一个活动的时候就可以取出来. 代码: (存 ...

  9. VIM格式化代码(How to format code with VIM)

    1) 按两下小写g,即gg,定位光标到第一行.(2) 按住Shift+v,即大写V,进入可视化编辑的列编辑模式.(3) Shift+g,即大写G,选中整个代码.(4) 按下等号=,格式化所有代码.

  10. UVa11882,Biggest Number

    搜索+剪枝 如此水的一个题,居然搞了一上午 出错在bfs与dfs时共用了一个vis数组,导致bfs完后返回dfs应该能访问到的点访问不到 自己想怎么剪枝,想了几个剪枝方法,又证明,又推翻,再想,再证明 ...