Netty 实现HTTP demo

1. 实现过程分析

  • 简历连接读取消息流
  • 解码Request
  • 业务处理
  • 编码Response
  • 返回消息关闭连接

2.HttpRequest 在netty 当中的表示结构

Netty 实现HTTP demo_第1张图片

3.HttpResponse在netty 当中的结构:

Netty 实现HTTP demo_第2张图片

4.代码实现

public class HttpServer {

    private final int port;

    public HttpServer(int port) {
        this.port = port;
    }

    public void run() {
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup).option(ChannelOption.SO_BACKLOG, 1024)
                    .channel(NioServerSocketChannel.class).childOption(ChannelOption.SO_KEEPALIVE, true)
                    .childOption(ChannelOption.TCP_NODELAY, true).childHandler(new ChannelInitializer() {
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast("codec", new HttpServerCodec()) // 或者使用HttpRequestDecoder
                                    .addLast("aggregator", new HttpObjectAggregator(1024 * 1024)) // 在处理
                                    .addLast("handler", new HttpServerhandler()); // 业务handler
                        };
                    });
            ChannelFuture future = bootstrap.bind(port).sync();

            System.out.println("Netty-http server listening on port " + port);
            future.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

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

public class HttpServerhandler extends ChannelHandlerAdapter {
    private HttpHeaders headers;
    private HttpRequest request;
    private FullHttpRequest fullRequest;
    private FullHttpResponse response;

    private HttpPostRequestDecoder decoder;

    private static final HttpDataFactory factory = new DefaultHttpDataFactory(DefaultHttpDataFactory.MAXSIZE);

