九.Netty入门到超神系列-Netty开发Http服务器

Posted 墨家巨子@俏如来

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了九.Netty入门到超神系列-Netty开发Http服务器相关的知识,希望对你有一定的参考价值。

前言

这章我们使用Netty来写一个Http服务器类似于Tomcat ,当然Netty和Tomcat是有很多的异同的,比如通信协议,Tomcat是一个基于Http协议的Web容器,而Netty能够通过codec自己来编码/解码字节流 ,因此Netty可以通过编程自定义各种协议,我们今天的目的还是对Netty练练手。

基于Netty的Http服务器

我这里要实现的案例就是客户端(浏览器或者Postmain)请求服务器,发送GET或者Post请求,服务器拿到请求中的参数,然后返回一个消息给客户端.

对于Netty服务端和之前的入门程序差不多,大致流程都是一样的,只不过这次需要给 SocketChannel中的pipeline添加编码和解码器。

  • HttpRequestDecoder 请求解码器
  • HttpResponseEncoder 响应转码器

对于Handler而言,我们需要继承 SimpleChannelInboundHandler<FullHttpRequest>, 泛型FullHttpRequest 就是对请求对象的封装,通过它我们可以获取到请求相关的内容。

服务器代码

public class NettyHttpServer {

    public static void main(String[] args) throws InterruptedException {
        //事件循环组
        NioEventLoopGroup bossGroup = new NioEventLoopGroup();
        NioEventLoopGroup workGroup = new NioEventLoopGroup();

        //启动对象
        ServerBootstrap bootstrap = new ServerBootstrap() ;

        try {
            //配置netty
            bootstrap.group(bossGroup, workGroup)
                    .channel(NioserverSocketChannel.class)
                    .childHandler(new ChannelInitializer< SocketChannel >(){
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            // 请求解码器
                            ch.pipeline().addLast("http-decoder", new HttpRequestDecoder());
                            // 将HTTP消息的多个部分合成一条完整的HTTP消息
                            ch.pipeline().addLast("http-aggregator", new HttpObjectAggregator(65535));
                            // 响应转码器
                            ch.pipeline().addLast("http-encoder", new HttpResponseEncoder());
                            // 解决大码流的问题,ChunkedWriteHandler:向客户端发送html5文件
                            ch.pipeline().addLast("http-chunked", new ChunkedWriteHandler());
                            //添加处理器到pipeline
                            ch.pipeline().addLast("http-server",new HttpServerHandler());
                        }
                    });
            //启动服务,监听端口,同步返回
            ChannelFuture channelFuture = bootstrap.bind(new InetSocketAddress("127.0.0.1", 6666)).sync();
            // 当通道关闭时继续向后执行,这是一个阻塞方法
            channelFuture.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            bossGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
        }
    }
}

相比之前的入门案例来说,就是给 pipeline 多添加了一堆编码器,目的就是对Http请求或响应进行编码。

处理器代码

handler中需要根据请求Method判断是GET或者POST,对于POST还要判断是普通表单提交还是JSON提交。然后根据不同的请求方式取到请求参数。最后把“Hello Netty”返回给客户端。

package cn.itsource.nio.httpserver;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.sun.deploy.util.StringUtils;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.multipart.DefaultHttpDataFactory;
import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder;
import io.netty.handler.codec.http.multipart.InterfaceHttpData;
import io.netty.handler.codec.http.multipart.MemoryAttribute;
import io.netty.util.CharsetUtil;

import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


public class HttpServerHandler extends SimpleChannelInboundHandler<FullHttpRequest> {

    //该方法的作用是用来读取客户端的数据, FullHttpRequest 请求对象
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {
        System.out.println("客户端:"+ctx.channel().remoteAddress()+" 发来请求");
        //读取数据
        Map<String, Object> data = readData(request);
        System.out.println("data:"+data);
        //根据请求参数,处理不同的业务,做出不同的响应
        //就返回一个Hello好了
        String content = "Hello Netty";
        //响应数据
        writeDate(ctx,request,content);
    }

