Netty实战:Echo服务之Netty项目搭建服务端以及客户端编写实战

什么是Echo服务?

就是回写应答服务,客户端发送什么数据,服务端就响应什么数据

什么作用呢?

检测调试对应的服务 压测网络编程模型

Step1:项目创建

选择Maven

在这里插入图片描述

Step2:外层pom引入依赖

在这里插入图片描述

线程组 启动类 处理器的类名都是有相对意义的 你去看Netty源码也是一样的

Step1:Server服务端

import io.netty.bootstrap.ServerBootstrap;
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.NioServerSocketChannel;

/**
 * 启动类
 */
public class EchoServer {

    /**
     * 端口号
     */
    private int port;

    private EchoServer(int port){
        this.port = port;
    }

    public static void main(String [] args) throws InterruptedException {

        int port = 8080;

        //判断传入参数是否有端口号
        if (args.length > 0){
            port = Integer.parseInt(args[0]);
        }

        new EchoServer(port).run();
    }


    /**
     * 启动流程
     * 使用Netty线程模型中的Reactor主从线程模型
     */
    public void run() throws InterruptedException {

        //Netty封装好的主从线程模型 配置服务端线程组
        EventLoopGroup bossGroup = new NioEventLoopGroup();

        EventLoopGroup workGroup = new NioEventLoopGroup();

        try {

            //启动类
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            //传入线程组 链式调用
            serverBootstrap.group(bossGroup,workGroup)
                    .channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel ch) throws Exception {
                    //一整串的handler串联起来
                    ch.pipeline().addLast(new EchoServerHandler());

                }
            });

            System.out.println("Echo 服务器启动ing");

            //绑定端口 同步等待             异步的 但需要等待绑定
            ChannelFuture channelFuture =serverBootstrap.bind(port).sync();
            //等待服务端监听端口关闭
            channelFuture.channel().closeFuture().sync();

        }catch (Exception e){
            e.printStackTrace();
        }finally {

            //释放线程池 优雅退出
            workGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }




    }


}

Step2:服务端处理器

package net.jhclass.echo;

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


/**
 * 处理器
 */
public class EchoServerHandler extends ChannelInboundHandlerAdapter {


    /**
     * 数据读写
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        //转换
        ByteBuf data = (ByteBuf) msg;

        System.out.println("服务端收到数据:"+data.toString(CharsetUtil.UTF_8));

        //回写
        ctx.writeAndFlush(data);
    }

    /**
     * 读写完成
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {

        System.out.println("EchoServerHandler channelReadComplete");

    }

    /**
     * 异常捕获
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {

        cause.printStackTrace();
        ctx.close();

    }
}

Step3:客户端

package net.jhclass.echo;

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;

import java.net.InetSocketAddress;

public class EchoClient {

    private int port;

    private String host;

    private EchoClient(String host,int port){
        this.host = host;
        this.port = port;
    }

    public static void main(String [] args) throws InterruptedException {

        new EchoClient("127.0.0.1",8080).start();


    }

    /**
     * 开启连接
     */
    public void start() throws InterruptedException {

        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)

                    .channel(NioSocketChannel.class)

                    .remoteAddress(new InetSocketAddress(host,port))

                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new EchoClientHandler());
                        }
                    });
            //连接到服务端 connect是异步连接,在调用同步等待sync,等待连接成功
            ChannelFuture channelFuture = bootstrap.connect().sync();
            //阻塞直到客户端通道关闭
            channelFuture.channel().closeFuture().sync();
        }finally {
            //优雅退出 释放NIO线程组
            group.shutdownGracefully();
        }
    }
}

Step4:客户端处理器

package net.jhclass.echo;

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

public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {

    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf msg) throws Exception {
        //打印服务端发送的数据
        System.out.println("Client received:"+msg.toString(CharsetUtil.UTF_8));
    }

    /**
     * channe激活监听
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {

        System.out.println("Active");

        //向服务端写数据
        //writ需要先刷到管道里 flush才是推过去
        ctx.writeAndFlush(Unpooled.copiedBuffer("jhclass.net",CharsetUtil.UTF_8));
    }

    /**
     * 数据处理完后回调
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        System.out.println("EchoClientHandler channelReadComplete");
    }

    /**
     * 异常捕获
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

Step5:测试

1.首先启动EchoServer,等待接收请求中
在这里插入图片描述

2.启动EchoClient客户端发送 jhclass.net 可以看到执行顺序 Active监听器最先执行

在这里插入图片描述

3.EchoServer收到数据

在这里插入图片描述
客户端停掉后这段是不是很眼熟 zookeeper底层就是使用的Netty

在这里插入图片描述

流程分析

1)EcentLoop和EventLoopGroup

就是两个线程组 先这样去理解

2)Bootstrapt启动引导类

这个可不是前端的框架 任何一个系统都需要启动的过程,这个过程需要配置文件,预加载等 所以Server端需要这个,并且将两个线程组给它

3)Channel 生命周期 状态变化

客户端和服务端建立的连接 Socket连接 建立>连接成功>读取数据

4)ChannelHandler和ChannelPipline

ChannelHandler:Channel对应处理类,比如这个连接需要做日志打点,处理返回等

ChannelPipline:Handler打比方就是一个工人,Pipline就是流水线,你可以往里面加很多Handler

博主Netty专栏对上面类有重点分析文章 不懂的可以去看一看

猜你喜欢

转载自blog.csdn.net/q736317048/article/details/113786449