netty(4)–编解码器

​ 每个网络应用程序都必须定义如何解析在两个节点之间来回传输的原始字节,以及如何将其和目标应用程序的数据格式做相互转换。这种转换逻辑由编解码器处理,编解码器由编码器和解码器组成,它们每种都可以将字节流从一种格式转换为另一种格式。解决粘包半包的其实也是编解码器框架的一部分。

解码器

  • ByteToMessageDecoder–将字节解码为消息

  • MessageToMessageDecoder–将一种消息类型解码为另一种

​ 因为解码器是负责将入站数据从一种格式转换到另一种格式的,所以 Netty 的解码器实现了 ChannelInboundHandler

ByteToMessageDecoder

​ 将字节解码为消息(或者另一个字节序列)是一项如此常见的任务,以至于 Netty 为它提供了一个抽象的基类:ByteToMessageDecoder。由于你不可能知道远程节点是否会一次性地发送一个完整的消息,所以这个类会对入站数据进行缓冲,直到它准备好处理。

decode(ChannelHandlerContext ctx,ByteBuf in,List out)

​ 这是你必须实现的唯一抽象方法。decode()方法被调用时将会传入一个包含了传入数据的 ByteBuf,以及一个用来添加解码消息的 List。对这个方法的调用将会重复进行,直到确定没有新的元素被添加到该 List,或者该 ByteBuf 中没有更多可读取的字节时为止。然后,如果该 List 不为空,那么它的内容将会被传递给 ChannelPipeline 中的下一个ChannelInboundHandler。

MessageToMessageDecoder

​ 在两个消息格式之间进行转换(例如,从 String->Integer)

decode(ChannelHandlerContext ctx,I msg,List out)

​ 对于每个需要被解码为另一种格式的入站消息来说,该方法都将会被调用。解码消息随
后会被传递给 ChannelPipeline 中的下一个 ChannelInboundHandler

TooLongFrameException

​ 由于 Netty 是一个异步框架,所以需要在字节可以解码之前在内存中缓冲它们。因此,不能让解码器缓冲大量的数据以至于耗尽可用的内存。为了解除这个常见的顾虑,Netty 提供了 TooLongFrameException 类,其将由解码器在帧超出指定的大小限制时抛出

​ 为了避免这种情况,你可以设置一个最大字节数的阈值,如果超出该阈值,则会导致抛出一个 TooLongFrameException(随后会被 ChannelHandler.exceptionCaught()方法捕获)。然后,如何处理该异常则完全取决于该解码器的用户。某些协议(如 HTTP)可能允许你返回一个特殊的响应。而在其他的情况下,唯一的选择可能就是关闭对应的连接。

编码器

  • MessageToByteEncoder–将消息编码为字节

  • MessageToMessageEncoder–将消息编码为消息

MessageToByteEncoder

encode(ChannelHandlerContext ctx,I msg,ByteBuf out)

​ encode()方法是你需要实现的唯一抽象方法。它被调用时将会传入要被该类编码为ByteBuf 的出站消息(类型为 I 的)。该 ByteBuf 随后将会被转发给 ChannelPipeline 中的下一个 ChannelOutboundHandler

MessageToMessageEncoder

encode(ChannelHandlerContext ctx,I msg,List out)

​ 这是你需要实现的唯一方法。每个通过 write()方法写入的消息都将会被传递给 encode()方法,以编码为一个或者多个出站消息。随后,这些出站消息将会被转发给 ChannelPipeline中的下一个 ChannelOutboundHandler

编解码器类

  • ByteToMessageCodec

  • MessageToMessageCodec

​ 我们一直将解码器和编码器作为单独的实体讨论,但是你有时将会发现在同一个类中管理入站和出站数据和消息的转换是很有用的。Netty 的抽象编解码器类正好用于这个目的,因为它们每个都将捆绑一个解码器/编码器对。这些类同时实现了 ChannelInboundHandler 和ChannelOutboundHandler 接口。

