高性能网络通信技术--java NIO

一.NIO产生的背景

BIO,输入输出(原始)
NIO(Not-blocking IO) 非阻塞的IO模型

          channel高速公路
(selector选择器)客户端 --------------------------> 服务端 多路复用技术

主要目的:提高程序的性能,借鉴了一些先进的思想

BIONIO都是 同步IO

二.必须明白的几个概念


1).阻塞(Block)和非阻塞(NoN-block)  都是面向数据而言
     阻塞:数据没有准备好,程序就一直等待,直到数据准备好之后才往下执行
     非阻塞:不管我的数据有没有准备好,程序都往下执行
 
2).同步和异步 相对于我们的IO事件而言的
     同步:当程序处理IO的时候,程序不能去干别的事情的,要等着IO事件完成之后才能去做别的事情
     异步:不关心IO处理过程,只要在处理IO的时候,可以去做别的事情,然后等待IO事件处理完成的通知

三.java现有的IO模型

BIO JDK1.4以前的IO模型,阻塞IO 
 
NIO JDK1.4以后新增的IO模型,非阻塞IO,借鉴Linux的多路复用技术,轮询机制
 
AIO         JDK1.7以后才有的IO模型,真正的异步处理,把IO读写操作完全交给我们的操作系统,学习Linux epoll模式

四.java NIO原理的解读
   
1).多路复用(Channel通道) 相当于是高速公路

2).轮询机制(Seletor选择器)

    Client(客户) (Selector)管家 Boss(老板)
   客人跟管家说,我要找你们老板帮忙,管家就跟客人说,我们老板现在比较忙,说你先坐会儿,先喝杯茶. (注册行为),给客户一个牌号(SelectionK),001号,过来,现在老板有空了,可以处理了,处理完之后,牌号要回收,给下一个人用,客户就可以打发走了. 

          一定要等IO事件做完之后,客户才能走 (同步)

        3). SelectionKey  相当于是时间标签

        4).Buffer(数据缓冲区)

        5).NIO常用API
   
Channel:

服务端: ServerSocketChannel BIO:ServerSocket
客户端: SocketChannel BIO:Socket

open(); //修路,把高速公路修起来

channel.regist(selector,注册的标识)

Selector 选择器
open();                    //开始营业
selectedKeys(); //获取到当前已经注册了的所有牌号信息

Buffer
put();   //往缓冲区里面写数据
get();   //读数据
flip();  //切换读写模式
clear(); //清空缓冲区

SelectionKey  牌号
isAccptable() //是否可以接受客户端连接
isConnctionable() //是否已经连接
isReadable() //缓冲区是否可读
isWriteable() //是否可写


五.java AIO简介  异步非阻塞IO

现在还没有流行起来,了解即可

服务端: AsynchronousServerSocketChannel
客户端: AsynchronousSocketChannel

CompletionHandler:通知程序,IO操作是否成功,还是失败  

六.编写代码并演示

BIO: 阻塞IO 同步

package com.yz.io.bio;

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * Created by yz on 2018/2/5.
 */
public class BioServer {

