Netty第一個入門實例-TCP服務

  本文我們來寫第一個Netty的入門實例,一個TCP服務案例。

Netty入門案例

1. 案例需求

  1. 創建Netty 服務器在 6668 端口監聽
  2. 創建Netty客戶端,客戶端能發送消息給服務器 “hello, 服務器~”
  3. 服務器可以回覆消息給客戶端 “hello, 客戶端~”

目的:對Netty 線程模型 有一個初步認識, 便於理解Netty 模型理論

2.創建maven項目

  通過eclipse或者IDEA創建一個普通的maven項目即可

3.導入依賴

  引入相關的maven座標

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

4.創建服務端Handler

  創建服務端的handler,主要是處理客戶端提交的請求的。

package com.dpb.netty.tcp;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;

/**
 * @program: netty4demo
 * @description:
 * @author: 波波烤鴨
 * @create: 2019-12-23 11:24
 */
public class NettyServerHandler extends ChannelInboundHandlerAdapter {

    /**
     * 讀取客戶端發送來的數據
     * @param ctx ChannelHandler的上下文對象 有管道 pipeline 通道 channel 和 請求地址 等信息
     * @param msg 客戶端發送的具體數據
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("客戶端請求到了..." + ctx.channel().remoteAddress());
        ByteBuf buf = (ByteBuf) msg;

        System.out.println("客戶端發送的數據是:" +buf.toString(CharsetUtil.UTF_8));

    }

    /**
     * 讀取客戶端發送數據完成後的方法
     *    在本方法中可以發送返回的數據
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        // writeAndFlush 是組合方法
        ctx.writeAndFlush(Unpooled.copiedBuffer("你好啊,客戶端....^_^",CharsetUtil.UTF_8));
    }
}

5.創建服務端

  創建服務端

package com.dpb.netty.simple;

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

/**
 * @program: netty4demo
 * @description:
 * @author: 波波烤鴨
 * @create: 2019-12-23 11:15
 */
public class NettyServerDemo {
    public static void main(String[] args) {
        // 創建對應的 線程池
        // 創建Boss group
        EventLoopGroup boosGroup = new NioEventLoopGroup(1);
        // 創建 workgroup
        EventLoopGroup workGroup = new NioEventLoopGroup();
        // 創建對應的啓動類
        ServerBootstrap bootstrap = new ServerBootstrap();
        try{
            // 設置相關的配置信息
            bootstrap.group(boosGroup,workGroup) // 設置對應的線程組
                    .channel(NioServerSocketChannel.class) // 設置對應的通道
                    .option(ChannelOption.SO_BACKLOG,1024) // 設置線程的連接個數
                    .childHandler(new ChannelInitializer<SocketChannel>() { // 設置
                        /**
                         * 給pipeline 設置處理器
                         * @param socketChannel
                         * @throws Exception
                         */
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline().addLast(new NettyServerHandler());
                        }
                    });
            System.out.println("服務啓動了....");
            // 綁定端口  啓動服務
            ChannelFuture channelFuture = bootstrap.bind(6668).sync();
            // 對關閉通道進行監聽
            channelFuture.channel().closeFuture().sync();
        }catch (Exception e){

        }finally {
            // 優雅停服
            boosGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
        }

    }
}

6.創建客戶端Handler

  客戶端handler主要是發送請求給客戶端及處理服務端返回的信息。

package com.dpb.netty.simple;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;

/**
 * @program: netty4demo
 * @description:
 * @author: 波波烤鴨
 * @create: 2019-12-23 11:36
 */
public class NettyClientHandler extends ChannelInboundHandlerAdapter {

    /**
     * 連接上服務的回調方法
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        // 發送數據
        System.out.println("連接上了 服務器....");
        ctx.writeAndFlush(Unpooled.copiedBuffer("哈哈 你好呀!!!", CharsetUtil.UTF_8));
    }

    /**
     * 讀取服務端返回的信息
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf buf  = (ByteBuf) msg;
        System.out.println("服務端返回的信息:" + buf.toString(CharsetUtil.UTF_8));
    }
}

7.創建客戶端

  客戶端用來連接服務器

package com.dpb.netty.simple;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;

/**
 * @program: netty4demo
 * @description:
 * @author: 波波烤鴨
 * @create: 2019-12-23 11:31
 */
public class NettyClientDemo {

    public static void main(String[] args) {
        // 客戶端就只需要創建一個 線程組了
        EventLoopGroup loopGroup = new NioEventLoopGroup();
        // 創建 啓動器
        Bootstrap bootstrap = new Bootstrap();
        try{
            // 設置相關的參數
            bootstrap.group(loopGroup)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline().addLast(new NettyClientHandler());
                        }
                    });
            // 連接服務
            ChannelFuture future = bootstrap.connect("localhost",6668).sync();
            // 對服務關閉 監聽
            future.channel().closeFuture().sync();
        }catch (Exception e){

        }finally {
            loopGroup.shutdownGracefully();
        }

    }
}

8.測試

  先啓動服務器,然後再啓動客戶端,然後看下面的效果,實現了我們的要求
在這裏插入圖片描述

方案再說明

  1. Netty 抽象出兩組線程池,BossGroup 專門負責接收客戶端連接,WorkerGroup 專門負責網絡讀寫操作。
  2. NioEventLoop 表示一個不斷循環執行處理任務的線程,每個 NioEventLoop 都有一個 selector,用於監聽綁定在其上的 socket 網絡通道。
  3. NioEventLoop 內部採用串行化設計,從消息的讀取->解碼->處理->編碼->發送,始終由 IO 線程 NioEventLoop 負責

NioEventLoopGroup 下包含多個 NioEventLoop
每個 NioEventLoop 中包含有一個 Selector,一個 taskQueue
每個 NioEventLoop 的 Selector 上可以註冊監聽多個 NioChannel
每個 NioChannel 只會綁定在唯一的 NioEventLoop 上
每個 NioChannel 都綁定有一個自己的 ChannelPipeline

好了netty的第一個入門案例就到此了,具體的代碼解釋請多看代碼中的註釋,有不清楚的歡迎留言交流 O(∩_∩)O哈哈~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章