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