Netty4.1入門(一)服務端
阿新 • • 發佈:2021-12-12
開發了一個簡單的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 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); 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