通过 SSL/TLS 保护 Netty 应用程序

​ SSL 和 TLS 这样的安全协议,它们层叠在其他协议之上,用以实现数据安全。我们在访问安全网站时遇到过这些协议,但是它们也可用于其他不是基于 HTTP 的应用程序,如安全SMTP(SMTPS)邮件服务器甚至是关系型数据库系统。
​ 为了支持 SSL/TLS,Java 提供了 javax.net.ssl 包,它的 SSLContext 和 SSLEngine 类使得实现解密和加密相当简单直接。Netty 通过一个名为 SslHandler 的 ChannelHandler 实现利用了这个 API,其中 SslHandler 在内部使用 SSLEngine 来完成实际的工作。

​ 在大多数情况下,SslHandler 将是 ChannelPipeline 中的第一个 ChannelHandler。

HTTP 系列

​ HTTP 是基于请求/响应模式的:客户端向服务器发送一个 HTTP 请求,然后服务器将会返回一个 HTTP 响应。Netty 提供了多种编码器和解码器以简化对这个协议的使用。

​ 一个HTTP 请求/响应可能由多个数据部分组成,FullHttpRequest 和FullHttpResponse 消息是特殊的子类型,分别代表了完整的请求和响应。所有类型的 HTTP 消息(FullHttpRequest、LastHttpContent 等等)都实现了 HttpObject 接口。

​ HttpRequestEncoder 将 HttpRequest、HttpContent 和 LastHttpContent 消息编码为字节
​ HttpResponseEncoder 将 HttpResponse、HttpContent 和 LastHttpContent 消息编码字节
​ HttpRequestDecoder 将字节解码为 HttpRequest、HttpContent 和 LastHttpContent 消息
​ HttpResponseDecoder 将字节解码为 HttpResponse、HttpContent 和 LastHttpContent消息
​ HttpClientCodec 和 HttpServerCodec 则将请求和响应做了一个组合。

聚合 HTTP 消息

​ 由于 HTTP 的请求和响应可能由许多部分组成,因此你需要聚合它们以形成完整的消息。为了消除这项繁琐的任务,Netty 提供了一个聚合器 HttpObjectAggregator,它可以将多个消息部分合并为 FullHttpRequest 或者 FullHttpResponse 消息。通过这样的方式,你将总是看到完整的消息内容。

HTTP 压缩

​ 当使用 HTTP 时,建议开启压缩功能以尽可能多地减小传输数据的大小。虽然压缩会带来一些 CPU 时钟周期上的开销,但是通常来说它都是一个好主意,特别是对于文本数据来说。Netty 为压缩和压缩提供了 ChannelHandler 实现,它们同时支持 gzip 和 deflate 编码。

使用 HTTPS

​ 启用 HTTPS 只需要将 SslHandler 添加到 ChannelPipeline 的 ChannelHandler 组合中。SSL 和 HTTP 的代码参见模块 netty-http
视频中实现步骤:

  • 首先实现 Http 服务器并浏览器访问;
  • 增加 SSL 控制;
  • 实现客户端并访问

空闲的连接和超时

​ 检测空闲连接以及超时对于及时释放资源来说是至关重要的。由于这是一项常见的任务,Netty 特地为它提供了几个 ChannelHandler 实现。

​ IdleStateHandler 当连接空闲时间太长时,将会触发一个 IdleStateEvent 事件。然后,你可以通过在你的 ChannelInboundHandler 中重写 userEventTriggered()方法来处理该IdleStateEvent 事件。

​ ReadTimeoutHandler 如果在指定的时间间隔内没有收到任何的入站数据,则抛出一个Read-TimeoutException 并关闭对应的 Channel。可以通过重写你的 ChannelHandler 中的exceptionCaught()方法来检测该 Read-TimeoutException。

