99re热这里只有精品视频,7777色鬼xxxx欧美色妇,国产成人精品一区二三区在线观看,内射爽无广熟女亚洲,精品人妻av一区二区三区

Netty如何寫一個 echo 服務器

2018-08-02 14:09 更新

Netty 實現(xiàn)的 echo 服務器都需要下面這些:

  • 一個服務器 handler:這個組件實現(xiàn)了服務器的業(yè)務邏輯,決定了連接創(chuàng)建后和接收到信息后該如何處理
  • Bootstrapping: 這個是配置服務器的啟動代碼。最少需要設置服務器綁定的端口,用來監(jiān)聽連接請求。

通過 ChannelHandler 來實現(xiàn)服務器的邏輯

Echo Server 將會將接受到的數(shù)據(jù)的拷貝發(fā)送給客戶端。因此,我們需要實現(xiàn) ChannelInboundHandler 接口,用來定義處理入站事件的方法。由于我們的應用很簡單,只需要繼承 ChannelInboundHandlerAdapter 就行了。這個類 提供了默認 ChannelInboundHandler 的實現(xiàn),所以只需要覆蓋下面的方法:

  • channelRead() - 每個信息入站都會調用
  • channelReadComplete() - 通知處理器最后的 channelread() 是當前批處理中的最后一條消息時調用
  • exceptionCaught()- 讀操作時捕獲到異常時調用

EchoServerHandler 代碼如下:

Listing 2.2 EchoServerHandler

@Sharable                                        //1
public class EchoServerHandler extends
        ChannelInboundHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx,
        Object msg) {
        ByteBuf in = (ByteBuf) msg;
        System.out.println("Server received: " + in.toString(CharsetUtil.UTF_8));        //2
        ctx.write(in);                            //3
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.writeAndFlush(Unpooled.EMPTY_BUFFER)//4
        .addListener(ChannelFutureListener.CLOSE);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx,
        Throwable cause) {
        cause.printStackTrace();                //5
        ctx.close();                            //6
    }
}

1.@Sharable 標識這類的實例之間可以在 channel 里面共享

2.日志消息輸出到控制臺

3.將所接收的消息返回給發(fā)送者。注意,這還沒有沖刷數(shù)據(jù)

4.沖刷所有待審消息到遠程節(jié)點。關閉通道后,操作完成

5.打印異常堆棧跟蹤

6.關閉通道

這種使用 ChannelHandler 的方式體現(xiàn)了關注點分離的設計原則,并簡化業(yè)務邏輯的迭代開發(fā)的要求。處理程序很簡單;它的每一個方法可以覆蓋到“hook(鉤子)”在活動周期適當?shù)狞c。很顯然,我們覆蓋 channelRead因為我們需要處理所有接收到的數(shù)據(jù)。

覆蓋 exceptionCaught 使我們能夠應對任何 Throwable 的子類型。在這種情況下我們記錄,并關閉所有可能處于未知狀態(tài)的連接。它通常是難以 從連接錯誤中恢復,所以干脆關閉遠程連接。當然,也有可能的情況是可以從錯誤中恢復的,所以可以用一個更復雜的措施來嘗試識別和處理 這樣的情況。

如果異常沒有被捕獲,會發(fā)生什么?

每個 Channel 都有一個關聯(lián)的 ChannelPipeline,它代表了 ChannelHandler 實例的鏈。適配器處理的實現(xiàn)只是將一個處理方法調用轉發(fā)到鏈中的下一個處理器。因此,如果一個 Netty 應用程序不覆蓋exceptionCaught ,那么這些錯誤將最終到達 ChannelPipeline,并且結束警告將被記錄。出于這個原因,你應該提供至少一個 實現(xiàn) exceptionCaught 的 ChannelHandler。

關鍵點要牢記:

  • ChannelHandler 是給不同類型的事件調用
  • 應用程序實現(xiàn)或擴展 ChannelHandler 掛接到事件生命周期和 提供自定義應用邏輯。

引導服務器

了解到業(yè)務核心處理邏輯 EchoServerHandler 后,下面要引導服務器自身了。

  • 監(jiān)聽和接收進來的連接請求
  • 配置 Channel 來通知一個關于入站消息的 EchoServerHandler 實例

Transport(傳輸)

在本節(jié)中,你會遇到“transport(傳輸)”一詞。在網(wǎng)絡的多層視圖協(xié)議里面,傳輸層提供了用于端至端或主機到主機的通信服務?;ヂ?lián)網(wǎng)通信的基礎是 TCP 傳輸。當我們使用術語“NIO transport”我們指的是一個傳輸?shù)膶崿F(xiàn),它是大多等同于 TCP ,除了一些由 Java NIO 的實現(xiàn)提供了服務器端的性能增強。Transport 詳細在第4章中討論。

