1. 程式人生 > >Netty學習——用Netty實現一個簡單的Http伺服器

Netty學習——用Netty實現一個簡單的Http伺服器

package study.netty;

import io.netty.bootstrap.ServerBootstrap;
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.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.SimpleChannelInboundHandler;
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.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpRequestEncoder;
import io.netty.handler.codec.http.HttpResponseEncoder;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.util.CharsetUtil;

public class NettyHttpServer {
public static void main(String[] args) {
	EventLoopGroup bossGroup = new NioEventLoopGroup();
	EventLoopGroup workerGroup = new NioEventLoopGroup();
	ServerBootstrap bootstrap = new ServerBootstrap();
	bootstrap.group(bossGroup, workerGroup)
	.option(ChannelOption.SO_BACKLOG, 1024)
	.channel(NioServerSocketChannel.class)
	.childHandler(new ChannelInitializer<SocketChannel>() {

		@Override
		protected void initChannel(SocketChannel ch) throws Exception {
			ch.pipeline().addLast("http-decoder",new HttpRequestDecoder());
			ch.pipeline().addLast("http-aggregator",new HttpObjectAggregator(65535));//將多個訊息轉化成一個
			ch.pipeline().addLast("http-encoder",new HttpResponseEncoder());
			ch.pipeline().addLast("http-chunked",new ChunkedWriteHandler());//解決大碼流的問題
			ch.pipeline().addLast("http-server",new HttpHandler());
		}
	});
	ChannelFuture future = bootstrap.bind("localhost",8888);
	try {
		future.channel().closeFuture().sync();
	} catch (InterruptedException e) {
		e.printStackTrace();
	}finally {
		bossGroup.shutdownGracefully();
		workerGroup.shutdownGracefully();
	}
}
	private static class HttpHandler extends SimpleChannelInboundHandler<FullHttpRequest>{

		@Override
		protected void messageReceived(ChannelHandlerContext ctx, FullHttpRequest msg) throws Exception {
			try {
			ByteBuf content = msg.content();
			byte[] bts = new byte[content.readableBytes()];
			content.readBytes(bts);
			String result = null;
			if(msg.getMethod() == HttpMethod.GET) {
				String url = msg.getUri().toString();
				result = "get method and paramters is "+ url.substring(url.indexOf("?")+1);
			}else if(msg.getMethod() == HttpMethod.POST) {
				result = "post method and paramters is "+ new String(bts);
			}
			FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
			response.headers().set("content-Type","text/html;charset=UTF-8");
			StringBuilder sb = new StringBuilder();
			sb.append("<html>")
						.append("<head>")
							.append("<title>netty http server</title>")
						.append("</head>")
						.append("<body>")
							.append(result)
						.append("</body>")
					.append("</html>\r\n");
			ByteBuf responseBuf = Unpooled.copiedBuffer(sb,CharsetUtil.UTF_8);
			response.content().writeBytes(responseBuf);
			responseBuf.release();
			ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
			}catch (Exception e) {
				e.printStackTrace();
			}
		}
		
	}
}
跟前面的伺服器-客戶端通訊的例子大同小異,也是新增兩個EventLoopGroup,然後建立一個ServerBootstrap,再把兩個EventLoopGroup註冊到ServerBootstrap中,新增相應的引數,實現SocketChannel初始化器,然後新增相應的處理器,處理器的功能就是相應註釋的說明。在繼承自SimpleChannelInboundHandler的類中實現自己的處理請求的業務功能,如果是get請求,那麼引數就是請求的URI中,解析出?後面的就是請求引數,如果是post請求,那麼請求引數就是請求體中,通過獲取resquest的content,得到一個緩衝物件,然後把這個緩衝物件中的資料提取出來就解析出了post請求的請求引數。再然後構造一個DefaultFullHttpResponse物件,向這個response的響應體中寫入頁面的資料,最後呼叫ctx的writeAndFlush把response物件寫回給請求客戶端,並新增CLOSE監聽,如果不新增的話,Netty就不能知道請求的業務邏輯是否已經結束,也就不知道是否應該結束http的一次請求會話。程式的執行結果就是無論是get請求還是post請求,返回都面都可以返回請求的方法型別還有請求的引數。