Netty简介

Netty

Netty是基于Java NIO的网络应用框架。
Netty是一个NIO client-server(客户端-服务器)框架,使用Netty可以快速开发网络应用,例如服务器和客户端协议。Netty提供一种新的方式来开发网络应用程序,这种新的方式是的它很容易使用和有很强的扩展性。
Netty的内部实现是复杂的,但是Netty提供了简单一用的api从网络处理代码中解耦业务逻辑。
Netty是完全基于NIO实现的,所以整个Netty都是非阻塞的。
网络应用程序通常需要较高的可扩展性,无论是Netty还是其他基于JAVA NIO的框架,都会提供可扩展性的解决方案。Netty中一个关键组成部分是它的异步特性,本章将讨论同步(阻塞)和异步(非阻塞)的IO来说明为什么使用异步代码来解决扩展性问题以及如何使用异步。

在学习Netty之前,先回顾一下NIO的通信步骤:

  1. 创就绪建ServerSocketChannel,为它配置非阻塞模式。
  2. 绑定监听,配置TCP参数,录入backlog大小
  3. 创建一个独立的IO线程,用于轮询多路复用器Selector
  4. 创建Selector,将之前创建的ServerSocketChannel注册到Selector上,并设置监听标志位SelectionKEY.ACCEPT
  5. 启动IO线程,在循环体中执行Selector.select()方法,轮询就绪的通道。
  6. 当轮询到了处于就绪的通道时,需要进行判断操作位,如果是ACCEPT状态,说明是新的客户端接入,则调用accept方法接受新的客户端。
  7. 设置新接入客户端的一些参数,如非阻塞、并将其通道继续注册到Selector之中,设置监听标志位等。
  8. 如果轮询的通道操作位是READ,则进行读取,构造Buffer对象等。
  9. 更细节的还有数据没法送完成继续发送的问题。

Netty实现通信的步骤:

  1. 创建两个NIO线程组,一个专门用于网络事件处理(接受客户端的连接),另一个则进行网络通信读写
  2. 创建一个ServerBootstrap对象,配置Netty的一系列参数,例如接受传出数据的缓存大小等等。
  3. 创建一个实际处理数据的类ChannelInitalizer,进行初始化的准备工作,比如设置接受传出数据的字符集、格式、已经实际处理数据的接口。
  4. 绑定端口,执行同步阻塞方法等待服务器启动即可。

Client.java

public class Client {

    public static void main(String[] args) throws InterruptedException {
        EventLoopGroup workgroup = new NioEventLoopGroup();
        Bootstrap b = new Bootstrap();
        b.group(workgroup)
        .channel(NioSocketChannel.class)
        .handler(new ChannelInitializer<SocketChannel>() {

            @Override
            protected void initChannel(SocketChannel sc) throws Exception {
                sc.pipeline().addLast(new ClientHandler());
            }
        });

        ChannelFuture cf1 = b.connect("127.0.0.1",8765).sync();
//      ChannelFuture cf2 = b.connect("127.0.0.1",8764).sync();
        //buf
        cf1.channel().writeAndFlush(Unpooled.copiedBuffer("hello netty".getBytes()));
        Thread.sleep(1000);
        cf1.channel().writeAndFlush(Unpooled.copiedBuffer("hello netty".getBytes()));
        Thread.sleep(1000);
        cf1.channel().writeAndFlush(Unpooled.copiedBuffer("hello netty".getBytes()));
//      cf1.channel().flush();

        cf1.channel().closeFuture().sync();
        workgroup.shutdownGracefully();
    }
}

Server.java

public class Server {

    public static void main(String[] args) throws InterruptedException {
        //1 第一个线程组用于接受client端连接的
        NioEventLoopGroup bossGroup = new NioEventLoopGroup();
        //2 第二个线程组用于实际的业务处理操作的
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();
        //3 创建一个辅助类Bootstrap,就是对我们的Server进行一系列的配置
        ServerBootstrap b = new ServerBootstrap();
        b.group(bossGroup,workerGroup)//把两个工作线程组加入进来
        .channel(NioServerSocketChannel.class)//使用NIOServerSocketChannel这种类型的通道
        .childHandler(new ChannelInitializer<SocketChannel>() {//使用childHandler绑定具体的事件处理器
            @Override
            protected void initChannel(SocketChannel sc) throws Exception {
                sc.pipeline().addLast(new ServerHandler());
            }
        })
        /**
         * 服务器端TCP内核模块维护两个队列,我们称之为A和B
         * 客户端向服务器端connect的时候 会发送带有SYN标志的包(第一次握手)
         * 服务器收到客户端发来的SYN时,向客户端发送SYN ACK确认(第二次握手)
         * 此时TCP内核模块将客户端连接加入A队列中,然后服务器收到客户端发来的ACK时(第三次握手)
         * TCP内核模块将客户端从A队列移动到B队列,连接完成,应用程序的accept会返回。
         * 也就是说accept从B队列中取出完成三次握手的连接
         * A队列和B队列的长度之和是backlog。当A、B队列的长度之和大于backlog时,新连接将会被TCP内核拒绝
         * 所以,如果backlog过小,可能会出现accept速度跟不上,A、B队列满了,导致新的客户无法连接。
         * 要注意的是:backlog对程序的连接数并无影响,backlog影响的知识还没有被accept取出的连接。
         */
        //设置TCP缓冲区
        .option(ChannelOption.SO_BACKLOG, 128)
        //保持连接
        .childOption(ChannelOption.SO_KEEPALIVE, true);

        //绑定指定的端口进行监听
        ChannelFuture f = b.bind(8765).sync();
//      ChannelFuture f2 = b.bind(8764).sync();
        //将服务器阻塞
        f.channel().closeFuture().sync();
//      f2.channel().closeFuture().sync();

        bossGroup.shutdownGracefully();
        workerGroup.shutdownGracefully();

    }

}

ServerHandler.java

public class ServerHandler extends ChannelHandlerAdapter{

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg)
            throws Exception {
//      ((ByteBuf)msg).release();
        try{
            //do sth msg
            ByteBuf buf = (ByteBuf)msg;
            byte[] data = new byte[buf.readableBytes()];
            buf.readBytes(data);
            String request = new String(data,"utf-8");
            System.out.println("server:"+request);
            //写给客户端
            String response = "我是反馈的消息";
            ChannelFuture channelFuture = ctx.channel().writeAndFlush(Unpooled.copiedBuffer("Hi Client!".getBytes()));
            //写完后断开连接
//          channelFuture.addListener(ChannelFutureListener.CLOSE);
//          ctx.channel().flush();
        }finally{
            ReferenceCountUtil.release(msg);
        }
    }

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

ClientHandler.java

public class ClientHandler extends ChannelHandlerAdapter{

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg)
            throws Exception {
//      ((ByteBuf)msg).release();
        try{
            //do sth msg
            ByteBuf buf = (ByteBuf)msg;
            byte[] data = new byte[buf.readableBytes()];
            buf.readBytes(data);
            String request = new String(data,"utf-8");
            System.out.println("client:"+request);
            //写给服务器端
            /*String response = "我是反馈给服务器的消息";
            ctx.channel().writeAndFlush(Unpooled.copiedBuffer("999".getBytes()));*/
        }finally{
            //读数据必须释放
            ReferenceCountUtil.release(msg);
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
            throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值