Netty教程02:Netty实战之TCP服务

源码地址:https://gitee.com/pidaner/netty-class

官网:https://netty.io/

Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients

Netty是一个异步事件驱动的网络应用程序框架
快速开发可维护的高性能协议服务器和客户端
在这里插入图片描述

netty分为三部分

netty的自我介绍

  • Netty 是由 JBOSS 提供的一个 Java 开源框架。Netty
    提供异步的、基于事件驱动的网络应用程序框架,用以快速开发高性能、高可靠性的网络 IO 程序
  • Netty 可以帮助你快速、简单的开发出一个网络应用,相当于简化和流程化了 NIO 的开发过程
  • Netty 是目前最流行的 NIO 框架,Netty 在互联网领域、大数据分布式计算领域、游戏行业、通信行业等获得了广泛的应用,知名的Elasticsearch 、Dubbo 框架内部都采用了 Netty。
  • netty 下载地址: https://bintray.com/netty/downloads/netty/

Reactor理解

  • 单 Reactor 单线程,前台接待员和服务员是同一个人,全程为顾客服务
  • 单 Reactor 多线程,1 个前台接待员,多个服务员,接待员只负责接待
  • 主从 Reactor 多线程,多个前台接待员,多个服务生

reactor架构图
在这里插入图片描述
结构图解释

  1. Netty抽象出两组线程池 BossGroup 专门负责接收客户端的连接, WorkerGroup 专门负责网络的读写
  2. BossGroup 和 WorkerGroup 类型都是 NioEventLoopGroup
  3. NioEventLoopGroup 相当于一个事件循环组, 这个组中含有多个事件循环 ,每一个事件循环是 NioEventLoop
  4. NioEventLoop 表示一个不断循环的执行处理任务的线程, 每个NioEventLoop 都有一个selector ,
    用于监听绑定在其上的socket的网络通讯
  5. NioEventLoopGroup 可以有多个线程, 即可以含有多个NioEventLoop
  6. 每个Boss NioEventLoop 循环执行的步骤有3步
  • 轮询accept 事件
  • 处理accept 事件 , 与client建立连接 , 生成NioScocketChannel , 并将其注册到某个worker,NIOEventLoop 上的 selector
  • 处理任务队列的任务 , 即 runAllTasks
  1. 每个 Worker NIOEventLoop 循环执行的步骤
  • 轮询read, write 事件
  • 处理i/o事件, 即read , write 事件,在对应NioScocketChannel 处理
  • 处理任务队列的任务 , 即 runAllTasks
  1. 每个Worker NIOEventLoop 处理业务时,会使用pipeline(管道), pipeline 中包含了channel , 即通过pipeline 可以获取到对应通道, 管道中维护了很多的处理器

Netty入门实例:TCP服务

实例要求:使用IDEA 创建Netty项目
Netty 服务器在 6668 端口监听,客户端能发送消息给服务器 “hello, 服务器~”
服务器可以回复消息给客户端 “hello, 客户端~”
目的:对Netty 线程模型 有一个初步认识, 便于理解Netty 模型理论
编写服务端、编写客户端,对netty 程序进行分析,看看netty模型特点
说明: 创建Maven项目,并引入Netty 包

1、创建maven项目,导入netty依赖

在这里插入图片描述

2、服务端

package com.lian.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.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;

public class Server {
    public static void main(String[] args) throws Exception {

        /**
         * 1、创建两个线程组 BossGroup 和 workerGroup
         * 2、bossGroup 只处理连接请求,workerGroup负责客户端业务处理
         * 3、两个都是无限循环
         */
        //门口迎宾的妹子们
        NioEventLoopGroup bossGroup = new NioEventLoopGroup();
        //店内负责端菜倒水的伙计们
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            //创建服务器端的启动对象,配置参数
            ServerBootstrap bootstrap = new ServerBootstrap();
            //使用链式编程来进行设置
            bootstrap
                    //设置两个线程组
                    .group(bossGroup, workerGroup)
                    //使用NioServerSocketChannel作为通道类型实现
                    .channel(NioServerSocketChannel.class)
                    //设置线程队列得到连接个数
                    .option(ChannelOption.SO_BACKLOG,128)
                    //设置保持活动连接状态
                    .childOption(ChannelOption.SO_KEEPALIVE,true)
                    //给workerGroup的eventLoop线程对应的管道设置处理器
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        //初始化一个通道
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            //通道上有很多处理器,都由pipeline管道统一管理
                            ChannelPipeline pipeline = ch.pipeline();
                            //管道里添加处理器
                            pipeline.addLast(new ServerHandler());
                        }
                    });
            System.out.println("server is prepare...");
            //绑定一个端口,并且同步,作用是启动服务器
            ChannelFuture cf = bootstrap.bind(6666).sync();
            //对关闭通道进行监听
            cf.channel().closeFuture().sync();
        }finally {
            //优雅的关闭线程
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }

    }
}