    ServerSocket server ;
    /**
     * 构造一个服务端
     * @param port
     */
    public BioServer(int port){
        try {
            server = new ServerSocket(port);
            System.out.println("Bio服务已启动,监听端口是: "+port);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 监听客户端过来的请求
     */
    public void listener() throws IOException {
        while (true){
            Socket client = server.accept();
            InputStream is = client.getInputStream();
            byte[] buff = new byte[1024];//缓冲区
            int len = is.read(buff);
            if(len > 0 ){
                String msg = new String(buff,0,len);
                System.out.println("接收到客户端发来的消息:"+msg);
            }
        }
    }

    public static void main(String[] args) throws IOException {
        new BioServer(8080).listener();
    }
}


package com.yz.io.bio;

import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;

/**
 * Created by yz on 2018/2/5.
 */
public class BioClient {

    public static void main(String[] args) throws IOException {
        Socket client = new Socket("localhost",8080);
        /**
         * 获取一个输出流
         */
        OutputStream os = client.getOutputStream();
        os.write("报个到".getBytes());
        os.close();
        client.close();
    }
}

NIO: 非阻塞IO  selector+非阻塞 同步 常用框架: Mina2.0 Netty5.0 

package com.yz.io.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.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
 * Created by yz on 2018/2/5.
 */
public class NioServer {


    int port = 8080;
    ServerSocketChannel server;
    Selector selector;
    //缓冲区
    ByteBuffer receiveBuffer = ByteBuffer.allocate(1024);
    ByteBuffer sendBuffer = ByteBuffer.allocate(1024);

    Map<SelectionKey,String> sessionMsg = new HashMap<>();

    public NioServer(int port) throws IOException {
        /**
         * 把高速公路修起来
         */
        server = ServerSocketChannel.open();
        /**
         * 关卡也打开了,可以多路复用了
         */
        server.socket().bind(new InetSocketAddress(this.port));
        /**
         * 默认是阻塞的,手动设置为非阻塞,它才是非阻塞
         */
        server.configureBlocking(false);
        /**
         * 管家开始营业
         */
        selector = Selector.open();

        /**
         * 告诉管家,Boss已经准备就绪,等会有客人来了,要通知我一下
         */
        server.register(selector, SelectionKey.OP_ACCEPT);

        System.out.println("nio服务已经启动,监听端口是:"+this.port);

    }

    public void listener() throws IOException {
        /**
         * 轮询
         */
        while (true){
            //判断一下,当前有没有客户来注册,有没有排队的,有没有取号的
           int i = selector.select();
            if(i == 0) continue;
            Set<SelectionKey> keys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = keys.iterator();
            while (iterator.hasNext()){
                //来一个处理一个
                process(iterator.next());
                //处理完之后打发走
                iterator.remove();
            }
        }
    }


    /**
     * 处理数据的方法
     */
    private void process(SelectionKey key) throws IOException {
        //首先要判断客户有没有跟我们boss建立好连接
        if(key.isAcceptable()){
            SocketChannel client = server.accept();
            client.configureBlocking(false);
            client.register(selector,SelectionKey.OP_READ);
        }else if(key.isReadable()){
            receiveBuffer.clear();
            //判断是否可以读数据了
            SocketChannel client = (SocketChannel) key.channel();
            int len = client.read(receiveBuffer);
            if(len > 0 ){
                String msg = new String(receiveBuffer.array(),0,len);

                sessionMsg.put(key,msg);
                System.out.println("获取客户端发送来的消息:"+msg);
            }
            //读完之后告诉管家可以写了
            client.register(selector,SelectionKey.OP_WRITE);
        }else if(key.isWritable()){
            //判断是否可以写数据了
            if(!sessionMsg.containsKey(key)){
                return ;
            }

            SocketChannel client = (SocketChannel) key.channel();
            sendBuffer.clear();
            sendBuffer.put(new String(sessionMsg.get(key)+",您好,您的请求已处理完成.").getBytes());
            sendBuffer.flip();//切换读写模式

            client.write(sendBuffer);
            client.register(selector,SelectionKey.OP_READ);
        }
    }

    public static void main(String[] args) throws IOException {
        new NioServer(8080).listener();
    }
}


package com.yz.io.nio;

import java.io.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set;

/**
 * Created by yz on 2018/2/5.
 */
public class NioClient {

    SocketChannel client ;
    InetSocketAddress serverAddress = new InetSocketAddress("localhost",8080);
    Selector selector;

    //缓冲区
    ByteBuffer receiveBuffer = ByteBuffer.allocate(1024);
    ByteBuffer sendBuffer = ByteBuffer.allocate(1024);

    public NioClient() throws IOException {
        //先开路
        client = SocketChannel.open();
        client.configureBlocking(false);
        client.connect(serverAddress);

        selector = Selector.open();

        client.register(selector, SelectionKey.OP_CONNECT);

    }

    public void session() throws IOException {
        //先要判断是否已经建立连接
        if(client.isConnectionPending()){
            client.finishConnect();
            System.out.println("请在控制台登记姓名");
            //告诉管家可以写东西了
            client.register(selector,SelectionKey.OP_WRITE);
        }
        Scanner scan = new Scanner(System.in);
        while (scan.hasNext()){
            String name = scan.nextLine();
            if("".equals(name)){
                continue;
            }
            process(name);
        }
    }

    public void process(String name) throws IOException {
        boolean unFinish = true;
        while (unFinish){
            int i = selector.select();
            if(i == 0) continue;
            Set<SelectionKey> keys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = keys.iterator();
            while (iterator.hasNext()){
                SelectionKey key = iterator.next();
                if(key.isWritable()){
                    sendBuffer.clear();
                    sendBuffer.put(name.getBytes());
                    sendBuffer.flip();

                    client.write(sendBuffer);
                    client.register(selector,SelectionKey.OP_READ);
                }else if(key.isReadable()){
                    receiveBuffer.clear();
                    int len = client.read(receiveBuffer);
                    if(len > 0){
                        receiveBuffer.flip();
                        System.out.println("获取到服务端反馈的消息:"+new String(receiveBuffer.array(),0,len));
                        client.register(selector,SelectionKey.OP_WRITE);
                        unFinish = false;
                    }
                }
            }
        }
    }

    public static void main(String[] args) throws IOException {
        new NioClient().session();
    }

}


AIO: 异步非阻塞IO

package com.yz.io.aio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.concurrent.ExecutionException;


/**
 * Created by yz on 2018/2/5.
 */
public class AioServer {

    AsynchronousServerSocketChannel server;

    ByteBuffer receviceBuff = ByteBuffer.allocate(1024);

    int port = 8080;

    public AioServer(int port) throws IOException {
        this.port = port;
        //要想富,先修路
        server = AsynchronousServerSocketChannel.open();
        //打开关卡
        server.bind(new InetSocketAddress("localhost",this.port));
    }

    public void listener(){
        new Thread(){
            public void run(){
                System.out.println("Aio服务已启动,监听端口:"+port);
                server.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {

                    //成功后的回调
                    @Override
                    public void completed(AsynchronousSocketChannel client, Void attachment) {
                        server.accept(null,this);
                        process(client);
                    }

                    @Override
                    public void failed(Throwable exc, Void attachment) {
                        System.out.println("异步IO失败");
                    }
                    private void process(AsynchronousSocketChannel client){
                        try {
                            receviceBuff.clear();
                            int len = client.read(receviceBuff).get();
                            receviceBuff.flip();
                            System.out.println("已接到客户端发来的消息:"+new String(receviceBuff.array(),0,len));
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                });
                while (true){}
            }
        }.run();
    }

    public static void main(String[] args) throws IOException, InterruptedException {
        new AioServer(8080).listener();
        Thread.sleep(20000);
    }

}

package com.yz.io.aio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.util.Scanner;
import java.util.concurrent.Future;

/**
 * Created by yz on 2018/2/5.
 */
public class AioClient {

    AsynchronousSocketChannel client;

    InetSocketAddress serverAddress = new InetSocketAddress("localhost",8080);

    ByteBuffer sendBuffer = ByteBuffer.allocate(1024);

    public AioClient() throws IOException {
        client = AsynchronousSocketChannel.open();
        Future<?> f = client.connect(serverAddress);
        System.out.println("客户端已启动");
    }

    public void send(String content){
        sendBuffer.clear();
        sendBuffer.put(content.getBytes());
        sendBuffer.flip();
        client.write(sendBuffer);
    }

    public static void main(String[] args) throws IOException {
        AioClient client = new AioClient();
        Scanner sc = new Scanner(System.in);
        while(sc.hasNext()){
            String content  = sc.nextLine();
            client.send(content);
        }
    }

}

七.总结

BIO: 阻塞IO 同步

NIO: 非阻塞IO  同步  selector+非阻塞  常用框架: Mina2.0 Netty5.0

AIO: 异步非阻塞IO



发布了43 篇原创文章 · 获赞 32 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/yz2015/article/details/79444899
今日推荐