    //写数据给客户端
    private void writeDate(ChannelHandlerContext ctx,FullHttpRequest request,String content) {
        //把数据拷贝到buffer
        ByteBuf buffer = Unpooled.copiedBuffer(content, CharsetUtil.UTF_8);
        //创建Http响应对象,设置http版本和状态吗,以及数据
        DefaultFullHttpResponse httpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,HttpResponseStatus.OK,buffer);
        //响应头信息
        httpResponse.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plan");
        httpResponse.headers().set(HttpHeaderNames.CONTENT_LENGTH, buffer.readableBytes());
       
        //响应结果
        ctx.writeAndFlush(httpResponse);
    }

    //读取数据,get,或者post
    public Map<String,Object> readData(FullHttpRequest request){
        Map<String, Object> param = null;
        if(request.method() == HttpMethod.GET){
            param = getGetParams(request);
        }else if(request.method() == HttpMethod.POST){
            param = getPostParams(request);
        }
        System.out.println(param);
        return param;
    }

    //获取GET方式传递的参数
    private Map<String, Object> getGetParams(FullHttpRequest fullHttpRequest) {
        Map<String, Object> params = new HashMap<String, Object>();
        if (fullHttpRequest.method() == HttpMethod.GET) {
            // 处理get请求
            QueryStringDecoder decoder = new QueryStringDecoder(fullHttpRequest.uri());
            Map<String, List<String>> paramList = decoder.parameters();
            for (Map.Entry<String, List<String>> entry : paramList.entrySet()) {
                params.put(entry.getKey(), entry.getValue().get(0));
            }
            return params;
        }
        return null;
    }

    //获取POST方式传递的参数
    private Map<String, Object> getPostParams(FullHttpRequest fullHttpRequest) {

        if (fullHttpRequest.method() == HttpMethod.POST) {
            // 处理POST请求
            String strContentType = fullHttpRequest.headers().get("Content-Type").trim();
            if (strContentType.contains("x-www-form-urlencoded")) {
                return getFormParams(fullHttpRequest);
            } else if (strContentType.contains("application/json")) {
                return getJSONParams(fullHttpRequest);
            }
        }
        return null;
    }

    //解析JSON数据
    private Map<String, Object> getJSONParams(FullHttpRequest fullHttpRequest)  {
        ByteBuf content = fullHttpRequest.content();
        byte[] reqContent = new byte[content.readableBytes()];
        content.readBytes(reqContent);
        try {
            return JSON.parseObject(new String(reqContent, "UTF-8"),Map.class);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return null;
    }

    //解析from表单数据(Content-Type = x-www-form-urlencoded)
    private Map<String, Object> getFormParams(FullHttpRequest fullHttpRequest) {
        Map<String, Object> params = new HashMap<String, Object>();
        HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(new DefaultHttpDataFactory(false), fullHttpRequest);
        List<InterfaceHttpData> postData = decoder.getBodyHttpDatas();
        for (InterfaceHttpData data : postData) {
            if (data.getHttpDataType() == InterfaceHttpData.HttpDataType.Attribute) {
                MemoryAttribute attribute = (MemoryAttribute) data;
                params.put(attribute.getName(), attribute.getValue());
            }
        }
        return params;
    }

}

文章到这就结束了,点赞还是要求一下的,万一屏幕面前的大帅哥,或者大漂亮一不小心就一键三连了啦,那我就是熬夜到头发掉光,也出下章

以上是关于九.Netty入门到超神系列-Netty开发Http服务器的主要内容,如果未能解决你的问题,请参考以下文章

七.Netty入门到超神系列-Netty介绍和线程模型

八.Netty入门到超神系列-Netty入门程序

十.Netty入门到超神系列-基于Netty群聊系统

一.Netty入门到超神系列-BIONIOAIO的认识

十一.Netty入门到超神系列-Netty使用Protobuf编码解码

十三.Netty入门到超神系列-手撸简单版RPC框架(仿Dubbo)