Listing 2.3 EchoServer

public class EchoServer {

    private final int port;

    public EchoServer(int port) {
        this.port = port;
    }
        public static void main(String[] args) throws Exception {
        if (args.length != 1) {
            System.err.println(
                    "Usage: " + EchoServer.class.getSimpleName() +
                    " <port>");
            return;
        }
        int port = Integer.parseInt(args[0]);        //1
        new EchoServer(port).start();                //2
    }

    public void start() throws Exception {
        NioEventLoopGroup group = new NioEventLoopGroup(); //3
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(group)                                //4
             .channel(NioServerSocketChannel.class)        //5
             .localAddress(new InetSocketAddress(port))    //6
             .childHandler(new ChannelInitializer<SocketChannel>() { //7
                 @Override
                 public void initChannel(SocketChannel ch) 
                     throws Exception {
                     ch.pipeline().addLast(
                             new EchoServerHandler());
                 }
             });

            ChannelFuture f = b.bind().sync();            //8
            System.out.println(EchoServer.class.getName() + " started and listen on " + f.channel().localAddress());
            f.channel().closeFuture().sync();            //9
        } finally {
            group.shutdownGracefully().sync();            //10
        }
    }

}

1.設置端口值(拋出一個 NumberFormatException 如果該端口參數(shù)的格式不正確)

2.呼叫服務器的 start() 方法

3.創(chuàng)建 EventLoopGroup

4.創(chuàng)建 ServerBootstrap

5.指定使用 NIO 的傳輸 Channel

6.設置 socket 地址使用所選的端口

7.添加 EchoServerHandler 到 Channel 的 ChannelPipeline

8.綁定的服務器;sync 等待服務器關閉

9.關閉 channel 和 塊,直到它被關閉

10.關機的 EventLoopGroup,釋放所有資源。

在這個例子中,代碼創(chuàng)建 ServerBootstrap 實例(步驟4)。由于我們使用在 NIO 傳輸,我們已指定 NioEventLoopGroup(3)接受和處理新連接,指定 NioServerSocketChannel(5)為信道類型。在此之后,我們設置本地地址是 InetSocketAddress 與所選擇的端口(6)如。服務器將綁定到此地址來監(jiān)聽新的連接請求。

第7步是關鍵:在這里我們使用一個特殊的類,ChannelInitializer 。當一個新的連接被接受,一個新的子 Channel 將被創(chuàng)建, ChannelInitializer 會添加我們EchoServerHandler 的實例到 Channel 的 ChannelPipeline。正如我們如前所述,如果有入站信息,這個處理器將被通知。

雖然 NIO 是可擴展性,但它的正確配置是不簡單的。特別是多線程,要正確處理也非易事。幸運的是,Netty 的設計封裝了大部分復雜性,尤其是通過抽象,例如 EventLoopGroup,SocketChannel 和 ChannelInitializer,其中每一個將在更詳細地在第3章中討論。

在步驟8,我們綁定的服務器,等待綁定完成。 (調用 sync() 的原因是當前線程阻塞)在第9步的應用程序將等待服務器 Channel 關閉(因為我們 在 Channel 的 CloseFuture 上調用 sync())?,F(xiàn)在,我們可以關閉下 EventLoopGroup 并釋放所有資源,包括所有創(chuàng)建的線程(10)。

NIO 用于在本實施例,因為它是目前最廣泛使用的傳輸,歸功于它的可擴展性和徹底的不同步。但不同的傳輸?shù)膶崿F(xiàn)是也是可能的。例如,如果本實施例中使用的 OIO 傳輸,我們將指定 OioServerSocketChannel 和 OioEventLoopGroup。 Netty 的架構,包括更關于傳輸信息,將包含在第4章。在此期間,讓我們回顧下在服務器上執(zhí)行,我們只研究重要步驟。

服務器的主代碼組件是

  • EchoServerHandler 實現(xiàn)了的業(yè)務邏輯
  • 在 main() 方法,引導了服務器

執(zhí)行后者所需的步驟是:

  • 創(chuàng)建 ServerBootstrap 實例來引導服務器并隨后綁定
  • 創(chuàng)建并分配一個 NioEventLoopGroup 實例來處理事件的處理,如接受新的連接和讀/寫數(shù)據(jù)。
  • 指定本地 InetSocketAddress 給服務器綁定
  • 通過 EchoServerHandler 實例給每一個新的 Channel 初始化
  • 最后調用 ServerBootstrap.bind() 綁定服務器

這樣服務器的初始化就完成了,并可以被使用。


以上內容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號