1. 程式人生 > 其它 >Netty4.1入門(一)服務端

Netty4.1入門(一)服務端

開發了一個簡單的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