通過學習了解到netty可以像tomcat一樣搭建一個httpServer伺服器,這裡簡單的實作一下。
首先,我們介紹一下httpRequest
認識Http請求
在動手寫Netty架構之前,我們先要了解http請求的組成,如下圖:
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIiNTOwYDN5YWYiRWNvwFcvwVbvNmL1h2cuFWaq5yd3d3Lc9CX6MHc0RHaiojIsJye.jpg)
image.png
HTTP Request 第一部分是包含的頭資訊
HttpContent 裡面包含的是資料,可以後續有多個 HttpContent 部分
LastHttpContent 标記是 HTTP request 的結束,同時可能包含頭的尾部資訊
完整的 HTTP request,由1,2,3組成
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIiNTOwYDN5YWYiRWNvwFcvwVbvNmL1h2cuFWaq5yd3d3Lc9CX6MHc0RHaiojIsJye.jpg)
image.png
HTTP response 第一部分是包含的頭資訊
HttpContent 裡面包含的是資料,可以後續有多個 HttpContent 部分
LastHttpContent 标記是 HTTP response 的結束,同時可能包含頭的尾部資訊
完整的 HTTP response,由1,2,3組成
從request的介紹我們可以看出來,一次http請求并不是通過一次對話完成的,他中間可能有很次的連接配接。netty每一次對話都會建立一個channel,并且一個ChannelInboundHandler一般是不會同時去處理多個Channel的。
如何在一個Channel裡面處理一次完整的Http請求?這就要用到我們上圖提到的FullHttpRequest,我們隻需要在使用netty處理channel的時候,隻處理消息是FullHttpRequest的Channel,這樣我們就能在一個ChannelHandler中處理一個完整的Http請求了。
ph.addLast("aggregator", new HttpObjectAggregator(10*1024*1024));//把單個http請求轉為FullHttpReuest或FullHttpResponse
開始搭建
Server
public class NettyServer {
private static final int port = 6789; //設定服務端端口
private static EventLoopGroup group = new NioEventLoopGroup(); // 通過nio方式來接收連接配接和處理連接配接
private static ServerBootstrap b = new ServerBootstrap();
public static void main(String[] args) throws InterruptedException {
try {
b.group(group);
b.channel(NioServerSocketChannel.class);
b.childHandler(new NettyServerFilter()); //設定過濾器
// 伺服器綁定端口監聽
ChannelFuture f = b.bind(port).sync();
System.out.println("服務端啟動成功...");
// 監聽伺服器關閉監聽
f.channel().closeFuture().sync();
} finally {
group.shutdownGracefully(); 關閉EventLoopGroup,釋放掉所有資源包括建立的線程
}
}
}
ChannelInitializer
public class NettyServerFilter extends ChannelInitializer {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline ph = ch.pipeline();
ph.addLast("encoder",new HttpResponseEncoder());
ph.addLast("decoder",new HttpRequestDecoder());
ph.addLast("aggregator", new HttpObjectAggregator(10*1024*1024));//把單個http請求轉為FullHttpReuest或FullHttpResponse
ph.addLast("handler", new NettyServerHandler());// 服務端業務邏輯
}
}
ChannelInboundHandlerAdapter
public class NettyServerHandler extends ChannelInboundHandlerAdapter {
private String result="";
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if(! (msg instanceof FullHttpRequest)){
result="未知請求!";
send(ctx,result,HttpResponseStatus.BAD_REQUEST);
return;
}
FullHttpRequest httpRequest = (FullHttpRequest)msg;
try{
String path=httpRequest.uri(); //擷取路徑
String body = getBody(httpRequest); //擷取參數
HttpMethod method=httpRequest.method();//擷取請求方法
//如果不是這個路徑,就直接傳回錯誤
if(!"/test".equalsIgnoreCase(path)){
result="非法請求!";
send(ctx,result,HttpResponseStatus.BAD_REQUEST);
return;
}
System.out.println("接收到:"+method+" 請求");
//如果是GET請求
if(HttpMethod.GET.equals(method)){
//接受到的消息,做業務邏輯處理...
System.out.println("body:"+body);
result="GET請求";
send(ctx,result,HttpResponseStatus.OK);
return;
}
//如果是POST請求
if(HttpMethod.POST.equals(method)){
//接受到的消息,做業務邏輯處理...
System.out.println("body:"+body);
result="POST請求";
send(ctx,result,HttpResponseStatus.OK);
return;
}
//如果是PUT請求
if(HttpMethod.PUT.equals(method)){
//接受到的消息,做業務邏輯處理...
System.out.println("body:"+body);
result="PUT請求";
send(ctx,result,HttpResponseStatus.OK);
return;
}
//如果是DELETE請求
if(HttpMethod.DELETE.equals(method)){
//接受到的消息,做業務邏輯處理...
System.out.println("body:"+body);
result="DELETE請求";
send(ctx,result,HttpResponseStatus.OK);
return;
}
}catch(Exception e){
System.out.println("處理請求失敗!");
e.printStackTrace();
}finally{
//釋放請求
httpRequest.release();
}
}
private String getBody(FullHttpRequest request){
ByteBuf buf = request.content();
return buf.toString(CharsetUtil.UTF_8);
}
private void send(ChannelHandlerContext ctx, String context,HttpResponseStatus status) {
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status, Unpooled.copiedBuffer(context, CharsetUtil.UTF_8));
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain; charset=UTF-8");
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("連接配接的用戶端位址:" + ctx.channel().remoteAddress());
ctx.writeAndFlush("用戶端"+ InetAddress.getLocalHost().getHostName() + "成功與服務端建立連接配接! ");
super.channelActive(ctx);
}
}
最終效果
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIiNTOwYDN5YWYiRWNvwFcvwVbvNmL1h2cuFWaq5yd3d3Lc9CX6MHc0RHaiojIsJye.jpg)
image.png
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIiNTOwYDN5YWYiRWNvwFcvwVbvNmL1h2cuFWaq5yd3d3Lc9CX6MHc0RHaiojIsJye.jpg)
項目位址 https://github.com/DespairYoke/netty/tree/master/netty-httpserver