​ WriteTimeoutHandler 如果在指定的时间间隔内没有任何出站数据写入,则抛出一个Write-TimeoutException 并关闭对应的 Channel 。可以通过重写你的 ChannelHandler 的exceptionCaught()方法检测该 WriteTimeout-Exception。

实战–http通信

Server

public class HttpServer {public static final int port = 6789; //设置服务端端口private static EventLoopGroup group = new NioEventLoopGroup();   // 通过nio方式来接收连接和处理连接private static ServerBootstrap b = new ServerBootstrap();private static final boolean SSL = false;/*是否开启SSL模式*//*** Netty创建全部都是实现自AbstractBootstrap。* 客户端的是Bootstrap,服务端的则是ServerBootstrap。**/public static void main(String[] args) throws Exception {final SslContext sslCtx;if(SSL){SelfSignedCertificate ssc = new SelfSignedCertificate();sslCtx = SslContextBuilder.forServer(ssc.certificate(),ssc.privateKey()).build();}else{sslCtx = null;}try {b.group(group);b.channel(NioServerSocketChannel.class);b.childHandler(new ServerHandlerInit(sslCtx)); //设置过滤器// 服务器绑定端口监听ChannelFuture f = b.bind(port).sync();System.out.println("服务端启动成功,端口是:"+port);// 监听服务器关闭监听f.channel().closeFuture().sync();} finally {group.shutdownGracefully(); //关闭EventLoopGroup,释放掉所有资源包括创建的线程}}
}
public class ServerHandlerInit extends ChannelInitializer<SocketChannel> {private final SslContext sslCtx;public ServerHandlerInit(SslContext sslCtx) {this.sslCtx = sslCtx;}@Overrideprotected void initChannel(SocketChannel ch) throws Exception {ChannelPipeline ph = ch.pipeline();if(sslCtx!=null){ph.addLast(sslCtx.newHandler(ch.alloc()));}/*把应答报文 编码*/ph.addLast("encoder",new HttpResponseEncoder());/*把请求报文 解码*/ph.addLast("decoder",new HttpRequestDecoder());/*聚合http为一个完整的报文*/ph.addLast("aggregator",new HttpObjectAggregator(10*1024*1024));/*把应答报文 压缩,非必要*/ph.addLast("compressor",new HttpContentCompressor());ph.addLast(new BusiHandler());}
}
public class RespConstant {private static final String[] NEWS = {"她那时候还太年轻,不知道所有命运赠送的礼物,早已在暗中标好了价格。——斯蒂芬·茨威格《断头皇后》","这是一个最好的时代,也是一个最坏的时代;这是一个智慧的年代,这是一个愚蠢的年代;\n" +"这是一个信任的时期,这是一个怀疑的时期;这是一个光明的季节,这是一个黑暗的季节;\n" +"这是希望之春,这是失望之冬;人们面前应有尽有,人们面前一无所有;\n" +"人们正踏上天堂之路,人们正走向地狱之门。 —— 狄更斯《双城记》",};private static final Random R = new Random();public static String getNews(){return NEWS[R.nextInt(NEWS.length)];}}
public class BusiHandler extends ChannelInboundHandlerAdapter {/*** 发送的返回值* @param ctx     返回* @param context 消息* @param status 状态*/private void send(ChannelHandlerContext ctx, String context,HttpResponseStatus status) {FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,status,Unpooled.copiedBuffer(context,CharsetUtil.UTF_8));response.headers().set(HttpHeaderNames.CONTENT_TYPE,"text/plain;charset=UTF-8");ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);}@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {String result="";FullHttpRequest httpRequest = (FullHttpRequest)msg;System.out.println(httpRequest.headers());try{//获取路径String path=httpRequest.uri();//获取bodyString body = httpRequest.content().toString(CharsetUtil.UTF_8);//获取请求方法HttpMethod method=httpRequest.method();System.out.println("接收到:"+method+" 请求");//如果不是这个路径,就直接返回错误if(!"/test".equalsIgnoreCase(path)){result="非法请求!"+path;send(ctx,result,HttpResponseStatus.BAD_REQUEST);return;}//如果是GET请求if(HttpMethod.GET.equals(method)){//接受到的消息,做业务逻辑处理...System.out.println("body:"+body);result="GET请求,应答:"+RespConstant.getNews();send(ctx,result,HttpResponseStatus.OK);return;}//如果是其他类型请求,如postif(HttpMethod.POST.equals(method)){//接受到的消息,做业务逻辑处理...//....return;}}catch(Exception e){System.out.println("处理请求失败!");e.printStackTrace();}finally{//释放请求httpRequest.release();}}/** 建立连接时,返回消息*/@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {System.out.println("连接的客户端地址:" + ctx.channel().remoteAddress());}
}

Client

public class HttpClient {public static final String HOST  = "127.0.0.1";private static final boolean SSL = false;public void connect(String host, int port) throws Exception {EventLoopGroup workerGroup = new NioEventLoopGroup();try {Bootstrap b = new Bootstrap();b.group(workerGroup);b.channel(NioSocketChannel.class);b.option(ChannelOption.SO_KEEPALIVE, true);b.handler(new ChannelInitializer<SocketChannel>() {@Overridepublic void initChannel(SocketChannel ch) throws Exception {ch.pipeline().addLast(new HttpClientCodec());/*聚合http为一个完整的报文*/ch.pipeline().addLast("aggregator",new HttpObjectAggregator(10*1024*1024));/*解压缩*/ch.pipeline().addLast("decompressor",new HttpContentDecompressor());ch.pipeline().addLast(new HttpClientInboundHandler());}});// Start the client.ChannelFuture f = b.connect(host, port).sync();f.channel().closeFuture().sync();} finally {workerGroup.shutdownGracefully();}}public static void main(String[] args) throws Exception {HttpClient client = new HttpClient();client.connect("127.0.0.1", HttpServer.port);}
}
public class HttpClientInboundHandler extends ChannelInboundHandlerAdapter {public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {FullHttpResponse httpResponse = (FullHttpResponse)msg;System.out.println(httpResponse.status());System.out.println(httpResponse.headers());ByteBuf buf = httpResponse.content();System.out.println(buf.toString(CharsetUtil.UTF_8));httpResponse.release();}@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {URI uri = new URI("/test");String msg = "Hello";DefaultFullHttpRequest request =new DefaultFullHttpRequest(HttpVersion.HTTP_1_1,HttpMethod.GET,uri.toASCIIString(),Unpooled.wrappedBuffer(msg.getBytes("UTF-8")));// 构建http请求request.headers().set(HttpHeaderNames.HOST, HttpClient.HOST);request.headers().set(HttpHeaderNames.CONNECTION,HttpHeaderValues.KEEP_ALIVE);request.headers().set(HttpHeaderNames.CONTENT_LENGTH,request.content().readableBytes());// 发送http请求ctx.writeAndFlush(request);}
}
equest.headers().set(HttpHeaderNames.HOST, HttpClient.HOST);request.headers().set(HttpHeaderNames.CONNECTION,HttpHeaderValues.KEEP_ALIVE);request.headers().set(HttpHeaderNames.CONTENT_LENGTH,request.content().readableBytes());// 发送http请求ctx.writeAndFlush(request);}
}

netty(4)--编解码器相关推荐

