Java--网络编程之Netty

一、什么是Netty
Netty是一个JAVA开源框架,提供异步、事件驱动的网络应用程序框架和工具。
二、Netty组件
1、Bootstrap
启动辅助器,Netty客户端和服务器的入口,Bootstrap是创建客户端连接的启动器,ServerBootstrap是监听服务端端口的启动器。
2、EventLoop
(1)类似线程,一个EventLoopGroup包含一个或者多个EventLoop。
(2)通过EventLoopGroup(Bootstrap启动时会设置EventLoopGroup)生成。
(3)一个EventLoop在它的生命周期内只和一个Thread绑定。
(4)所有由EventLoop处理的IO事件都将在它专有的Thread上被处理。
(5)一个Channel在它的生命周期内只注册于一个EventLoop。
(6)一个EventLoop可能会被分配一个或多个Channel。
(7)EventLoop内部有一个无限循环,维护了一个selector,处理所有注册到selector上的IO操作,在这里实现了一个线程维护多条连接的工作。
3、ByteBuf
(1)提供了对TCP、UDP和文件传输的支持。
(2)使用更高效的socket底层,对epoll空轮询引起的CPU占用飙升在内部进行了处理,避免了直接使用NIO的陷阱,简化了NIO的处理方式。
(3)采用多种decoder/encoder 支持,对TCP粘包/分包进行自动化处理。
(4)可使用线程池,提高连接效率。
(5)可配置IO线程数、TCP参数, TCP接收和发送缓冲区使用直接内存代替堆内存,通过内存池的方式循环利用ByteBuf。
(6)通过引用计数器及时申请释放不再引用的对象,降低GC频率。
(7)使用单线程串行化的方式,是高效的Reactor线程模型。
(8)大量使用了volitale、CAS和原子类、线程安全类、读写锁。
4、Channel
(1)常用的是NioServerSocketChannel和NioSocketChannel。
(2)NioServerSocketChannel负责监听一个tcp端口,有连接进来通过boss reactor创建一个NioSocketChannel将其绑定到worker reactor,然后worker reactor负责这个NioSocketChannel的读写等IO事件。
5、ChannelPipeline
(1)ChannelPipeline内部有两个节点:head和tail,分别对应着ChannelHandler链的头和尾。
(2)write、read、connect等所有IO操作都会通过ChannelPipeline。
(3)依次通过ChannelPipeline上面的ChannelHandler处理,是Netty模型的核心。
6、ChannelHandler
(1)Netty中真正的IO事件处理单元,可以创建自己的ChannelHandler来处理自己的逻辑,完全控制事件的处理方式。
(2)ChannelHandler分为inBound和outBound,分别对应IO的read和write执行链。
(3)ChannelHandler和ChannelPipeline组成责任链,使得一组ChannelHandler像一条链一样执行下去。
(4)ChannelHandler用ChannelHandlerContext包裹着,有prev和next节点,可以获取前后ChannelHandler,read时从ChannelPipeline的head执行到tail,write时从tail执行到head,所以head既是read事件的起点也是write事件的终点。
三、特点
1、统一的API,适用于不同的协议。
2、基于灵活、可扩展的事件驱动模型。
3、高度可定制的线程模型。
4、可靠的无连接数据Socket支持。
5、更好的吞吐量,低延迟,更省资源,减少不必要的内存拷贝。
6、安全易用。
四、使用Netty
1、引入依赖

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

2、服务端编程
(1)具体流程
实例化两个EventLoopGroup事件循环组,一个boss事件循环组用于接收客户端连接,一个worker事件循环组用于处理连接;
实例化服务端的启动辅助类ServerBootstrap,配置group组、指定主事件循环组的channel类型、worker处理的chanlerHandler;
启动服务端,serverBootstrap.bind().sync(); 其中sync()方法保证服务端完全启动(否则会阻塞)。
(2)代码实现
EchoNettyServerDemo类:

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

