• Netty4.1 Http开发入门(一)服务端


    开发了一个简单的Http Server,使用的是Netty 4.1.46.Final版本。

    服务器类
    import io.netty.bootstrap.ServerBootstrap;
    import io.netty.channel.ChannelFuture;
    import io.netty.channel.ChannelInitializer;
    import io.netty.channel.ChannelOption;
    import io.netty.channel.EventLoopGroup;
    import io.netty.channel.nio.NioEventLoopGroup;
    import io.netty.channel.socket.SocketChannel;
    import io.netty.channel.socket.nio.NioServerSocketChannel;
    import io.netty.handler.codec.http.HttpObjectAggregator;
    import io.netty.handler.codec.http.HttpServerCodec;
    import lombok.extern.slf4j.Slf4j;
    
    /**
     * 一个使用netty实现的简单的http服务器
     * HttpServerCodec是能同时处理入站和出站的编解码器
     * */
    @Slf4j
    public class SimpleHttpServer {
    
    	private static final int _1M = 1024 * 1024;
    
    	public static void main(String[] args) {
    		log.info("启动SimpleHttpServer服务器...");
    		EventLoopGroup boss = new NioEventLoopGroup();
    		EventLoopGroup worker = new NioEventLoopGroup(2);
    		try {
    			ServerBootstrap bootstrap = new ServerBootstrap();
    			bootstrap.option(ChannelOption.SO_BACKLOG, 1024).group(boss, worker).channel(NioServerSocketChannel.class)
    					.childHandler(new ChannelInitializer<SocketChannel>() {
    
    						@Override
    						protected void initChannel(SocketChannel ch) {
    
    							ch.pipeline().addLast("codec", new HttpServerCodec());
    							ch.pipeline().addLast("aggregate", new HttpObjectAggregator(_1M));
    							ch.pipeline().addLast("msg", new MyHttpMsgHandler());
    						}
    
    					});
    			ChannelFuture future = bootstrap.bind(8080).sync();
    			future.channel().closeFuture().sync();
    		} catch (Exception e) {
    			e.printStackTrace();
    			log.error("http服务器错误:" + e.getMessage(), e);
    		} finally {
    			log.info("关闭http服务器");
    			try {
    				boss.shutdownGracefully().sync();
    				worker.shutdownGracefully().sync();
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    	}
    }
    
    自定义Handler,用来处理Http Request并向客户端写Response
    package com.wangan.netty_httpserver;
    
    import io.netty.buffer.ByteBuf;
    import io.netty.buffer.Unpooled;
    import io.netty.channel.ChannelFuture;
    import io.netty.channel.ChannelFutureListener;
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.channel.SimpleChannelInboundHandler;
    import io.netty.handler.codec.http.DefaultFullHttpResponse;
    import io.netty.handler.codec.http.FullHttpRequest;
    import io.netty.handler.codec.http.HttpObject;
    import io.netty.handler.codec.http.HttpResponse;
    import io.netty.handler.codec.http.HttpResponseStatus;
    import io.netty.handler.codec.http.HttpVersion;
    import io.netty.handler.codec.http.LastHttpContent;
    import io.netty.util.CharsetUtil;
    import lombok.extern.slf4j.Slf4j;
    
    /**
     * 用来处理http消息,包括请求与响应
     * 入站byte由HttpServerCodec转为请求消息,而出站响应同样由HttpServerCodec转为出站byte
     * */
    @Slf4j
    public class MyHttpMsgHandler extends SimpleChannelInboundHandler<HttpObject> {
    
    	@Override
    	protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
    		
    		if (msg instanceof FullHttpRequest) {
    			log.info("收到HTTP请求,聚合为FullHttpRequest");
    
    			FullHttpRequest request = (FullHttpRequest) msg;
    			log.info("HTTP Headers:{}", request.headers().toString());
    			String requestId = request.headers().get("RequestID");
    			log.info("Http RequestID:{}", requestId);
    			String requestContent = request.content().toString(CharsetUtil.UTF_8);
    			log.info("HTTP Content:{}", requestContent);
    
    			ByteBuf responseContent = Unpooled.copiedBuffer("已收到,请求内容为" + requestContent, CharsetUtil.UTF_8);
    			HttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK,
    					responseContent);
    			if (requestId != null)
    				response.headers().add("RequestID", requestId);
    			ctx.write(response);
    			ChannelFuture future = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
    			future.addListener(ChannelFutureListener.CLOSE);//写完Response,关闭了Channel
    			log.info("写回response");
    		}
    
    	}
    
    }
    
    关于HttpObjectAggregator

    Http协议底层传输是基于TCP对数据进行字节传输的,传输的时候对端需要知道何时是一个完整的请求结束,一般会有如下几个方式:

    • 在Header里边的Content-Length:xxx属性里边声明传输请求的大小
    • 传输的数据比较大、或者没法预先知道传输数据的确切大小的时候,指定Header属性Transfer-Encoding: chunked采用所谓分块传输。每一个chunk第一行声明本次传输的数据块的长度、换行后面跟数据块的内容。一个完整请求最后被分为若干个chunk发送,最后一个chunk长度是0,标识请求结尾。
    • 如果没法预先获知并指定长度、也不支持chunked传输,那只能以短链接的方式进行传输,最终以连接结束来标识本次请求结束

    当使用的是分块传输的话,如果不使用HttpObjectAggregator的话,我们需要在channelRead0处理多个HttpContent,以及最后的LastHttpContent,每个HttpContent都是变长的数据块,而通过在pipeline上的HttpServerCodec后面添加HttpObjectAggregator,可以将多个块聚合成一个完整的FullHttpRequest,方便以上层Http应用层协议的方式进行统一处理,而不用考虑底层数据传输的细节了。

    参考

    https://www.cnblogs.com/nxlhero/p/11670942.html
    https://www.cnblogs.com/xuehaoyue/p/6639029.html

  • 相关阅读:
    NOI2015 小园丁和老司机
    退役记
    留言板
    $mathfrak {reputation}$
    计算几何基础
    HNOI2018简要题解
    JXOI2018简要题解
    BJOI2018简要题解
    HAOI2018 简要题解
    CQOI2018简要题解
  • 原文地址:https://www.cnblogs.com/lyhero11/p/15676944.html
Copyright © 2020-2023  润新知