第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

Netty 入門案例

1. 前言

本節(jié)主要是使用 Netty 來(lái)開(kāi)發(fā)服務(wù)端和客戶端,Netty 的開(kāi)發(fā)模式基本上都是主啟動(dòng)類 + 自定義業(yè)務(wù) Handler,Netty 是基于責(zé)任鏈的模式來(lái)管理自定義部分的 Handler,本節(jié)帶大家感受一下 Netty 的開(kāi)發(fā)。

需求: 本節(jié)主要通過(guò) Netty 來(lái)實(shí)現(xiàn)我們的第一個(gè) Demo,主要功能是分別建立兩個(gè)項(xiàng)目(客戶端和服務(wù)端),客戶端向服務(wù)端發(fā)送 Hello World,服務(wù)端接受到數(shù)據(jù)之后打印到控制臺(tái),并且給客戶端響應(yīng)。

2. 環(huán)境搭建

第一步: 使用 Maven 構(gòu)建工程,項(xiàng)目結(jié)構(gòu)如下:圖片描述

第二步: netty-demo-clientnetty-demo-server 兩個(gè)工程的 pom.xml 導(dǎo)入 Netty 坐標(biāo),Netty 主流有三個(gè)版本,分別是 3.x、4.x、5.x,一般主流都是使用 4.x 版本。

<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-all</artifactId>
    <version>4.1.6.Final</version>
</dependency>

第三步: netty-demo-client 工程相關(guān)類

建立兩個(gè)類,分別是客戶端啟動(dòng)類 NettyClient.java 和業(yè)務(wù)處理類 NettyClientHandler.java

圖片描述

第四步: netty-demo-server 工程相關(guān)類

建立兩個(gè)類,分別是服務(wù)端啟動(dòng)類 NettyServer.java 和業(yè)務(wù)處理類 NettyServerHandler.java
圖片描述

3. 核心流程

客戶端和服務(wù)端通信流程圖如下圖所示:
圖片描述
核心步驟說(shuō)明:

  1. 在 NettyClientHandler 的 channelActive () 方法往服務(wù)端發(fā)送消息;
  2. 在 NettyServerHandler 的 channelRead () 方法接受消息,并且響應(yīng)消息給客戶端;
  3. 在 NettyClientHandler 的 channelRead () 方法接受服務(wù)端的響應(yīng)消息。

4. 如何自定義 Handler

在 Netty 的開(kāi)發(fā)當(dāng)中,最核心就是自定義 Handler,通常根據(jù)不同的業(yè)務(wù)定義不同的 Handler。自定義 Handler 一般分為三個(gè)核心步驟:

  1. 需要繼承 ChannelInboundHandlerAdapter 類;

實(shí)例:

public class TestHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
     	//業(yè)務(wù)處理   
    }
}

  1. 重寫幾個(gè)核心的方法,其中 channelRead () 是業(yè)務(wù)邏輯編寫,使用最多;
方法名稱 觸發(fā)時(shí)機(jī) 常見(jiàn)業(yè)務(wù)場(chǎng)景
channelActive 連接就緒時(shí)觸發(fā) 連接時(shí)進(jìn)行登錄認(rèn)證
channelRead 通道有數(shù)據(jù)可讀取時(shí)觸發(fā) 讀取數(shù)據(jù)并且做處理,這個(gè)是用的最多的方法
channelInactive 連接斷開(kāi)時(shí)觸發(fā) 連接斷開(kāi),刪除服務(wù)端對(duì)于的 Session 關(guān)系;也可以在這里實(shí)現(xiàn)斷開(kāi)重新
exceptionCaught 發(fā)生異常時(shí)觸發(fā) 發(fā)生日常時(shí),做日志記錄
  1. 把 Handler 添加到 Pipeline 管道里面

實(shí)例:

bootstrap.handler(new ChannelInitializer<SocketChannel>() {
    @Override
    public void initChannel(SocketChannel ch) {
        //自定義業(yè)務(wù) Handler
        ch.pipeline().addLast(new TestHandler());
    }
});

5. 服務(wù)端實(shí)現(xiàn)

5.1 服務(wù)端啟動(dòng)類

public class NettyServer {
    public static void main(String[] args) {
        //線程組-主要是監(jiān)聽(tīng)客戶端請(qǐng)求
        NioEventLoopGroup bossGroup = new NioEventLoopGroup();
        //線程組-主要是處理具體業(yè)務(wù)
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();
		//啟動(dòng)類
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        serverBootstrap
            	//指定線程組
                .group(bossGroup, workerGroup)
            	//指定 NIO 模式
                .channel(NioServerSocketChannel.class)
            	//雙向鏈表管理
                .childHandler(new ChannelInitializer<NioSocketChannel>() {
                    protected void initChannel(NioSocketChannel ch) {
                        //責(zé)任鏈,指定自定義處理業(yè)務(wù)的 Handler
                        ch.pipeline().addLast(new NettyServerHandler());
                    }
                });
		//綁定端口號(hào)
        serverBootstrap.bind(80);
    }
}

代碼說(shuō)明:

  1. 以上都是模板代碼,需要變動(dòng)的是根據(jù)不同的業(yè)務(wù)自定義對(duì)應(yīng)的 Handler,并且在 initChannel () 添加邏輯處理器;
  2. 根據(jù)實(shí)際情況指定 bind () 方法的端口號(hào),注意的是端口號(hào)不能和其它端口號(hào)沖突;
  3. 這里大家先熟練掌握模板代碼的編寫,后面章節(jié)會(huì)講解 NioEventLoopGroup、Pipeline 等核心組件的原理。

5.2 自定義 Handler