    private static final String FAVICON_ICO = "/favicon.ico";
    private static final String SUCCESS = "success";
    private static final String ERROR = "error";
    private static final String CONNECTION_KEEP_ALIVE = "keep-alive";
    private static final String CONNECTION_CLOSE = "close";
    private static final Charset cs = Charset.forName("utf-8");

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        if (msg instanceof HttpRequest) {
            try {
                request = (HttpRequest) msg;
                headers = request.headers();

                String uri = request.uri();
                System.out.println("http uri: " + uri);
                // 去除浏览器"/favicon.ico"的干扰
                if (uri.equals(FAVICON_ICO)) {
                    return;
                }

                HttpMethod method = request.method();
                if (method.equals(HttpMethod.GET)) {
                    QueryStringDecoder queryDecoder = new QueryStringDecoder(uri, cs);
                    Map> uriAttributes = queryDecoder.parameters();
                    // 此处仅打印请求参数(你可以根据业务需求自定义处理)
                    for (Map.Entry> attr : uriAttributes.entrySet()) {
                        for (String attrVal : attr.getValue()) {
                            System.out.println(attr.getKey() + "=" + attrVal);
                        }
                    }
                } else if (method.equals(HttpMethod.POST)) {

                    // POST请求,由于你需要从消息体中获取数据,因此有必要把msg转换成FullHttpRequest
                    fullRequest = (FullHttpRequest) msg;

                    // 根据不同的 Content_Type 处理 body 数据
                    dealWithContentType();
                } else {
                    // 其他类型在此不做处理,需要的话可自己扩展
                }

                writeResponse(ctx.channel(), HttpResponseStatus.OK, SUCCESS, false);

            } catch (Exception e) {
                writeResponse(ctx.channel(), HttpResponseStatus.INTERNAL_SERVER_ERROR, ERROR, true);

            } finally {
                ReferenceCountUtil.release(msg);
            }

        } else {
            // discard request...
            ReferenceCountUtil.release(msg);
        }
    }

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

    /**
     * 简单处理常用几种 Content-Type 的 POST 内容(可自行扩展)
     * 
     * @param headers
     * @param content
     * @throws Exception
     */
    private void dealWithContentType() throws Exception {
        String contentType = getContentType();
        if (contentType.equals("application/json")) { // 可以使用HttpJsonDecoder
            String jsonStr = fullRequest.content().toString(cs);
            JSONObject obj = JSON.parseObject(jsonStr);
            for (Entry item : obj.entrySet()) {
                System.out.println(item.getKey() + "=" + item.getValue().toString());
            }

        } else if (contentType.equals("application/x-www-form-urlencoded")) {
            // 方式一:使用 QueryStringDecoder
            // String jsonStr =
            // fullRequest.content().toString(Charsets.toCharset(CharEncoding.UTF_8));
            // QueryStringDecoder queryDecoder = new QueryStringDecoder(jsonStr,
            // false);
            // Map> uriAttributes =
            // queryDecoder.parameters();
            // for (Map.Entry> attr :
            // uriAttributes.entrySet()) {
            // for (String attrVal : attr.getValue()) {
            // System.out.println(attr.getKey() + "=" + attrVal);
            // }
            // }
            // 方式二:使用 HttpPostRequestDecoder
            initPostRequestDecoder();
            List datas = decoder.getBodyHttpDatas();
            for (InterfaceHttpData data : datas) {
                if (data.getHttpDataType() == HttpDataType.Attribute) {
                    Attribute attribute = (Attribute) data;
                    System.out.println(attribute.getName() + "=" + attribute.getValue());
                }
            }

        } else if (contentType.equals("multipart/form-data")) { // 用于文件上传
            readHttpDataAllReceive();

        } else {
            // do nothing...
        }
    }

    private void readHttpDataAllReceive() throws Exception {
        initPostRequestDecoder();
        try {
            List datas = decoder.getBodyHttpDatas();
            for (InterfaceHttpData data : datas) {
                writeHttpData(data);
            }
        } catch (Exception e) {
            // 此处仅简单抛出异常至上一层捕获处理,可自定义处理
            throw new Exception(e);
        }
    }

    private void writeHttpData(InterfaceHttpData data) throws Exception {
        // 后续会加上块传输(HttpChunk),目前仅简单处理
        if (data.getHttpDataType() == HttpDataType.FileUpload) {
            FileUpload fileUpload = (FileUpload) data;
            String fileName = fileUpload.getFilename();
            if (fileUpload.isCompleted()) {
                // 保存到磁盘
                StringBuffer fileNameBuf = new StringBuffer();
                fileNameBuf.append(DiskFileUpload.baseDirectory).append(fileName);
                fileUpload.renameTo(new File(fileNameBuf.toString()));
            }
        }
    }

    private void writeResponse(Channel channel, HttpResponseStatus status, String msg, boolean forceClose) {
        ByteBuf byteBuf = Unpooled.wrappedBuffer(msg.getBytes());
        response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status, byteBuf);
        boolean close = isClose();
        if (!close && !forceClose) {
            response.headers().add(org.apache.http.HttpHeaders.CONTENT_LENGTH, String.valueOf(byteBuf.readableBytes()));
        }
        ChannelFuture future = channel.write(response);
        if (close || forceClose) {
            future.addListener(ChannelFutureListener.CLOSE);
        }
    }

    private String getContentType() {
        String typeStr = headers.get("Content-Type").toString();
        String[] list = typeStr.split(";");
        return list[0];
    }

    private void initPostRequestDecoder() {
        if (decoder != null) {
            decoder.cleanFiles();
            decoder = null;
        }

        decoder = new HttpPostRequestDecoder(factory, request, cs);
    }

    private boolean isClose() {
        if (request.headers().contains(org.apache.http.HttpHeaders.CONNECTION, CONNECTION_CLOSE, true) || (request
                .protocolVersion().equals(HttpVersion.HTTP_1_0)
                && !request.headers().contains(org.apache.http.HttpHeaders.CONNECTION, CONNECTION_KEEP_ALIVE, true)))
            return true;
        return false;
    }

}

你可能感兴趣的:(Netty)