  1. Netty的编解码器

    Netty常用编解码器 Netty 常用编码器 MessageToByteEncoder 对象编码成字节流 MessageToMessageEncoder 一种消息类型编码成另外一种消息类型 Nett ...

  2. netty的编解码器理解(转)

    一.简介 在网络应用中需要实现某种编解码器,将原始字节数据与自定义的消息对象进行互相转换.网络中都是以字节码的数据形式来传输数据的,服务器编码数据后发送到客户端,客户端需要对数据进行解码. 编解码器由 ...

  3. 你了解Netty的编解码器吗?史上最通俗易懂的Netty解码器应用案例带你解开Netty解码器的神秘面纱

    Netty解码器也是非常重要的一个模块, 服务端接收到客户端发送过来的消息, 准确说是字节数组, Netty底层已经将它们读取成ByteBuf了, 但是这些ByteBuf是没有任何含义的,需要我们根据 ...

  4. Netty 编解码器详解

    什么是编解码器? 在网络中都是以字节码的数据形式来传输数据的,如何将其和目标应用程序的自定义消息对象数据格式进行相互转换.这种转换逻辑就需要编解码器处理,编解码器由编码器和解码器组成,它们每种都可以将 ...

  5. Netty 源码分析系列(十五)自定义解码器、编码器、编解码器