public class NettyServerHandler extends ChannelInboundHandlerAdapter {
    //接受客戶端端響應(yīng)時(shí)觸發(fā)該事件
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        //轉(zhuǎn)換為 ByteBuf 緩沖區(qū)(底層是 byte[] 數(shù)組)
        ByteBuf buffer=(ByteBuf)msg;
        //定義一個(gè) byte[] 數(shù)組
        byte[] bytes=new byte[buffer.readableBytes()];
        //緩沖區(qū)把數(shù)據(jù)寫到 byte[] 數(shù)組
        buffer.readBytes(bytes);
        //把 byte[] 轉(zhuǎn)換字符串
        String req=new String(bytes,"UTF-8");
        System.out.println("客戶端請(qǐng)求:"+req);
        
        //給客戶端響應(yīng)信息>>>>>>>>>>>>>>>>>>>>>>>>>>>>
        String res="Hello World>>>>Client";
        //把字符串轉(zhuǎn)換 ByteBuf
        ByteBuf buf=getByteBuf(ctx,res);
        
		//把 ByteBuf 寫到通道并且刷新
        ctx.writeAndFlush(buf);
    }
    
    private ByteBuf getByteBuf(ChannelHandlerContext ctx,String str) {
        // 1. 獲取二進(jìn)制抽象 ByteBuf
        ByteBuf buffer = ctx.alloc().buffer();
        // 2. 準(zhǔn)備數(shù)據(jù),指定字符串的字符集為 utf-8
        byte[] bytes = str.getBytes(Charset.forName("utf-8"));
        // 3. 填充數(shù)據(jù)到 ByteBuf
        buffer.writeBytes(bytes);
        return buffer;
    }
}

代碼說(shuō)明:

  1. 這個(gè)邏輯處理器繼承自 ChannelInboundHandlerAdapter,然后覆蓋了 channelRead () 方法;

  2. channelRead () 方法,接受客戶端請(qǐng)求數(shù)據(jù)時(shí)會(huì)觸發(fā)該方法,一般是用來(lái)處理具體的業(yè)務(wù);

  3. Netty 是面向 ByteBuf 通訊的,接受數(shù)據(jù)和響應(yīng)數(shù)據(jù)都需要轉(zhuǎn)換 ByteBuf,ByteBuf 的 API 后面再詳細(xì)講解,這里我們需要知道的是常見(jiàn)創(chuàng)建 ByteBuf 有常見(jiàn)兩種方式,①通過(guò) Unpooled 非池化工具類來(lái)操作;②通過(guò) ctx.alloc().buffer() 來(lái)獲取。最后我們調(diào)用 ctx.channel().writeAndFlush() 把數(shù)據(jù)寫到服務(wù)端。

6. 客戶端實(shí)現(xiàn)

6.1 客戶端啟動(dòng)類

public class NettyClient {
    public static void main(String[] args) throws InterruptedException {
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();

        Bootstrap bootstrap = new Bootstrap();
        bootstrap
                // 1.指定線程模型
                .group(workerGroup)
                // 2.指定 IO 類型為 NIO
                .channel(NioSocketChannel.class)
                // 3.IO 處理邏輯
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    public void initChannel(SocketChannel ch) {
                        //自定義業(yè)務(wù) Handler
                        ch.pipeline().addLast(new NettyClientHandler());
                    }
                });
        // 4.建立連接
        ChannelFuture future=bootstrap.connect("127.0.0.1", 80).sync();
    }
}

代碼說(shuō)明:

  1. 以上都是模板代碼,需要變動(dòng)的是根據(jù)不同的業(yè)務(wù)自定義對(duì)應(yīng)的 Handler,并且在 initChannel () 添加邏輯處理器;
  2. connect () 方法,指定對(duì)應(yīng)服務(wù)端 ip 和 port。

6.2 自定義 Handler

public class NettyClientHandler extends ChannelInboundHandlerAdapter {
    //客戶端連接成功之后觸發(fā)該事件,只會(huì)觸發(fā)一次
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ctx.channel().writeAndFlush(Unpooled.copiedBuffer("Hello World".getBytes()));
    }

    //接受服務(wù)端響應(yīng)時(shí)觸發(fā)該事件
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf buffer=(ByteBuf)msg;
        byte[] bytes=new byte[buffer.readableBytes()];
        buffer.readBytes(bytes);
        String res=new String(bytes,"UTF-8");
        System.out.println("服務(wù)端響應(yīng):"+res);
    }
}

代碼說(shuō)明:

  1. 這個(gè)邏輯處理器繼承自 ChannelInboundHandlerAdapter,然后覆蓋了 channelActive () 和 channelRead () 方法;
  2. channelActive (),這個(gè)方法會(huì)在客戶端連接建立成功之后被調(diào)用,可以在這個(gè)方法里面,做一些初始化的工作,該方法僅被調(diào)用一次;
  3. channelRead 方法,在接受客戶端響應(yīng)時(shí)觸發(fā),會(huì)觸發(fā)多次。

7. 測(cè)試效果

服務(wù)端打?。?br> 圖片描述

客戶端打印:
圖片描述

8. 視頻演示

9. 小結(jié)

通過(guò)以上的代碼,我們主要實(shí)現(xiàn)了客戶端和服務(wù)端之間的通訊,需要掌握以下關(guān)鍵點(diǎn):

  1. 客戶端和服務(wù)端的啟動(dòng)類代碼,這個(gè)基本上是固定寫法;
  2. 掌握 Handler 的作用以及如何自定義,幾個(gè)核心方法的觸發(fā)時(shí)機(jī)以及常見(jiàn)的應(yīng)用場(chǎng)景;
  3. 和傳統(tǒng)的 socket 編程不同的是,Netty 里面數(shù)據(jù)是以 ByteBuf 為單位的。