一 、服务端程序,超越最大数额的线程就只好等待

《netty权威指南》读书笔记

Java 网络IO编程(BIO、NIO、AIO),nioaio

一、BIO

本概念

图片 1

壹 、服务端程序:

BIO编程

图片 2图片 3

传统的BIO编程

图片 4

代码示例:

图片 5

public class Server {
    final static int PROT = 8765;

    public static void main(String[] args) {
        ServerSocket server = null;
        try {
            server = new ServerSocket(PROT);
            System.out.println(" server start .. ");
            // 进行阻塞
            Socket socket = server.accept();
            // 新建一个线程执行客户端的任务
            new Thread(new ServerHandler(socket)).start();

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (server != null) {
                try {
                    server.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            server = null;
        }
    }
}


public class ServerHandler implements Runnable {

    private Socket socket;

    public ServerHandler(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        BufferedReader in = null;
        PrintWriter out = null;
        try {
            in = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));
            out = new PrintWriter(this.socket.getOutputStream(), true);
            String body = null;
            while (true) {
                body = in.readLine();
                if (body == null)
                    break;
                System.out.println("Server :" + body);
                out.println("服务器端回送响的应数据.");
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (out != null) {
                try {
                    out.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            socket = null;
        }
    }
}


public class Client {
    final static String ADDRESS = "127.0.0.1";
    final static int PORT = 8765;
    public static void main(String[] args) {
        Socket socket = null;
        BufferedReader in = null;
        PrintWriter out = null;

        try {
            socket = new Socket(ADDRESS, PORT);
            in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            out = new PrintWriter(socket.getOutputStream(), true);

            // 向服务器端发送数据
            out.println("接收到客户端的请求数据...");
            // out.println("接收到客户端的请求数据1111...");
            String response = in.readLine();
            System.out.println("Client: " + response);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (out != null) {
                try {
                    out.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            socket = null;
        }
    }
}

View Code

该模型最大的题材尽管贫乏弹性伸缩能力,当客户端并发访问量扩充后,服务端的线程个数和客户端并发访问数呈1:1的正比关系,Java中的线程也是相比尊贵的系统财富,线程数量火速膨胀后,系统的性情将大幅度下跌,随着访问量的持续增大,系统末段就死-掉-了。

 1 package bio;
 2 
 3 import java.io.BufferedReader;
 4 import java.io.IOException;
 5 import java.io.InputStreamReader;
 6 import java.io.PrintWriter;
 7 import java.net.ServerSocket;
 8 import java.net.Socket;
 9 import java.util.Date;
10 
11 public class BioServer {
12 
13     public static void main(String[] args) throws IOException {
14         ServerSocket serverSocket = new ServerSocket(8081);
15         Socket clientSocket = null;
16         while(true){
17             clientSocket = serverSocket.accept();//如果没有客户端接入,主线程阻塞在这里
18             new Thread(new ServerHandler(clientSocket)).start();
19         }
20         //finall关闭serverSocket
21     }
22 
23 }
24 
25 class ServerHandler implements Runnable{
26     private Socket clientSocket;
27 
28     public ServerHandler(Socket clientSocket) {
29         this.clientSocket = clientSocket;
30     }
31 
32     @Override
33     public void run() {
34         try {
35             BufferedReader reader = new BufferedReader(new InputStreamReader(this.clientSocket.getInputStream()));
36             PrintWriter writer = new PrintWriter(this.clientSocket.getOutputStream(), true);
37             while(true){
38                 String body = reader.readLine();
39                 if (body==null){
40                     break;
41                 }
42                 System.out.println(body);
43                 writer.println(new Date().toString() + "->" + body);
44             }
45         } catch (IOException e) {
46             e.printStackTrace();
47         }
48         //finally关闭资源:流和socket
49     }
50 }

伪异步I/O编程

图片 6

代码示例:

图片 7

public class Server {
    final static int PORT = 8765;
    public static void main(String[] args) {
        ServerSocket server = null;
        BufferedReader in = null;
        PrintWriter out = null;
        try {
            server = new ServerSocket(PORT);
            System.out.println("server start");
            Socket socket = null;
            HandlerExecutorPool executorPool = new HandlerExecutorPool(50, 1000);
            while (true) {
                socket = server.accept();
                executorPool.execute(new ServerHandler(socket));
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (Exception e1) {
                    e1.printStackTrace();
                }
            }
            if (out != null) {
                try {
                    out.close();
                } catch (Exception e2) {
                    e2.printStackTrace();
                }
            }
            if (server != null) {
                try {
                    server.close();
                } catch (Exception e3) {
                    e3.printStackTrace();
                }
            }
            server = null;
        }
    }
}


public class ServerHandler implements Runnable {

    private Socket socket;

    public ServerHandler(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        BufferedReader in = null;
        PrintWriter out = null;
        try {
            in = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));
            out = new PrintWriter(this.socket.getOutputStream(), true);
            String body = null;
            while (true) {
                body = in.readLine();
                if (body == null)
                    break;
                System.out.println("Server:" + body);
                out.println("Server response");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (Exception e1) {
                    e1.printStackTrace();
                }
            }
            if (out != null) {
                try {
                    out.close();
                } catch (Exception e2) {
                    e2.printStackTrace();
                }
            }
            if (socket != null) {
                try {
                    socket.close();
                } catch (Exception e3) {
                    e3.printStackTrace();
                }
            }
            socket = null;
        }
    }
}


public class HandlerExecutorPool {

    private ExecutorService executor;
    public HandlerExecutorPool(int maxPoolSize, int queueSize){
        this.executor = new ThreadPoolExecutor(
                Runtime.getRuntime().availableProcessors(),
                maxPoolSize, 
                120L, 
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(queueSize));
    }

    public void execute(Runnable task){
        this.executor.execute(task);
    }
}


public class Client {

    final static String ADDRESS = "127.0.0.1";
    final static int PORT = 8765;

    public static void main(String[] args) {
        Socket socket = null;
        BufferedReader in = null;
        PrintWriter out = null;
        try {
            socket = new Socket(ADDRESS, PORT);
            in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            out = new PrintWriter(socket.getOutputStream(), true);

            out.println("Client request");

            String response = in.readLine();
            System.out.println("Client:" + response);

        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (Exception e1) {
                    e1.printStackTrace();
                }
            }
            if (out != null) {
                try {
                    out.close();
                } catch (Exception e2) {
                    e2.printStackTrace();
                }
            }
            if (socket != null) {
                try {
                    socket.close();
                } catch (Exception e3) {
                    e3.printStackTrace();
                }
            }
            socket = null;
        }
    }
}

View Code

该格局使用线程池,大家就使得的控制了线程的最大数目,保证了系统有限的财富的支配,完成了N:M的伪异步I/O模型。可是,正因为限制了线程数量,若是发生大气产出请求,超过最大数量的线程就只可以等待,直到线程池中的有闲暇的线程能够被复用。而对Socket的输入流就行读取时,会直接不通,直到发生:

  •     有数据可读
  •     可用数据以及读取实现
  •     产生空指针或I/O分外

   
所以在读取数据较慢时(比如数据量大、互连网传输慢等),大批量出现的情状下,别的连接的音信,只可以直接等候,那便是最大的坏处。

View Code

NIO 编程

  • 服务端使用8081端口打开服务,不断接入客户端请求,每接入八个呼吁,都创制2个线程来处理那些请求。
  • 拍卖逻辑:读取客户端传来的新闻,之后想客户端写音信。

简介

 图片 8

NIO提供了与守旧BIO模型中的Socket和ServerSocket绝对应的SocketChannel和ServerSocketChannel二种差别的套接字通道达成。

增加产量的着三种通路都协理阻塞和非阻塞三种格局。

卡住方式选用仿佛守旧中的援助一样,相比较简单,可是质量和可信性都倒霉;非阻塞方式正好与之相反。

对此低负载、低产出的应用程序,能够运用同步阻塞I/O来进步开发速率和更好的维护性;对于高负载、高并发的(互联网)应用,应采取NIO的非阻塞方式来支付。

贰 、客户端程序:

缓冲区 Buffer

 Buffer是一个指标,包蕴部分要写入或许读出的多少。

   
在NIO库中,全体数据都以用缓冲区处理的。在读取数据时,它是一贯读到缓冲区中的;在写入数据时,也是写入到缓冲区中。任曾几何时候访问NIO中的数据,都以因此缓冲区进行操作。

   
缓冲区实际上是一个数组,并提供了对数码结构化访问以及爱戴读写地方等音信。

    具体的缓存区有那一个:ByteBuffe、CharBuffer、
ShortBuffer、IntBuffer、LongBuffer、FloatBuffer、DoubleBuffer。他们落实了同等的接口:Buffer。

具体介绍可参照 http://ifeve.com/buffers/

图片 9图片 10

通道 Channel

  
大家对数据的读取和写入要通过Channel,它就像是水管一样,是三个通道。通道不一样于流的位置正是通道是双向的,能够用于读、写和同时读写操作。

   
底层的操作系统的康庄大道一般都是全双工的,所以全双工的Channel比流能更好的映射底层操作系统的API。

    Channel首要分两大类:

  •     SelectableChannel:用户互连网读写
  •     FileChannel:用于文书操作

   
前面代码会涉及的ServerSocketChannel和SocketChannel都是SelectableChannel的子类。

 1 package bio;
 2 
 3 import java.io.BufferedReader;
 4 import java.io.IOException;
 5 import java.io.InputStreamReader;
 6 import java.io.PrintWriter;
 7 import java.net.Socket;
 8 
 9 public class BioClient {
10     public static void main(String[] args) throws IOException {
11         Socket clientSocket = new Socket("127.0.0.1", 8081);
12         BufferedReader reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
13         PrintWriter writer = new PrintWriter(clientSocket.getOutputStream(), true);
14         writer.println("haha");
15         String resp = reader.readLine();
16         System.out.println(resp);
17         //finall关闭serverSocket
18     }
19 }

多路复用器 Selector

 Selector是Java  NIO 编制程序的根基。

   
Selector提供选用已经就绪的职责的力量:Selector会不断轮询注册在其上的Channel,如若有些Channel上面发生读只怕写事件,这么些Channel就高居就绪状态,会被Selector轮询出来,然后通过SelectionKey能够得到就绪Channel的集结,进行持续的I/O操作。

   
二个Selector能够而且轮询两个Channel,因为JDK使用了epoll()代替古板的select完成,所以没有最洛桑接句柄1024/2048的限制。所以,只要求叁个线程负责Selector的轮询,就能够接入成千上万的客户端。

 图片 11

代码示例:

图片 12

public class Server implements Runnable {
    // 1 多路复用器(管理所有的通道)
    private Selector seletor;
    // 2 建立缓冲区
    private ByteBuffer readBuf = ByteBuffer.allocate(1024);
    // 3
    private ByteBuffer writeBuf = ByteBuffer.allocate(1024);

    public Server(int port) {
        try {
            // 1 打开路复用器
            this.seletor = Selector.open();
            // 2 打开服务器通道
            ServerSocketChannel ssc = ServerSocketChannel.open();
            // 3 设置服务器通道为非阻塞模式
            ssc.configureBlocking(false);
            // 4 绑定地址
            ssc.bind(new InetSocketAddress(port));
            // 5 把服务器通道注册到多路复用器上,并且监听阻塞事件
            ssc.register(this.seletor, SelectionKey.OP_ACCEPT);

            System.out.println("Server start, port :" + port);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        while (true) {
            try {
                // 1 必须要让多路复用器开始监听
                // 阻塞,等待客户端操作(连接或者写入数据)
                // 客户端刚连上时,key为isAcceptable;客户端输入数据时,key为isReadable;
                this.seletor.select();
                // 2 返回多路复用器已经选择的结果集
                Iterator<SelectionKey> keys = this.seletor.selectedKeys().iterator();
                // 3 进行遍历
                while (keys.hasNext()) {
                    // 4 获取一个选择的元素
                    SelectionKey key = keys.next();
                    // 5 直接从容器中移除就可以了
                    keys.remove();
                    // 6 如果是有效的
                    if (key.isValid()) {
                        // 7 如果为阻塞状态
                        if (key.isAcceptable()) {
                            this.accept(key);
                        }
                        // 8 如果为可读状态
                        if (key.isReadable()) {
                            this.read(key);
                        }
                        // 9 写数据
                        if (key.isWritable()) {
                            // this.write(key); //ssc
                        }
                    }

                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    // 向客户端写数据是通过ServerSocketChannel的来写
    private void write(SelectionKey key) {
        // ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
        // ssc.register(this.seletor, SelectionKey.OP_WRITE);
    }

    private void read(SelectionKey key) {
        try {
            // 1 清空缓冲区旧的数据
            this.readBuf.clear();
            // 2 获取之前注册的socket通道对象
            SocketChannel sc = (SocketChannel) key.channel();
            // 3 读取数据
            int count = sc.read(this.readBuf);
            // 4 如果没有数据
            if (count == -1) {
                key.channel().close();
                key.cancel();
                return;
            }
            // 5 有数据则进行读取 读取之前需要进行复位方法(把position 和limit进行复位)
            this.readBuf.flip();
            // 6 根据缓冲区的数据长度创建相应大小的byte数组,接收缓冲区的数据
            byte[] bytes = new byte[this.readBuf.remaining()];
            // 7 接收缓冲区数据
            this.readBuf.get(bytes);
            // 8 打印结果
            String body = new String(bytes).trim();
            System.out.println("Server : " + body);

            // 9..可以写回给客户端数据

        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    private void accept(SelectionKey key) {
        try {
            // 1 获取服务通道
            ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
            // 2 执行阻塞方法
            SocketChannel sc = ssc.accept();
            // 3 设置阻塞模式
            sc.configureBlocking(false);
            // 4 注册到多路复用器上,并设置读取标识
            sc.register(this.seletor, SelectionKey.OP_READ);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {

        new Thread(new Server(8765)).start();
        ;
    }

}


public class Client {

    // 需要一个Selector
    public static void main(String[] args) {

        // 创建连接的地址
        InetSocketAddress address = new InetSocketAddress("127.0.0.1", 8765);

        // 声明连接通道
        SocketChannel sc = null;

        // 建立缓冲区
        ByteBuffer buf = ByteBuffer.allocate(1024);

        try {
            // 打开通道
            sc = SocketChannel.open();
            // 进行连接
            sc.connect(address);

            while (true) {
                // 定义一个字节数组,然后使用系统录入功能:
                byte[] bytes = new byte[1024];
                System.in.read(bytes);

                // 把数据放到缓冲区中
                buf.put(bytes);
                // 对缓冲区进行复位
                buf.flip();
                // 写出数据
                sc.write(buf);
                // 清空缓冲区数据
                buf.clear();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (sc != null) {
                try {
                    sc.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }

}

View Code

View Code

AIO编程

图片 13

代码示例:

图片 14

public class Server {
    // 线程池
    private ExecutorService executorService;
    // 线程组
    private AsynchronousChannelGroup threadGroup;
    // 服务器通道
    public AsynchronousServerSocketChannel assc;

    public Server(int port) {
        try {
            // 创建一个缓存池
            executorService = Executors.newCachedThreadPool();
            // 创建线程组
            threadGroup = AsynchronousChannelGroup.withCachedThreadPool(executorService, 1);
            // 创建服务器通道
            assc = AsynchronousServerSocketChannel.open(threadGroup);
            // 进行绑定
            assc.bind(new InetSocketAddress(port));

            System.out.println("server start , port : " + port);
            // 进行阻塞
            assc.accept(this, new ServerCompletionHandler());
            // 一直阻塞 不让服务器停止
            Thread.sleep(Integer.MAX_VALUE);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        Server server = new Server(8765);
    }
}


public class ServerCompletionHandler implements CompletionHandler<AsynchronousSocketChannel, Server> {

    @Override
    public void completed(AsynchronousSocketChannel asc, Server attachment) {
        // 当有下一个客户端接入的时候 直接调用Server的accept方法,这样反复执行下去,保证多个客户端都可以阻塞
        attachment.assc.accept(attachment, this);
        read(asc);
    }

    private void read(final AsynchronousSocketChannel asc) {
        // 读取数据
        ByteBuffer buf = ByteBuffer.allocate(1024);
        asc.read(buf, buf, new CompletionHandler<Integer, ByteBuffer>() {
            @Override
            public void completed(Integer resultSize, ByteBuffer attachment) {
                // 进行读取之后,重置标识位
                attachment.flip();
                // 获得读取的字节数
                System.out.println("Server -> " + "收到客户端的数据长度为:" + resultSize);
                // 获取读取的数据
                String resultData = new String(attachment.array()).trim();
                System.out.println("Server -> " + "收到客户端的数据信息为:" + resultData);
                String response = "服务器响应, 收到了客户端发来的数据: " + resultData;
                write(asc, response);
            }

            @Override
            public void failed(Throwable exc, ByteBuffer attachment) {
                exc.printStackTrace();
            }
        });
    }

    private void write(AsynchronousSocketChannel asc, String response) {
        try {
            ByteBuffer buf = ByteBuffer.allocate(1024);
            buf.put(response.getBytes());
            buf.flip();
            asc.write(buf).get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void failed(Throwable exc, Server attachment) {
        exc.printStackTrace();
    }
}


public class Client implements Runnable{

    private AsynchronousSocketChannel asc ;

    public Client() throws Exception {
        asc = AsynchronousSocketChannel.open();
    }

    public void connect(){
        asc.connect(new InetSocketAddress("127.0.0.1", 8765));
    }

    public void write(String request){
        try {
            asc.write(ByteBuffer.wrap(request.getBytes())).get();
            read();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void read() {
        ByteBuffer buf = ByteBuffer.allocate(1024);
        try {
            asc.read(buf).get();
            buf.flip();
            byte[] respByte = new byte[buf.remaining()];
            buf.get(respByte);
            System.out.println(new String(respByte,"utf-8").trim());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        while(true){

        }
    }

    public static void main(String[] args) throws Exception {
        Client c1 = new Client();
        c1.connect();

        Client c2 = new Client();
        c2.connect();

        Client c3 = new Client();
        c3.connect();

        new Thread(c1, "c1").start();
        new Thread(c2, "c2").start();
        new Thread(c3, "c3").start();

        Thread.sleep(1000);

        c1.write("c1 aaa");
        c2.write("c2 bbbb");
        c3.write("c3 ccccc");
    }
}

View Code

  • 客户端创造socket去老是服务端,之后想服务端写音信,并且读取服务端传来的新闻。

各种I/O的对比

    先以一张表来直观的对待一下:

    图片 15

   
具体采用什么的模子恐怕NIO框架,完全根据业务的实际利用场景和品质必要,假设客户端很少,服务器负荷不重,就从未有过供给选用开发起来相对不那么粗略的NIO做服务端;相反,就应考虑使用NIO只怕相关的框架(Netty,Nima)了。

 

 

 

http://www.bkjia.com/Javabc/1306688.htmlwww.bkjia.comtruehttp://www.bkjia.com/Javabc/1306688.htmlTechArticleJava 网络IO编制程序(BIO、NIO、AIO),nioaio 本概念
BIO编制程序 古板的BIO编制程序 代码示例: public class Server { final static int
PROT = 8765 ; public static void mai…

 

服务端阻塞的几个点:

  • serverSocket.accept();//假设没有客户端连接,主线程阻塞在这里
  • 输入流InputStream的read操作也会阻塞:直到“有数量可读”或然“可用数据读取完结”也许“产生越发”
  • 出口流OutputStream的write操作也会卡住:直到“全体要发送的字节全部写入”大概“爆发尤其”

 

二、NIO

① 、服务端程序

图片 16图片 17

 1 package nio;
 2 
 3 import java.io.IOException;
 4 import java.net.InetSocketAddress;
 5 import java.nio.ByteBuffer;
 6 import java.nio.channels.SelectionKey;
 7 import java.nio.channels.Selector;
 8 import java.nio.channels.ServerSocketChannel;
 9 import java.nio.channels.SocketChannel;
10 import java.util.Iterator;
11 
12 public class NioServer {
13     public static void main(String[] args) throws IOException {
14         Selector selector = Selector.open();
15 
16         ServerSocketChannel serverChannel = ServerSocketChannel.open();
17         serverChannel.configureBlocking(false);
18         serverChannel.socket().bind(new InetSocketAddress(8083));//监听链接8082端口的客户端socket
19         serverChannel.register(selector, SelectionKey.OP_ACCEPT);//将serverChannel注册到selector,并监听接受连接事件
20 
21         while (selector.select() > 0) {//该方法会发生阻塞,直到至少有一个事件"准备就绪"为止
22             Iterator<SelectionKey> it = selector.selectedKeys().iterator();
23             while (it.hasNext()) {
24                 SelectionKey sk = it.next();
25                 if (sk.isAcceptable()) {
26                     SocketChannel clientChannel = serverChannel.accept();//相当于客户端三次握手
27                     clientChannel.configureBlocking(false);
28                     clientChannel.register(selector, SelectionKey.OP_READ);
29                 } else if (sk.isReadable()) {
30                     SocketChannel clientChannel = (SocketChannel) sk.channel();
31                     ByteBuffer buf = ByteBuffer.allocate(1024);
32                     while (clientChannel.read(buf) > 0) {//将通道中的数据读到缓冲区,因为clientChannel已经是非阻塞的,所以这里的read是非阻塞的
33                         buf.flip();//将缓冲区由写模式切换到读模式
34                         byte[] bytes = new byte[buf.remaining()];
35                         buf.get(bytes);//将缓冲区中的数据读取到bytes中
36                         String body = new String(bytes, "UTF-8");
37                         System.out.println("接收到来自客户端的信息:" + body);
38                         buf.clear();
39                     }
40                 }
41                 it.remove();
42             }
43         }
44     }
45 }

View Code

  • nio三组件:
    • Buffer:用于存取数据,最要紧的是ByteBuffer
      • position:下四个将被操作的字节地方
      • limit:在写形式下表示能够拓展写的字节数,在读情势下代表能够实行读的字节数
      • capacity:Buffer的大小
    • Channel:用于传输数据,与Buffer相互协作
    • Selector:多路复用器。轮询注册在其上的Channel,当发现有个别大概两个Channel处于“就绪状态”后(有新的TCP链接接入、读、写事件),从阻塞状态再次来到就绪的Channel的SelectionKey集合,之后举办IO操作。
  • selector.select():阻塞,直到有“就绪事件”产生或抛出极度

二 、客户端程序

图片 18图片 19

 1 package nio;
 2 
 3 import java.io.IOException;
 4 import java.net.InetSocketAddress;
 5 import java.nio.ByteBuffer;
 6 import java.nio.channels.SocketChannel;
 7 
 8 public class NioClient {
 9     public static void main(String[] args) throws IOException {
10         SocketChannel clientChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 8083));//向服务端发出连接请求,服务端会通过accept()方法实现三次握手后,建立连接
11         clientChannel.configureBlocking(false);
12         ByteBuffer buf = ByteBuffer.allocate(1024);//分配在JVM堆中:capacity=1024;position=0;limit=1024
13 //        ByteBuffer buf = ByteBuffer.allocateDirect(1024);//分配在堆外内存(物理内存)中
14         buf.put("abcde".getBytes());//将数据写入缓冲区buf中:capacity=1024;position=5;limit=1024
15         buf.flip();//将缓冲区从写模式切换到读模式:capacity=1024;position=0;limit=5
16         clientChannel.write(buf);//将缓冲区的数据写入通道:capacity=1024;position=5;limit=5
17         buf.clear();//清空缓冲区:capacity=1024;position=0;limit=1024
18         clientChannel.close();
19     }
20 }

View Code

 

附:ByteBuffer使用JVM堆内部存款和储蓄器和利用堆外内部存储器的界别:(图片源于尚硅谷的NIO教程)

图片 20

  • 动用堆内部存款和储蓄器:
    • 应用程序将数据写入用户地址空间(JVM)中,之后将用户地址空间中的数据拷贝到内核地址空间(操作系统)
  • 行使堆外内部存款和储蓄器:
    • 直接在情理内部存款和储蓄器开辟空间,应用程序直接将数据写入物理内部存款和储蓄器,之后操作系统将其写入硬盘 –
      “零拷贝”

 

三、BIO与NIO的比较

1、线程数

  • BIO:贰个客户端连接就要选拔二个服务端线程来进展处理
    • 只怕会有大批量的想爱你成处于休眠状态,只是等待输入或输出(阻塞)
    • 为各种线程分配调用栈,大概1M,服务端内部存款和储蓄器吃紧
    • 固然服务端内部存储器一点都不小,线程数太大会导致线程上下文切换浪费多量时光
  • NIO:一个服务端线程操作二个Selector,就能够拍卖成千成万的客户端连接

贰 、阻塞境况

  • BIO:读、写、接受连接都会产生堵塞
  • NIO:唯有Selector.select()会卡住,其实是伺机Channel上的“就绪事件”

叁 、面向对象

  • BIO:面向流
  • NIO:面向Buffer

4、适合点

  • BIO:要是您有微量的连日使用11分高的带宽,一回发送大批量的多寡,可能典型的IO服务器完成只怕尤其符合
  • NIO:倘若须求管理同时打开的不在少数个延续,这几个连接每回只是发送少量的多寡,例如聊天服务器,完毕NIO的服务器或者是贰个优势。

 

四、Reactor模型

图片 21

主干模型:

  • 主线程池:
    • 全副由NIO线程组成,使用线程池是因为担心质量问题
    • 选取客户端连接请求,或然包罗认证
    • 收下到客户端的连年请求并处理完了(比如认证)后,将创建出来的SocketChannel(查看下面包车型大巴NIOServer类)注册到次线程池的某一条NIO线程上,之后那条NIO线程进行IO操作。
  • 次线程池:
    • 一切由NIO线程组成
    • 进展IO操作(编解码、业务逻辑等)