Netty之Http

2020-04-23  本文已影响0人  Cool_Pomelo

Netty之Http

Http协议

HTTP是一个属于应用层的面向对象的协议,由于其使用简捷和快速的方式,非常适用于分布式超媒体信息系统。

HTTP协议特点

标示 ASCII 描述 字符
CR 13 Carriage return (回车) \n
LF 10 Line feed character(换行) \r
SP 32 Horizontal space(空格)
COLON 58 COLON(冒号) :

http协议主要使用CRLF进行分割。

请求包

图1.png

三部分组成:

响应包

图2.png

三部分组成:

chunked

HTTP协议通常使用Content-Length来标识body的长度,在服务器端,需要先申请对应长度的buffer,然后再赋值。如果需要一边生产数据一边发送数据,就需要使用"Transfer-Encoding: chunked" 来代替Content-Length,也就是对数据进行分块传输。

Content-Length

chunked

truncked协议

图3.png

主要三部分:

优点

缺点

http针对拆包粘包解决方案

Netty对Http的处理

对请求request的抽象:

图4.png

request处理流程

只需要在netty 的pipeLine 中配置 HttpRequestDecoder 和HttpObjectAggregator。

图5.png

HttpObjectAggregator

public class HttpObjectAggregator
extends MessageAggregator<HttpObject,HttpMessage,HttpContent,FullHttpMessage>

一个ChannelHandler,它将HttpMessage及其后续的HttpContents聚合为一个没有后续HttpContents的单个FullHttpRequest或FullHttpResponse(取决于它是否用于处理请求或响应)。 当您不想处理传输编码为“块状”的HTTP消息时,此功能很有用。 如果用于处理响应,则将此处理程序插入到ChannelPipeline中的HttpResponseDecoder之后;如果用于处理请求,则将其插入到ChannelPipeline中的HttpRequestDecoder和HttpResponseEncoder之后。

  ChannelPipeline p = ...;
  ...
  p.addLast("decoder", new HttpRequestDecoder());
  p.addLast("encoder", new HttpResponseEncoder());
  p.addLast("aggregator", new HttpObjectAggregator(1048576));
  ...
  p.addLast("handler", new HttpRequestHandler());
  

为了方便起见,请考虑将HttpServerCodec放在HttpObjectAggregator之前,因为它既充当HttpRequestDecoder,又充当HttpResponseEncoder。

请注意,HttpObjectAggregator可能最终会发送HttpResponse:

Response Status Condition When Sent
100 Continue A '100-continue' expectation is received and the 'content-length' doesn't exceed maxContentLength
417 Expectation Failed A '100-continue' expectation is received and the 'content-length' exceeds maxContentLength
413 Request Entity Too Large Either the 'content-length' or the bytes received so far exceed maxContentLength

构造函数:


//maxContentLength-聚合内容的最大长度(以字节为单位)。 如果聚合内容的长度超过此值,则将调用handleOversizedMessage(ChannelHandlerContext,HttpMessage)。
public HttpObjectAggregator(int maxContentLength)



QueryStringDecoder


public class QueryStringDecoder
extends java.lang.Object

将HTTP查询字符串拆分为路径字符串和键值参数对。 该解码器仅供一次使用。 为每个URI创建一个新实例:

 QueryStringDecoder decoder = new QueryStringDecoder("/hello?recipient=world&x=1;y=2");
 assert decoder.path().equals("/hello");
 assert decoder.parameters().get("recipient").get(0).equals("world");
 assert decoder.parameters().get("x").get(0).equals("1");
 assert decoder.parameters().get("y").get(0).equals("2");

该解码器还可以解码HTTP POST请求的内容,其内容类型为application / x-www-form-urlencoded:

 QueryStringDecoder decoder = new QueryStringDecoder("recipient=world&x=1;y=2", false);


例子

客户端发送http请求,服务端响应


public class HttpServer {


