netty实现http协议服务

前面了解了netty的启动流程,实现http协议服务在pipeline里添加对应的http协议报文处理器即可。

server端:

java 复制代码
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workGroup = new NioEventLoopGroup(4);

ServerBootstrap bootstrap = new ServerBootstrap().group(bossGroup,workGroup)
        .channel(NioServerSocketChannel.class)
        .option(ChannelOption.SO_BACKLOG, 1024)
        .childHandler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel ch) throws Exception {
                ChannelPipeline p = ch.pipeline();
                p.addLast(new HttpServerCodec());
                p.addLast(new HttpObjectAggregator(65536));
                p.addLast(new CustomHttpServerHandler());
            }
        });
bootstrap.bind(8080).sync();

这里添加了三个编解码处理handler。HttpServerCodec这个是http协议格式处理器,其相当于HttpRequestDecoder和HttpResponseEncoder一对编解码处理器。HttpObjectAggregator是用来报文数据聚合,合并请求数据成一个报文。CustomHttpServerHandler这个是我们自定义的报文处理器,

java 复制代码
public class CustomHttpServerHandler extends SimpleChannelInboundHandler<FullHttpRequest> {

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest msg) throws Exception {
        String content = "Hello";
        FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK,
                Unpooled.wrappedBuffer(content.getBytes()));
        response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain");
        response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes());
        ctx.writeAndFlush(response);
    }
}

这里收到消息经过前面两个handler处理后类型是FullHttpRequest,在channelRead0方法里构造了一个简单的FullHttpResponse对象,返回数据给客户端。

这样访问http://localhost:8080就会看到服务端返回的hello。

上面例子只是做了一个简单的请求响应。没有对请求数据进行解析,也没有根据资源uri进行转发不同处理。

请求地址可以从入参FullHttpRequest msg中获取

java 复制代码
HttpMethod method = msg.method();
System.out.println("接收请求:"+msg.uri()+"请求方法:"+msg.method().name());

这里uri的dispatch就不处理了,主要是了解报文数据处理。下面来看不同报文格式数据如何处理。同样的这里也只考虑POST请求。

我们平时web开发知道,POST传送数据有好几种方式,其报文数据格式是不一样的。不同数据格式可以从请求报文头的Content-type获取。

1、form表单形式(application/x-www-form-urlencoded)

这种方式参数还是key=value&key=value形式放到请求body中传过来的,可以使用HttpPostRequestDecoder来进行解析.

java 复制代码
            HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(new DefaultHttpDataFactory(false), msg);
            List<InterfaceHttpData> postData = decoder.getBodyHttpDatas();

            for (InterfaceHttpData data : postData) {
                if (data.getHttpDataType() == InterfaceHttpData.HttpDataType.Attribute) {
                    // 处理表单字段
                    Attribute attribute = (Attribute) data;
                    String fieldName = attribute.getName();
                    String fieldValue = null;
                    try {
                        fieldValue = attribute.getValue();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    System.out.println(fieldName + ": " + fieldValue);
                }
            }

2、包含文件(multipart/form-data)

这种方式有一个boundary来区分不同的表单项。可以用HttpPostMultipartRequestDecoder来解析

java 复制代码
HttpPostMultipartRequestDecoder decoder = new HttpPostMultipartRequestDecoder(new DefaultHttpDataFactory(false), msg);
List<InterfaceHttpData> postData = decoder.getBodyHttpDatas();

for (InterfaceHttpData data : postData) {
    if (data.getHttpDataType() == InterfaceHttpData.HttpDataType.Attribute) {
        // 处理表单字段
        Attribute attribute = (Attribute) data;
        String fieldName = attribute.getName();
        String fieldValue = null;
        try {
            fieldValue = attribute.getValue();
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println(fieldName + ": " + fieldValue);
    } else if (data.getHttpDataType() == InterfaceHttpData.HttpDataType.FileUpload) {
        // 处理文件上传字段
        FileUpload fileUpload = (FileUpload) data;
        String fieldName = fileUpload.getName();
        String fileName = fileUpload.getFilename();
        String fileType = fileUpload.getContentType();
        long fileSize = fileUpload.length();
        System.out.println("文件上传属性: " + fieldName + ", 文件名: " + fileName + ", 文件类型: " + fileType + ", 文件大小: " + fileSize + " bytes");
    }
}

这里要注意文件的大小,还记得最开始添加的报文聚合handler:HttpObjectAggregator,如果超过了这个指定的大小会直接返回414错误。

另外像其它如json、xml这种格式,直接获取报文体进行处理即可,不需要特殊的处理

java 复制代码
ByteBuf content = msg.content();
String requestBody = content.toString(StandardCharsets.UTF_8);
System.out.println("请求内容:"+requestBody);

请求报文处理完了之后还是最后要构建一个FullHttpResponse对象将响应数据返回给客户端。这里整个处理下来搭建一个完整的http服务还是有许多东西要考虑的,不如直接使用现有的web容器,这里仅仅是为了演示,也为下一篇说websocket做准备。

相关推荐
YWamy6 小时前
如何降低RTC SDK在复杂网络环境下的运行成本
网络·音视频·实时音视频
北京盛世宏博6 小时前
信创国产化智慧档案馆管库系统:十二防一体化管控平台落地笔记
网络·方案·温湿度·智慧档案馆建设
w-w0w-w7 小时前
SQL注入
网络·数据库·sql
一条泥憨鱼7 小时前
【计算机网络】短连接、长连接与连接池:从 TCP 握手到池化复用的完整链路
网络·数据库·网络协议·tcp/ip·计算机网络
北京盛世宏博8 小时前
数字孪生 3D 可视化实战:档案十防体系温湿度数据联动与告警定位实现
网络·档案八防·十防
Lynne3099 小时前
EtherCAT网络中,多个节点如何配置才能互不干扰?
网络·网络配置·ethercat·工业网络
Yang96119 小时前
实验室里的多面手:鼎讯信通TM-150信号发生器技术拆解与实测思考
网络
默|笙11 小时前
【Linux网络】传输层协议TCP(1)---TCP报文介绍
linux·网络·tcp/ip
云飞云共享云桌面11 小时前
1台SolidWorks服务器如何承载10个设计并发?
运维·服务器·网络·3d·制造
Escalating_xu13 小时前
【Linux 网络编程】NAT、代理服务与内网穿透:从一个公网 IP,到一条真正可达的连接
linux·网络·tcp/ip