netty 4.1.45 第一個netty程序

注:更多netty相關文章請訪問博主專欄: netty專欄

概述

本系列文章介紹netty的學習。使用的版本是4.1.45
jdk採用Java11

本系列文章由淺入深,先學習使用,再研究其實現原理。
本節編寫一個最簡單的netty服務器。

maven依賴

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

服務端

package com.example;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;

public class MyNettyServer {
    public static void main(String[] args) {
        //配置服務端NIO線程組
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workGroup = new NioEventLoopGroup();

        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup, workGroup)//配置主從線程組
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 1024)//配置一些TCP的參數
                    .childHandler(new MyChildHandler());//添加自定義的channel
            //綁定8080端口
            ChannelFuture channelFuture = serverBootstrap.bind(8080).sync();
            //服務端監聽端口關閉
            ChannelFuture future = channelFuture.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //netty優雅停機
            bossGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
        }
    }
}

class MyChildHandler extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel socketChannel) {
        //將自定義的channelhandler添加到channelpipeline的末尾
        socketChannel.pipeline().addLast(new TimeServerHandler());
    }
}

/**
 * TimeServerHandler這個纔是服務端真正處理請求的服務方法
 */
class TimeServerHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

        //將請求入參轉爲ByteBuf對象
        ByteBuf byteBuf = (ByteBuf) msg;
        byte[] bytes = new byte[byteBuf.readableBytes()];
        byteBuf.readBytes(bytes);
        //由於我們這裏傳的參數是string,所以直接強制轉換
        String body = new String(bytes, "UTF-8");
        System.out.println("收到客戶端請求:" + body);

        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String currentTimeStr = "QUERY TIME ORDER".equalsIgnoreCase(body) ?
                format.format(new Date()) + "" : "BAD ORDER";
        ByteBuf resp = Unpooled.copiedBuffer(currentTimeStr.getBytes());
        ctx.write(resp);
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        ctx.flush();
    }

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

這裏配置了NIO服務端,採用的是主從線程模型,關於主從模型後續再介紹。
服務端端口是8080.
服務端真正處理客戶端請求的方法是com.example.TimeServerHandler#channelRead,該方法的作用是接收請求,返回服務器時間

客戶端

package com.example;

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;


public class MyNettyClient {
    public static void main(String[] args) {
        //客戶端NIO線程組
        EventLoopGroup group = new NioEventLoopGroup();

        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) {
                            ch.pipeline().addLast(new TimeClientHandler());
                        }
                    });

            //異步鏈接服務器
            ChannelFuture future = bootstrap.connect("127.0.0.1", 8080).sync();
            //等等客戶端鏈接關閉
            future.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //優雅停機
            group.shutdownGracefully();
        }
    }
}

//客戶端業務邏輯處理類
class TimeClientHandler extends ChannelInboundHandlerAdapter {

    /**
     * 客戶端與服務器TCP鏈路鏈接成功後調用該方法
     * @param ctx
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        byte[] req = "QUERY TIME ORDER".getBytes();
        ByteBuf firstMsg = Unpooled.buffer(req.length);
        firstMsg.writeBytes(req);
        ctx.writeAndFlush(firstMsg);//寫入緩衝並且發送到socketchannel
    }

    /**
     * 讀取到服務端相應後執行該方法
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf byteBuf = (ByteBuf) msg;
        byte[] bytes = new byte[byteBuf.readableBytes()];
        byteBuf.readBytes(bytes);
        String body = new String(bytes, "UTF-8");
        System.out.println("服務端返回:"+body);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("Unexpected exception from downstream : " + cause.getMessage());
        ctx.close();
    }
}

運行結果

先啓動服務器端,再啓動客戶端。可以看到客戶端運行結果:
在這裏插入圖片描述

OK,第一個netty程序就完成了,上面代碼中有一些註釋幫助理解運行過程。後續會再寫更深入的文章。

注:更多netty相關文章請訪問博主專欄: netty專欄

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