public class EchoNettyServerDemo {
    public static void main(String[] args) {
        //实例化EventLoopGroup
        //服务端实例化两个事件循环组
        //其中Boss事件循环组用于接收客户端连接(accept)
        //Worker事件循环组用于处理连接(channel交给worker循环组)
        //reactor网络模型
        EventLoopGroup boss=new NioEventLoopGroup();
        EventLoopGroup worker=new NioEventLoopGroup(5);
        try {
        //实例化服务端的启动辅助类(服务端相关配置、操作都交给启动辅助类)
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        serverBootstrap
                //配置事件循环组
                .group(boss,worker)
                //指定主事件循环组
                .channel(NioServerSocketChannel.class)
                //自定义childHandler实现具体的业务处理逻辑
                .childHandler(new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel ch) throws Exception {
                        //pipeline是容器,所有的channel通过该容器
                        ChannelPipeline pipeline=ch.pipeline();
                        //编码解码
                        pipeline.addLast(new StringDecoder());
                        pipeline.addLast(new StringEncoder());
                        //自定义channelHandler
                        pipeline.addLast(new EchoServerHandler());
                    }
                });
            //通过sync方法启动服务端,作用是同步阻塞直至服务端完全启动(这步操作才结束)否则被阻塞,一句话,为了完全启动
            ChannelFuture sync = serverBootstrap.bind(8888).sync();
            System.out.println("服务端启动");

            //关闭channel
            //关闭操作会阻塞,直到通道完全关闭
            sync.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            boss.shutdownGracefully();
            worker.shutdownGracefully();
        }
    }
}

EchoServerHandler类:

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import java.net.SocketAddress;

//接收消息的channelHandler需要实现ChannelInboundHandler接口
//该接口下提供了很多方法,只需实现方法就可以接收消息
//可以在自定义的类中进行重写以实现特定需求
public class EchoServerHandler extends SimpleChannelInboundHandler<String> {
    //父类是一个抽象方法,子类要进行重写
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, String s) throws Exception {

    }
    //重写父类的channelRead方法用来接收数据
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println(ctx.channel().remoteAddress() + "发送消息:" + msg);
        //接收消息并返回给客户端
        ctx.writeAndFlush("echo:" + msg);
    }
    //客户端下线
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        SocketAddress socketAddress = ctx.channel().remoteAddress();
        System.out.println(socketAddress+"客户端下线");
    }
    //客户端上线
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        SocketAddress socketAddress = ctx.channel().remoteAddress();
        System.out.println(socketAddress+"客户端上线");
    }
}

3、客户端编程
(1)具体流程
实例化事件循环组EventLoopGroup组;
实例化启动服务类Bootstrap,配置group组、指定channel类型、配置channelHandler;
同步连接服务端;
向服务端发送消息。
(2)代码实现
EchoNettyClientDemo类:

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import java.util.Scanner;

public class EchoNettyClientDemo {
    public static void main(String[] args) {
        //启动事件循环组
        NioEventLoopGroup loopGroup = new NioEventLoopGroup();
        //启动辅助类
        Bootstrap bootstrap = new Bootstrap();
        try {
        //配置启动辅助类
        Bootstrap channel = bootstrap
                //配置事件循环组
                .group(loopGroup)
                //指定通道类型
                .channel(NioSocketChannel.class)
                //配置通道
                .handler(new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel ch) throws Exception {
                        ChannelPipeline pipeline = ch.pipeline();
                        //编码解码
                        pipeline.addLast(new StringDecoder());
                        pipeline.addLast(new StringEncoder());
                        //接受服务端返回的消息
                        //自定义Handler
                        pipeline.addLast(new EchoClientHandler());
                    }
                });
            //客户端连接服务端
            Channel channel1 = bootstrap.connect("127.0.0.1", 8888).sync().channel();
            System.out.println("客户端已连接上");
            //channel1.writeAndFlush("hello");
            while(true) {
                System.out.println("请输入:");
                Scanner s = new Scanner(System.in);
                String msg = s.nextLine();
                channel1.writeAndFlush(msg);
                if("exit".equals(msg)){
                    break;
                }
            }
            //关闭通道
            channel1.closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            loopGroup.shutdownGracefully();
        }
    }
}

EchoClientHandler类:

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

public class EchoClientHandler extends SimpleChannelInboundHandler {
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, Object o) throws Exception {

    }

    //重写方法用于接收消息
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        //打印服务端返回的消息
        System.out.println(msg);
    }
}

3、运行结果
先启动EchoNettyServerDemo类后启动EchoNettyClientDemo类,然后在客户端输入消息,服务端可接收到。
在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值