    public void bind(int port) {

        EventLoopGroup boss = new NioEventLoopGroup();
        EventLoopGroup worker = new NioEventLoopGroup();

        try {

            ServerBootstrap sb = new ServerBootstrap();

            sb.group(boss, worker)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {

                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {

                            ChannelPipeline pipeline = ch.pipeline();

                            pipeline.addLast(new HttpServerCodec());

                            pipeline.addLast(new HttpObjectAggregator(61024));

                            pipeline.addLast(new HttpServerHandler());


                        }
                    });


            ChannelFuture future = sb.bind(port).sync();
            System.out.println("Server...............................");
            future.channel().closeFuture().sync();

        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {

            boss.shutdownGracefully();
            worker.shutdownGracefully();

        }


    }

    public static void main(String[] args) {

        new HttpServer().bind(9921);
        
    }

}




public class HttpServerHandler extends SimpleChannelInboundHandler<FullHttpRequest>{

    private static final Logger logger = LoggerFactory.getLogger(HttpServerHandler.class);


    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();

    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest msg) throws Exception {

        // 获取请求头
        HttpHeaders httpHeaders = msg.headers();
//        System.out.println(httpHeaders.toString());
        // 查看请求头的详细信息
        logger.info(httpHeaders.toString());
        logger.info("*****************************");
        // 打印uri
        logger.info(msg.uri());

        // 获取Content-Type信息
        String strContentType = httpHeaders.get("Content-Type").trim();
        Map<String, Object> mapReturnData = new HashMap<String, Object>();
        System.out.println("ContentType:" + strContentType);

        // 处理get请求
        QueryStringDecoder decoder = new QueryStringDecoder(
                msg.getUri());
        if (msg.getMethod() == HttpMethod.GET) {
            Map<String, List<String>> get_parame = decoder.parameters();

            System.out.println("GET方式:" + get_parame.toString());

            response_message(ctx);
        } else if (msg.getMethod() == HttpMethod.POST) {

            if (strContentType.contains("application/json")) {
                    ByteBuf content = msg.content();
                    Map<String, List<String>> post_parame = decoder.parameters();
                    System.out.println("POST方式:" + post_parame.toString());
                    if (content.capacity() != 0 ) {
                        byte[] resquestContent = new byte[content.readableBytes()];
                        content.readBytes(resquestContent);
                        String strContent = new String(resquestContent, "UTF-8");
                        JSONObject jsonParamRoot = JSONObject.parseObject(strContent);
                        for (String key : jsonParamRoot.keySet()) {
                            mapReturnData.put(key, jsonParamRoot.get(key));
                        }
                    }
                }

//                logger.info("POST: " + mapReturnData.toString());
        }


    }


    // 服务端回应数据
    private void response_message(ChannelHandlerContext ctx) {

        // 构造返回数据
        JSONObject jsonRootObj = new JSONObject();
        JSONObject jsonUserInfo = new JSONObject();
        jsonUserInfo.put("id", 1);
        jsonUserInfo.put("name", "张三");
        jsonUserInfo.put("password", "123");
        jsonRootObj.put("userInfo", jsonUserInfo);
        FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK);
        response.headers().set(CONTENT_TYPE, "application/json; charset=UTF-8");
        StringBuilder bufRespose = new StringBuilder();
        bufRespose.append(jsonRootObj.toJSONString());
        ByteBuf buffer = Unpooled.copiedBuffer(bufRespose, CharsetUtil.UTF_8);
        response.content().writeBytes(buffer);
        buffer.release();
        ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);


    }
}

PostMan测试:

图6.png

参考文章

https://blog.csdn.net/u014209205/article/details/96426595

https://www.jhonrain.org/2019/10/15/Netty%E7%B3%BB%E5%88%97-%E4%B8%AD%E7%BA%A7%E7%AF%87-008-Netty%E7%BB%93%E5%90%88Http%E5%8D%8F%E8%AE%AE%E5%BC%80%E5%8F%91-%E4%B8%80-Http%E5%8D%8F%E8%AE%AE%E8%AE%A4%E8%AF%86/

上一篇 下一篇

猜你喜欢

热点阅读