Java架构学习(十)NIO同步阻塞IO&NIO同步非阻塞&Netty入门&Netty实现客户端&netty实现服务端 基于netty3的案例

NIO高级编程与Netty入门

一、NIO同步阻塞IO&NIO非阻塞IO

IO(BIO)与NIO的区别:其本质就是阻塞IO和非阻塞IO的区别。
我们所用的IO都是同步阻塞式IO也叫BIO

阻塞:应用在获取网络数据的时候,如果网络传输速率很慢,就会一直等待直到
    传输完成为止。
非阻塞:应用程序可以直接获取已经准备好的数据。无需等待。

IO:为同步阻塞
NIO:为同步非阻塞,NIO并没有实现异步,在jdk1.7之后支持异步非阻塞通讯。
NIO2.0(AIO)

BIO(IO):同步阻塞式IO
NIO:同步非阻塞式IO
AIO:异步非阻塞式IO


解决问题:同步阻塞IO的办法? 
解决办法:将阻塞的那下面代码放到多线程里面。
    1、使用多线程,也称为伪异步阻塞
    2、使用线程池
伪异步形式缺点:
    1、没有真正解决阻塞IO核心。
    2、创建太多线程,消耗内存。

NIO:同步非阻塞、用于网络相关的。

NIO使用selected选择器来实现同步非阻塞的。

这里写图片描述

非阻塞IO:数据准备就绪之后,由选择器通知给服务器,
数据在准备之前服务器无需等待。

阻塞IO:就是服务器会一直等待客户端传来的数据。

二、NIO非阻塞 客户端与服务端 代码

package com.leeue.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Date;
import java.util.Iterator;

/**
 * 
 * @classDesc: 功能描述:(实现nio客户端与服务端  写的是TCP协议的)
 * @author:<a href="[email protected]">李月</a>
 * @Version:v1.0
 * @createTime:2018年7月30日 下午3:57:14
 */
// nio客户端
class NioCliet {
    public static void main(String[] args) throws IOException {
        System.out.println("客户端已经被启动了...");
        // 1.创建socket通道
        SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 8080));
        // 2、切换为异步非阻塞
        sChannel.configureBlocking(false);// jdk1.7以上才有,设置成异步非阻塞
        // 3、指定缓冲区大小
        ByteBuffer buff = ByteBuffer.allocate(1024);
        buff.put(new Date().toString().getBytes());
        // 4.切换读取模式
        buff.flip();
        sChannel.write(buff);
        buff.clear();
        // 5、关闭通道
        sChannel.close();
    }
}

// NIO服务器端
class NIOServer {
    public static void main(String[] args) throws IOException {
        System.out.println("服务器端已被启动...");
        // 1、创建服务器通道
        ServerSocketChannel sChannel = ServerSocketChannel.open();
        // 2、设置成异步非阻塞
        sChannel.configureBlocking(false);
        // 3、绑定连接
        sChannel.bind(new InetSocketAddress(8080));
        // 4、获取选择器
        Selector selector = Selector.open();
        // 5、将通道注册到选择器中,并监听已经接受到的事件
        sChannel.register(selector, SelectionKey.OP_ACCEPT);
        // 6、轮训获取已经准备就绪的事件
        while (selector.select() > 0) {
            // 7、获取当前选择器有注册的已经监听到的事件
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()) {
                // 8、获取准备就绪的事件
                SelectionKey sk = iterator.next();
                // 9.判断事件准备就绪
                if (sk.isAcceptable()) {
                    // 10.若接受就绪,获取客户端连接
                    SocketChannel socketChannel = sChannel.accept();
                    // 11.设置为阻塞模式
                    socketChannel.configureBlocking(false);// 异步非阻塞IO
                    // 12.将该通道注册到服务器上
                    socketChannel.register(selector, SelectionKey.OP_READ);

                } else if (sk.isReadable()) {
                    // 13.获取当前选择器 是就绪状态的通道
                    SocketChannel socketChannel = (SocketChannel) sk.channel();
                    //14.读取数据
                    ByteBuffer  byteBuffer = ByteBuffer.allocate(1024);
                    int len = 0;
                    while((len=socketChannel.read(byteBuffer))>0) {
                        byteBuffer.flip();
                        System.out.println(new String(byteBuffer.array(),0,len));
                        byteBuffer.clear();
                    }
                }
                iterator.remove();
            }

        }
    }
}

public class NIOCSDemo01 {

}
选择KEY
1、SelectionKey.OP_CONNECT
2、SelectionKey.OP_ACCEPT
3、SelectionKey.OP_READ
4、SelectionKey.OP_WRITE
如果你对不止一种事件感兴趣,那么可以用“位或”操作符将常量连接起来,如下:
int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE;
在SelectionKey类的源码中我们可以看到如下的4中属性,
四个变量用来表示四种不同类型的事件:可读、可写、可连接、可接受连接.

三、Netty入门

什么是Netty?
netty是基于Java NIO 类库实现的异步通讯框架,它的特点:异步非阻塞、基于事件
驱动,性能高,高可靠性和高可定制性。

netty应用场景?
1、分布式开源框架中。
dubbo zookeeper rocketMQ底层的rpc通讯就是使用netty
2.游戏开发中底层使用netty通讯。

为什么使用netty?
解决传统的NIO非阻塞代码的bug问题,进行一些封装,因为传统的,NIO代码比较
复杂。netty使用的事件驱动。
netty:是一个异步通讯框架,底层对NIO进行封装了,NIO框架也是一个通讯框架。

使用netty写的服务端程序代码,并证明netty不是阻塞一直等待的

package com.leeue.netty;