3、服务端处理器类

package com.lian.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;

/**
 * 自定义一个handler,需要继承netty 绑定好的某个 handlerAdapter
 * 这时我们自定义一个handler,才能称位一个hanler
 */
public class ServerHandler extends ChannelInboundHandlerAdapter {

    /**
     * 2、服务器端读取到客户端发送过来的消息
     *
     * 读取客户端发送的消息
     * @param ctx 上下文对象,含有 管道pipeline、通道channel
     * @param msg 客户端发送的数据
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("server ctx =" + ctx);
        //将客户端消息msg 转换为ByteBuf类型,此bytebuf是netty提供的,性能更高
        ByteBuf buf = (ByteBuf) msg;
        //
        System.out.println("client send message: " + buf.toString(CharsetUtil.UTF_8));
        System.out.println("client send address: " + ctx.channel().remoteAddress());
    }

    /**
     * 3、服务器端读取完毕客户端发过来的消息后,也回复了客户端的消息
     *
     * 读取客户端数据后,给客户端回复消息
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        //将数据写到缓存区ButeBuf中,并且传到通道里,write+flush
        //unPooled: Netty 提供一个专门用来操作缓冲区(即 Netty 的数据容器)的工具类
        ctx.writeAndFlush(Unpooled.copiedBuffer("hello client",CharsetUtil.UTF_8));
    }

    /**
     * 发生异常时,需要关闭通道
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        //关闭通道,拿到通道再关闭
        ctx.channel().close();
    }
}

4、客户端

package com.lian.simple;

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

public class Client {
    public static void main(String[] args) throws Exception {
        NioEventLoopGroup clientEventLoop = new NioEventLoopGroup();
        try{
            //创建客户端启动对象
            Bootstrap bootstrap = new Bootstrap();
            //设置相关参数
            bootstrap
                    //设置线程组
                    .group(clientEventLoop)
                    //设置客户端通道的实现类
                    .channel(NioSocketChannel.class)
                    //处理器
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            pipeline.addLast(new ClientHandler());
                        }
                    });
            System.out.println("client is ok");
            //启动客户端连接服务器端
            ChannelFuture cf = bootstrap.connect("127.0.0.1", 6666).sync();
            //监听关闭通道,监听啥时候关闭
            cf.channel().closeFuture().sync();
        }finally {
            //优雅的关闭
            clientEventLoop.shutdownGracefully();
        }
    }
}

5、客户端处理器

package com.lian.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;

/**
 * 处理器必须要继承 ChannelInboundHandlerAdapter
 */
public class ClientHandler extends ChannelInboundHandlerAdapter {

    /**
     * 1、客户端是active的,主动先和服务器端打招呼
     *
     * Http协议是只能客户端和服务器联系,服务器然后回复,服务器不能主动联系到客户端
     * websocket协议是客户端和服务器端可互相联系到对方,实现平等通信
     *
     * 当通道就绪就会触发该方法
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("client" + ctx);
        //将数据写到缓存区,再传到通道里
        ctx.writeAndFlush(Unpooled.copiedBuffer("hello server", CharsetUtil.UTF_8));

    }

    /**
     * 4、客户端收到服务器端的消息
     *
     * 当通道有读取事件时,会触发
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf buf = (ByteBuf) msg;
        System.out.println("server answer: "+buf.toString(CharsetUtil.UTF_8));
        System.out.println("server address: "+ctx.channel().remoteAddress());
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        //打印异常信息
        cause.printStackTrace();
        //关闭通道
        ctx.channel().close();
    }
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值