    前言 我们今天继续来分析 Netty 的编解码器,这次我们要自己动手实现自定义的编码器.解码器和编解码器. 自定义基于换行的解码器 LineBasedFrameDecoder 类 LineBasedF ...

  6. 【Netty】自定义解码器、编码器、编解码器(十五)

    文章目录 前言 一.自定义基于换行的解码器 1.1 LineBasedFrameDecoder 类 1.2 定义解码器 1.3 定义 ChannelHandler 1.4 定义 ChannelInit ...

  7. Netty with protobuf(二)

    2019独角兽企业重金招聘Python工程师标准>>> http://my.oschina.net/xinxingegeya/blog/295031 上一篇了解了protobuf,现 ...

  8. Netty 学习笔记(已完结)

    Netty 0代码示例 A.经典IO多线程 // 获取到的inputStream是SocketInputStream,这个类不是公开的,继承了FileInputStream, InputStream ...

  9. 07 | 接头暗语:如何利用 Netty 实现自定义协议通信?

    既然是网络编程,自然离不开通信协议,应用层之间通信需要实现各种各样的网络协议.在项目开发的过程中,我们就需要去构建满足自己业务场景的应用层协议.在上节课中我们介绍了如何使用网络协议解决 TCP 拆包/ ...

最新文章

  1. MySQL中对varchar类型排序问题的解决
  2. 安装需要的第三方库时,命令行输入pip提示不是内部或外部命令
  3. 文本框自动提示_Excel办公小技巧,使用艺术字与文本框,就是那么的简单
  4. 【译】Asp.net MVC 利用自定义RouteHandler来防止图片盗链 (转)
  5. 人脸识别技术大总结——Face Detection Alignment
  6. Java学习手记2——多线程
  7. 翻译 | 2015年的最佳Material Design集锦 【上篇】
  8. spring boot 整合redis实现方法缓存
  9. 内网突破SSL嗅探的探究
  10. linux编译安装的报错,linux编译安装时常见错误解决办法
  11. 如何确定h.264的码率
  12. linux shell rsync,linux – 如何在我的下面的shell脚本中使用rsync而不是scp来复制文件?...
  13. dll找不到dll electron_electron之集成node-ffi-napi
  14. Caffe 数据结构
  15. 51单片机c语言音乐盒设计,基于51单片机的音乐盒课程设计开题报告精品
  16. 网站跳出率的相关要点介绍 1
  17. unity 射线检测 碰撞点不准确的原因分析
  18. pg预热插件pg_prewarm
  19. 程序员最重要的核心竞争力是什么?
  20. CGB2107-Day07-实现前后端调用

热门文章

  1. fcpx插件:各种酷炫视频转场特效,效果震撼
  2. Java(匿名内部类)
  3. 客服打字速度慢怎么提高效率
  4. 达梦数据库集群部署(已实现)
  5. 新媒体运营怎么做?有什么技巧?
  6. 电脑耳机为什么会有很大噪声
  7. 视频网站节约流量的小妙招
  8. notepad++ 格式化html代码
  9. 对c语言的认知报告怎么写,对C语言指针的认识的认知实习报告.doc
  10. JavaEE——Servlet生命周期