热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

NettyHttp协议解析实现

NettyHttp协议解析实现,Go语言社区,Golang程序员人脉社

Http协议交互过程

协议交互本质是指协议两端(客户端、服务端)如何传输数据?如何交换数据?

传输数据一般基于TCP/IP 实现,体现到开发语言上就是我们所熟悉的Socket 编程。
交换数据本质是指,两端(客户端、服务端)能各自识别对方所发送的数据。那么这就需要制定一套报文编码格式,双方以该格式编码数据发送给对方。

Http 对应的Request 与Response报文格式如下图:

Request 报文:

Response 报文:

 

http报文解析方案:

1. 请求行的边界是CRLF(回车),如果读取到CRLF(回车),则意味着请求行的信息已经读取完成
2. Header的边界是CRLF,如果连续读取两个CRLF,则意味着header的信息读取完成
3. body的长度是有Content-Length 来进行确定


Netty关于http 的解决方案:

很多http server的实现都是基于servlet标准,但是netty对http实现并没有基于servlet。所以在使用上比Servlet复杂很多。比如在servlet 中直接可以通过 HttpServletRequest 获取 请求方法、请求头、请求参数。而netty 确需要通过如下对象自行解析获取

HttpMethod:主要是对method的封装,包含method序列化的操作
HttpVersion: 对version的封装,netty包含1.0和1.1的版本
QueryStringDecoder: 主要是对urI进行解析,解析path和url上面的参数。
HttpPostRequestDecoder:对post 中body 内容进行解析获取 form 参数。
HttpHeaders:包含对header的内容进行封装及操作
HttpContent:是对body进行封装,本质上就是一个ByteBuf。如果ByteBuf的长度是固定的,则请求的body过大,可能包含多个HttpContent,其中最后一个为LastHttpContent(空的HttpContent),用来说明body的结束。
HttpRequest:主要包含对Request Line和Header的组合
FullHttpRequest: 主要包含对HttpRequest和httpContent的组合

Netty Http的请求处理流程:

从图中可以看出做为服务端的Netty 就是在做 编码和解码操作。其分别通过以下两个ChannelHandler对象实现:

HttpRequestDecoder: 用于从byteBuf 获取数据并解析封装成HttpRequest对象
HttpResponseEncoder: 用于将业务返回数据编码成 Response报文并发送到ByteBuf

将以上两个对象添加进 Netty 的 pipeline 即可实现最简单的http 服务

示例程序:

package com.lic.demo;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.*;
public class HttpServer {
    public void openServer(int port){
        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap.channel(NioServerSocketChannel.class);  //接收客户端请求Channel
        EventLoopGroup boss = new NioEventLoopGroup(1);  //boss线程组
        EventLoopGroup work = new NioEventLoopGroup(2);  //work线程组
        bootstrap.group(boss,work);
        bootstrap.childHandler(new ChannelInitializer() {
            @Override
            protected void initChannel(NioSocketChannel ch) throws Exception {
                ch.pipeline().addLast("http-decoder",new HttpRequestDecoder()); //request解码
                //ch.pipeline().addLast("http-aggregator", new HttpObjectAggregator(65536)); //将请求头与请求体聚合(FullHttpRequest)
                ch.pipeline().addLast("http-encode",new HttpResponseEncoder());  //resposen编码
                ch.pipeline().addLast("http-service",new HttpServerHandler());  //业务逻辑处理
            }
        });

        try {
            ChannelFuture cf = bootstrap.bind(port).sync();
            System.out.println("服务已启动, 端口为:"+port);
            cf.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            //出现异常,释放线程池资源
            boss.shutdownGracefully();
            work.shutdownGracefully();
        }
    }

    public class HttpServerHandler extends SimpleChannelInboundHandler{
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
            FullHttpResponse respOnse= new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
            //写入请求头
            response.headers().set(HttpHeaderNames.CONTENT_TYPE,"text/html;charset=UTF-8");
            response.content().writeBytes("Hello Netty".getBytes());
            ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
        }
    }

    public static void main(String[] args) {
        new HttpServer().openServer(8080);
    }
}

Http完整流程介绍:

Decoder流程:

Encode流程:


WebScoket协议实现

webSocket 协议简介:

webSocket 是html5 开始提供的一种浏览器与服务器间进行全双工二进制通信协议,其基于TCP双向全双工作进行消息传递,同一时刻即可以发又可以接收消息,相比Http的半双工协议性能有很大的提升

webSocket特点如下:
  1.  单一TCP长连接,采用全双工通信模式
  2.  对代理、防火墙透明
  3.  无头部信息、消息更精简
  4.  通过ping/pong 来保活
  5.  服务器可以主动推送消息给客户端,不在需要客户轮询

WebSocket 协议报文格式:

我们知道,任何应用协议都有其特有的报文格式,比如Http协议通过 空格 换行组成其报文。如http 协议不同在于WebSocket属于二进制协议,通过规范进二进位来组成其报文。具体组成如下图:

报文说明:

FIN

标识是否为此消息的最后一个数据包,占 1 bit

RSV1, RSV2, RSV3

用于扩展协议,一般为0,各占1bit

Opcode

数据包类型(frame type),占4bits
0x0:标识一个中间数据包
0x1:标识一个text类型数据包
0x2:标识一个binary类型数据包
0x3-7:保留
0x8:标识一个断开连接类型数据包
0x9:标识一个ping类型数据包
0xA:表示一个pong类型数据包
0xB-F:保留

MASK

占1bits; 用于标识PayloadData是否经过掩码处理。如果是1,Masking-key域的数据即是掩码密钥,用于解码PayloadData。客户端发出的数据帧需要进行掩码处理,所以此位是1

Payload length

Payload data的长度,占7bits,7+16bits,7+64bits:
如果其值在0-125,则是payload的真实长度
如果值是126,则后面2个字节形成的16bits无符号整型数的值是payload的真实长度。注意,网络字节序,需要转换
如果值是127,则后面8个字节形成的64bits无符号整型数的值是payload的真实长度。注意,网络字节序,需要转换

Payload data

应用层数据

Http 连接(左)与webSocket连接(右)建立示意图:


推荐阅读
author-avatar
wangnan00
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有