import java.net.InetSocketAddress;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.handler.codec.string.StringDecoder;
import org.jboss.netty.handler.codec.string.StringEncoder;
class ServerHanlder extends SimpleChannelHandler{
    /**
     * 通道关闭的时候触发
     */
    @Override
    public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
        // TODO Auto-generated method stub
        super.channelClosed(ctx, e);
        System.out.println("channelClosed");
    }
    /**
     * 必须要建立连接,然后关闭通道的时候才会触发
     */
    @Override
    public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
        // TODO Auto-generated method stub
        super.channelDisconnected(ctx, e);
        System.out.println("channelDisconnected");
    }
    /**
     * 接受出现异常
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
        // TODO Auto-generated method stub
        super.exceptionCaught(ctx, e);
        System.out.println("exceptionCaught");
    }
    /**
     * 接受客户端数据
     */
    @Override
    public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
        // TODO Auto-generated method stub
        super.messageReceived(ctx, e);
        System.out.println("messageReceived");
        System.out.println("服务器端获取客户端发来的参数:"+e.getMessage());
    }
}
public class NettyServer {
    public static void main(String[] args) {
        //1.创建服务对象
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        //2.创建两个线程池 第一个作用监听端口号,第二个作用nio监听的,
        ExecutorService boos = Executors.newCachedThreadPool();
        ExecutorService wook = Executors.newCachedThreadPool();
        //3.将线程池放入到工程中
        serverBootstrap.setFactory(new NioServerSocketChannelFactory(boos, wook));
        //4.设置管道工程
        serverBootstrap.setPipelineFactory(new ChannelPipelineFactory() {
            //设置这样的管道
            public ChannelPipeline getPipeline() throws Exception {

                ChannelPipeline pipeline = Channels.pipeline();
                //传输数据的时候直接为string类型
                pipeline.addLast("decoder", new StringDecoder());
                pipeline.addLast("encode",new StringEncoder());
                pipeline.addLast("serverHanlder", new ServerHanlder());
                return pipeline;
            }
        });;
        //绑定端口号
        serverBootstrap.bind(new InetSocketAddress(8080));
        System.out.println("服务器端已经被启动.......");

        while(true) {
            try {
                Thread.sleep(1000);
                System.out.println("netty没有阻塞嘻嘻");
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        }
    }
}

这里写图片描述

使用netty实现的客户端

package com.leeue.netty;

import java.net.InetSocketAddress;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.handler.codec.string.StringDecoder;
import org.jboss.netty.handler.codec.string.StringEncoder;
/**
 * 
 * @classDesc: 功能描述:(使用netty实现的客户端)
 * @author:<a href="[email protected]">李月</a>
 * @Version:v1.0
 * @createTime:2018年7月30日 下午5:25:14
 */
class ClientHanlder extends SimpleChannelHandler{
    /**
     * 通道关闭的时候触发
     */
    @Override
    public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
        // TODO Auto-generated method stub
        super.channelClosed(ctx, e);
        System.out.println("channelClosed");
    }
    /**
     * 必须要建立连接,然后关闭通道的时候才会触发
     */
    @Override
    public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
        // TODO Auto-generated method stub
        super.channelDisconnected(ctx, e);
        System.out.println("channelDisconnected");
    }
    /**
     * 接受出现异常
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
        // TODO Auto-generated method stub
        super.exceptionCaught(ctx, e);
        System.out.println("exceptionCaught");
    }
    /**
     * 接受客户端数据
     */
    @Override
    public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
        // TODO Auto-generated method stub
        super.messageReceived(ctx, e);
        System.out.println("messageReceived");
        System.out.println("服务器端向客户端回复的内容:"+e.getMessage());
    }
}
public class NettyClient {
    public static void main(String[] args) {
        //1.创建服务对象
        ClientBootstrap clientBootstrap = new ClientBootstrap();
        //2.创建两个线程池 第一个作用监听端口号,第二个作用nio监听的,
        ExecutorService boos = Executors.newCachedThreadPool();
        ExecutorService wook = Executors.newCachedThreadPool();
        //3.将线程池放入到工程中
        clientBootstrap.setFactory(new NioClientSocketChannelFactory(boos, wook));
        //4.设置管道工程
        clientBootstrap.setPipelineFactory(new ChannelPipelineFactory() {
            //设置这样的管道
            public ChannelPipeline getPipeline() throws Exception {

                ChannelPipeline pipeline = Channels.pipeline();
                //传输数据的时候直接为string类型
                pipeline.addLast("decoder", new StringDecoder());
                pipeline.addLast("encode",new StringEncoder());
                pipeline.addLast("clientHanlder", new ClientHanlder());
                return pipeline;
            }
        });
        //绑定端口号
        ChannelFuture connect = clientBootstrap.connect(new InetSocketAddress("127.0.0.1", 8080));
        System.out.println("客户端已经被启动.......");
        Channel channel = connect.getChannel();
        Scanner scanner = new Scanner(System.in);
        while(true) {
            System.out.println("请输入内容");
            channel.write(scanner.nextLine());
        }

    }
}
非阻塞IO:服务器端只有在拿到客户端传来的数据执行客户端的,服务器端不会等待。
阻塞IO:服务期端会一直等待客户端传数据。服务器端不能干其他事
同步非阻塞:同步表示在管道里面获取的时候是单线程获取值
异步非阻塞:异步是从多个单独线程去获取值

IO与NIO区别?
NIO:有通道、非阻塞、缓冲区 NIO是面向缓冲区的,还有选择器

非阻塞IO与阻塞IO?

netty应用场景?

猜你喜欢

转载自blog.csdn.net/leeue/article/details/81286289