天天看點

netty4 學習入門1-netty作為http伺服器

1. 首先下載下傳gradle然後配置到環境變量中,gradle下載下傳位址:https://gradle.org/

2. 使用gradle下載下傳netty所需要的jar包(搜尋網址:http://search.maven.org/)

netty4 學習入門1-netty作為http伺服器

選擇all

netty4 學習入門1-netty作為http伺服器

在選擇最新的版本(ps最新的版本不是5.x,netty的5.x已經被廢棄掉了,原因是netty5做了大量的工作以後發現并沒有提高netty的便利和性能):

netty4 學習入門1-netty作為http伺服器

複制gradle位址

netty4 學習入門1-netty作為http伺服器

導入成功:

netty4 學習入門1-netty作為http伺服器

3. 編寫netty的伺服器端代碼

package com.baidu.netty.firstExample;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;

public class TestHttpServer {
    public static void main(String[] args)  throws Exception{
        //1. 定義2個線程池 一個是boss 一個是worker, boss負責接收請求,然後傳遞給worker處理
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            //2. 建立netty給我們提供的用來簡化伺服器端啟動的ServerBootstrap 對象
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            //3. 設定組、通道、事件流
            serverBootstrap.group(bossGroup,workerGroup).channel(NioServerSocketChannel.class).childHandler(new TestHttpServerInitializer());
            //4. 綁定端口等資料
            ChannelFuture future = serverBootstrap.bind(8899).sync();
            future.channel().closeFuture().sync();
        }finally {
            // 5. 調用netty的優雅關閉
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}
           

4. 編寫netty事件流對象(netty主要是使用事件回調的方式來工作的,是以事件對象在netty中是非常重要的,可以了解為struts2中的Interceptor)

package com.baidu.netty.firstExample;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpServerCodec;

public class TestHttpServerInitializer extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        //HttpServerCodec是netty中非常重要的一個類,用來設定http的編碼和解碼,
        pipeline.addLast("httpServerCodec", new HttpServerCodec());
        //添加自己的處理器
        pipeline.addLast(new TestHttpHandler());
    }
}
           

5. 編寫自己的處理器對象

package com.baidu.netty.firstExample;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import io.netty.util.CharsetUtil;

public class TestHttpHandler extends SimpleChannelInboundHandler<HttpObject> {

    /**
     * 該方法在5.0中被命名為messageReceived,估計後期還是會對該方法進行重命名
     * 該方法的主要作用是讀取用戶端的請求,并生産相應資料,我們在該處傳回一個helloworld
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
        //1.定義傳回對象
        ByteBuf content = Unpooled.copiedBuffer("hello world", CharsetUtil.UTF_8);
        //2. 定義response傳回對象,注意該處的response對象不是servlet中的HttpResponse,他是netty自己封裝的傳回資料,跟http沒有任何關系
        FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,HttpResponseStatus.OK, content);
        //3. 設定傳回的資料頭資訊
        response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain");
        response.headers().set(HttpHeaderNames.CONTENT_LENGTH, content.readableBytes());
        //4. 調用ctx傳回response對象
        ctx.writeAndFlush(response);
    }
}
           

6. 啟動伺服器,完成測試

netty4 學習入門1-netty作為http伺服器

7.  在TestHttpHandler 的channelRead0中添加一行列印,然後使用浏覽器和crul再次通路會發現curl執行了2次,浏覽器執行了4次

netty4 學習入門1-netty作為http伺服器
netty4 學習入門1-netty作為http伺服器

為何會出現這種情況呢?

我們可以對我們的代碼在進行一次修改,然後在使用curl進行通路

netty4 學習入門1-netty作為http伺服器

我們發現其實有2個http請求,但是真正有意思的是第一個,第二個屬于空請求,參考Servlet的做法,我們可以對http請求進行過濾

package com.baidu.netty.firstExample;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import io.netty.util.CharsetUtil;

public class TestHttpHandler extends SimpleChannelInboundHandler<HttpObject> {

    /**
     * 該方法在5.0中被命名為messageReceived,估計後期還是會對該方法進行重命名
     * 該方法的主要作用是讀取用戶端的請求,并生産相應資料,我們在該處傳回一個helloworld
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
            System.out.println(msg);
            if (msg instanceof HttpRequest) //過濾非HTTP的請求和垃圾請求
            {
                System.out.println("invoked....");
                //1.定義傳回對象
                ByteBuf content = Unpooled.copiedBuffer("hello world", CharsetUtil.UTF_8);
                //2. 定義response傳回對象,注意該處的response對象不是servlet中的HttpResponse,他是netty自己封裝的傳回資料,跟http沒有任何關系
                FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,HttpResponseStatus.OK, content);
                //3. 設定傳回的資料頭資訊
                response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain");
                response.headers().set(HttpHeaderNames.CONTENT_LENGTH, content.readableBytes());
                //4. 調用ctx傳回response對象
                ctx.writeAndFlush(response);
            }
    }


}
           

那麼為何浏覽器是4次呢?我們打開浏覽器的F12看network發現其實請求了2次

netty4 學習入門1-netty作為http伺服器

其中有一次是圖示,是以我們應該把圖示這個請求也過濾掉,

package com.baidu.netty.firstExample;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import io.netty.util.CharsetUtil;

import java.net.URI;

public class TestHttpHandler extends SimpleChannelInboundHandler<HttpObject> {

    /**
     * 該方法在5.0中被命名為messageReceived,估計後期還是會對該方法進行重命名
     * 該方法的主要作用是讀取用戶端的請求,并生産相應資料,我們在該處傳回一個helloworld
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
            if (msg instanceof HttpRequest) //過濾非HTTP的請求和垃圾請求
            {
                HttpRequest req = (HttpRequest)msg; //将對象強制類型轉換為HttpRequest的對象
                URI uri = new URI(req.uri()); //注意不要引錯了包,是java.net下的
                if ("/favicon.ico".equals(uri.getPath())) //過濾掉圖示請求
                {
                    System.out.println("get favicon.icon");
                    return;
                }
                System.out.println("invoked....");
                //1.定義傳回對象
                ByteBuf content = Unpooled.copiedBuffer("hello world", CharsetUtil.UTF_8);
                //2. 定義response傳回對象,注意該處的response對象不是servlet中的HttpResponse,他是netty自己封裝的傳回資料,跟http沒有任何關系
                FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,HttpResponseStatus.OK, content);
                //3. 設定傳回的資料頭資訊
                response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain");
                response.headers().set(HttpHeaderNames.CONTENT_LENGTH, content.readableBytes());
                //4. 調用ctx傳回response對象
                ctx.writeAndFlush(response);
            }
    }


}
           

結果就自己測試了

 總結

   作為netty的第一個程式而已,還是有很多的api不熟悉,耐心寫完第一個helloworld程式,發現netty程式基本都是這個套路來編寫了,而且千萬不要将HttpServerCodec寫成了HttpClientCodec,同時通路的時候需要使用netty綁定的端口來進行通路。