https://blog.csdn.net/qq_18860653/article/details/53406723

Netty的使用或许我们看着官网user guide还是很容易入门的。因为java nio使用非常的繁琐,netty对java nio进行了大量的封装。对于Netty的理解,我们首先需要了解NIO的原理和使用。所以,我也特别渴望去了解NIO这种通信模式。

官方的定义是:nio 是non-blocking的简称,在jdk1.4 里提供的新api 。Sun 官方标榜的特性如下: 为所有的原始类型提供(Buffer)缓存支持。字符集编码解码解决方案。 Channel :一个新的原始I/O 抽象。 支持锁和内存映射文件的文件访问接口。 提供多路(non-bloking) 非阻塞式的高伸缩性网络I/O 。是不是很抽象?

在阅读《NIO入门》这篇技术文档之后,收获了很多。包括对Java NIO的理解和使用,所以也特别的感谢作者。

首先,还是来回顾以下从这篇文档中学到的要点。

为什么要使用 NIO?
NIO 的创建目的是为了让 Java 程序员可以实现高速 I/O 而无需编写自定义的本机代码。NIO 将最耗时的 I/O 操作(即填充和提取缓冲区)转移回操作系统,因而可以极大地提高速度。

NIO最重要的组成部分

通道 Channels
缓冲区 Buffers
选择器 Selectors

Buffer 是一个对象, 它包含一些要写入或者刚读出的数据。

在 NIO 库中,所有数据都是用缓冲区处理的。在读取数据时,它是直接读到缓冲区中的。在写入数据时,它是写入到缓冲区中的。任何时候访问 NIO 中的数据,您都是将它放到缓冲区中。
缓冲区实质上是一个数组。通常它是一个字节数组,但是也可以使用其他种类的数组。但是一个缓冲区不 仅仅 是一个数组。缓冲区提供了对数据的结构化访问,而且还可以跟踪系统的读/写进程。

Channel是一个对象,可以通过它读取和写入数据

看完下面这个例子,基本上就理解buffer和channel的作用了

  1.  
    package yyf.java.nio.ibm;
  2.  
     
  3.  
    import java.io.*;
  4.  
    import java.nio.*;
  5.  
    import java.nio.channels.*;
  6.  
     
  7.  
    public class CopyFile {
  8.  
    static public void main(String args[]) throws Exception {
  9.  
     
  10.  
    String infile = "c://test/nio_copy.txt";
  11.  
    String outfile = "c://test/result.txt";
  12.  
     
  13.  
    FileInputStream fin = new FileInputStream(infile);
  14.  
    FileOutputStream fout = new FileOutputStream(outfile);
  15.  
    // 获取读的通道
  16.  
    FileChannel fcin = fin.getChannel();
  17.  
    // 获取写的通道
  18.  
    FileChannel fcout = fout.getChannel();
  19.  
    // 定义缓冲区,并指定大小
  20.  
    ByteBuffer buffer = ByteBuffer.allocate(1024);
  21.  
     
  22.  
    while (true) {
  23.  
    // 清空缓冲区
  24.  
    buffer.clear();
  25.  
    //从通道读取一个数据到缓冲区
  26.  
    int r = fcin.read(buffer);
  27.  
    //判断是否有从通道读到数据
  28.  
    if (r == -1) {
  29.  
    break;
  30.  
    }
  31.  
    //将buffer指针指向头部
  32.  
    buffer.flip();
  33.  
    //把缓冲区数据写入通道
  34.  
    fcout.write(buffer);
  35.  
    }
  36.  
    }
  37.  
    }

缓冲区主要是三个变量

position
limit
capacity
这三个变量一起可以跟踪缓冲区的状态和它所包含的数据。我们将在下面的小节中详细分析每一个变量,还要介绍它们如何适应典型的读/写(输入/输出)进程。在这个例子中,我们假定要将数据从一个输入通道拷贝到一个输出通道。
Position
您可以回想一下,缓冲区实际上就是美化了的数组。在从通道读取时,您将所读取的数据放到底层的数组中。 position 变量跟踪已经写了多少数据。更准确地说,它指定了下一个字节将放到数组的哪一个元素中。因此,如果您从通道中读三个字节到缓冲区中,那么缓冲区的 position 将会设置为3,指向数组中第四个元素。
同样,在写入通道时,您是从缓冲区中获取数据。 position 值跟踪从缓冲区中获取了多少数据。更准确地说,它指定下一个字节来自数组的哪一个元素。因此如果从缓冲区写了5个字节到通道中,那么缓冲区的 position 将被设置为5,指向数组的第六个元素。
Limit
limit 变量表明还有多少数据需要取出(在从缓冲区写入通道时),或者还有多少空间可以放入数据(在从通道读入缓冲区时)。
position 总是小于或者等于 limit。
Capacity
缓冲区的 capacity 表明可以储存在缓冲区中的最大数据容量。实际上,它指定了底层数组的大小 ― 或者至少是指定了准许我们使用的底层数组的容量。
limit 决不能大于 capacity。

缓冲区作为一个数组,这三个变量就是其中数据的标记,也很好理解。

Selector(选择器)是Java NIO中能够检测一到多个NIO通道,并能够知晓通道是否为诸如读写事件做好准备的组件。这样,一个单独的线程可以管理多个channel,从而管理多个网络连接。

接下来来看看具体的使用把,我创建了一个直接收消息的服务器(一边接收一边写数据可能对于新手不好理解)

服务端:

  1.  
    package yyf.java.nio.test;
  2.  
     
  3.  
    import java.net.InetSocketAddress;
  4.  
    import java.net.ServerSocket;
  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.  
    import java.util.Set;
  12.  
     
  13.  
    public class NioReceiver {
  14.  
    @SuppressWarnings("null")
  15.  
    public static void main(String[] args) throws Exception {
  16.  
    ByteBuffer echoBuffer = ByteBuffer.allocate(8);
  17.  
    ServerSocketChannel ssc = ServerSocketChannel.open();
  18.  
    Selector selector = Selector.open();
  19.  
    ssc.configureBlocking(false);
  20.  
    ServerSocket ss = ssc.socket();
  21.  
    InetSocketAddress address = new InetSocketAddress(8080);
  22.  
    ss.bind(address);
  23.  
    SelectionKey key = ssc.register(selector, SelectionKey.OP_ACCEPT);
  24.  
    System.out.println("开始监听……");
  25.  
    while (true) {
  26.  
    int num = selector.select();
  27.  
    Set selectedKeys = selector.selectedKeys();
  28.  
    Iterator it = selectedKeys.iterator();
  29.  
    while (it.hasNext()) {
  30.  
    SelectionKey sKey = (SelectionKey) it.next();
  31.  
    SocketChannel channel = null;
  32.  
    if (sKey.isAcceptable()) {
  33.  
    ServerSocketChannel sc = (ServerSocketChannel) key.channel();
  34.  
    channel = sc.accept();// 接受连接请求
  35.  
    channel.configureBlocking(false);
  36.  
    channel.register(selector, SelectionKey.OP_READ);
  37.  
    it.remove();
  38.  
    } else if (sKey.isReadable()) {
  39.  
    channel = (SocketChannel) sKey.channel();
  40.  
    while (true) {
  41.  
    echoBuffer.clear();
  42.  
    int r = channel.read(echoBuffer);
  43.  
    if (r <= 0) {
  44.  
    channel.close();
  45.  
    System.out.println("接收完毕,断开连接");
  46.  
    break;
  47.  
    }
  48.  
    System.out.println("##" + r + " " + new String(echoBuffer.array(), 0, echoBuffer.position()));
  49.  
    echoBuffer.flip();
  50.  
    }
  51.  
    it.remove();
  52.  
    } else {
  53.  
    channel.close();
  54.  
    }
  55.  
    }
  56.  
    }
  57.  
     
  58.  
    }
  59.  
     
  60.  
    }

客户端(NIO):

  1.  
    package yyf.java.nio.test;
  2.  
     
  3.  
    import java.net.InetSocketAddress;
  4.  
    import java.nio.ByteBuffer;
  5.  
    import java.nio.channels.SelectionKey;
  6.  
    import java.nio.channels.Selector;
  7.  
    import java.nio.channels.SocketChannel;
  8.  
    import java.util.Iterator;
  9.  
    import java.util.Set;
  10.  
     
  11.  
    public class NioTest {
  12.  
    public static void main(String[] args) throws Exception {
  13.  
    ByteBuffer echoBuffer = ByteBuffer.allocate(1024);
  14.  
    SocketChannel channel = null;
  15.  
    Selector selector = null;
  16.  
    channel = SocketChannel.open();
  17.  
    channel.configureBlocking(false);
  18.  
    // 请求连接
  19.  
    channel.connect(new InetSocketAddress("localhost", 8080));
  20.  
    selector = Selector.open();
  21.  
    channel.register(selector, SelectionKey.OP_CONNECT);
  22.  
    int num = selector.select();
  23.  
    Set selectedKeys = selector.selectedKeys();
  24.  
    Iterator it = selectedKeys.iterator();
  25.  
    while (it.hasNext()) {
  26.  
    SelectionKey key = (SelectionKey) it.next();
  27.  
    it.remove();
  28.  
    if (key.isConnectable()) {
  29.  
    if (channel.isConnectionPending()) {
  30.  
    if (channel.finishConnect()) {
  31.  
    // 只有当连接成功后才能注册OP_READ事件
  32.  
    key.interestOps(SelectionKey.OP_READ);
  33.  
    echoBuffer.put("123456789abcdefghijklmnopq".getBytes());
  34.  
    echoBuffer.flip();
  35.  
    System.out.println("##" + new String(echoBuffer.array()));
  36.  
    channel.write(echoBuffer);
  37.  
    System.out.println("写入完毕");
  38.  
    } else {
  39.  
    key.cancel();
  40.  
    }
  41.  
    }
  42.  
    }
  43.  
    }
  44.  
     
  45.  
    }
  46.  
    }

运行结果:

  1.  
    开始监听……
  2.  
    ##8 12345678
  3.  
    ##8 9abcdefg
  4.  
    ##8 hijklmno
  5.  
    ##2 pq
  6.  
    接收完毕,断开连接

当然,BIO的客户端也可以,开启10个BIO客户端线程

  1.  
    package yyf.java.nio.test;
  2.  
     
  3.  
    import java.io.ByteArrayOutputStream;
  4.  
    import java.io.IOException;
  5.  
    import java.io.InputStream;
  6.  
    import java.io.OutputStream;
  7.  
    import java.net.InetAddress;
  8.  
    import java.net.InetSocketAddress;
  9.  
    import java.net.Socket;
  10.  
    import java.net.UnknownHostException;
  11.  
    import java.util.Random;
  12.  
     
  13.  
    import yyf.java.test.Main;
  14.  
     
  15.  
    public class BioClientTest {
  16.  
    public static void main(String[] args) throws Exception {
  17.  
    BioClient n = new BioClient();
  18.  
    for (int i = 0; i < 10; i++) {
  19.  
    Thread t1 = new Thread(n);
  20.  
    t1.start();
  21.  
    }
  22.  
    }
  23.  
    }
  24.  
     
  25.  
    class BioClient implements Runnable {
  26.  
    @Override
  27.  
    public void run() {
  28.  
     
  29.  
    try {
  30.  
    Socket socket = new Socket("127.0.0.1", 8080);
  31.  
    OutputStream os = socket.getOutputStream();
  32.  
    InputStream is = socket.getInputStream();
  33.  
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
  34.  
    String str = Thread.currentThread().getName() + "...........sadsadasJava";
  35.  
    os.write(str.getBytes());
  36.  
    StringBuffer sb = new StringBuffer();
  37.  
    byte[] b = new byte[1024];
  38.  
    int len;
  39.  
    while ((len = is.read(b)) != -1) {
  40.  
    bos.write(b, 0, len);
  41.  
    }
  42.  
    is.close();
  43.  
    os.close();
  44.  
    socket.close();
  45.  
    System.out.println(Thread.currentThread().getName() + " 写入完毕 " + new String(bos.toByteArray()));
  46.  
    } catch (Exception e) {
  47.  
    e.printStackTrace();
  48.  
    }
  49.  
    }
  50.  
     
  51.  
    }

运行结果:

  1.  
    ##8 Thread-4
  2.  
    ##8 ........
  3.  
    ##8 ...sadsa
  4.  
    ##7 dasJava
  5.  
    接收完毕,断开连接
  6.  
    ##8 Thread-3
  7.  
    ##8 ........
  8.  
    ##8 ...sadsa
  9.  
    ##7 dasJava
  10.  
    接收完毕,断开连接
  11.  
    ##8 Thread-9
  12.  
    ##8 ........
  13.  
    ##8 ...sadsa
  14.  
    ##7 dasJava
  15.  
    接收完毕,断开连接
  16.  
    ##8 Thread-7
  17.  
    ##8 ........
  18.  
    ##8 ...sadsa
  19.  
    ##7 dasJava
  20.  
    接收完毕,断开连接
  21.  
    ##8 Thread-0
  22.  
    ##8 ........
  23.  
    ##8 ...sadsa
  24.  
    ##7 dasJava
  25.  
    接收完毕,断开连接
  26.  
    ##8 Thread-5
  27.  
    ##8 ........
  28.  
    ##8 ...sadsa
  29.  
    ##7 dasJava
  30.  
    接收完毕,断开连接
  31.  
    ##8 Thread-2
  32.  
    ##8 ........
  33.  
    ##8 ...sadsa
  34.  
    ##7 dasJava
  35.  
    接收完毕,断开连接
  36.  
    ##8 Thread-8
  37.  
    ##8 ........
  38.  
    ##8 ...sadsa
  39.  
    ##7 dasJava
  40.  
    接收完毕,断开连接
  41.  
    ##8 Thread-1
  42.  
    ##8 ........
  43.  
    ##8 ...sadsa
  44.  
    ##7 dasJava
  45.  
    接收完毕,断开连接
  46.  
    ##8 Thread-6
  47.  
    ##8 ........
  48.  
    ##8 ...sadsa
  49.  
    ##7 dasJava
  50.  
    接收完毕,断开连接

当然,这只是一个测试,对于一个服务器,是有读取,也有写出的,这是文档给的一个服务端例子

  1.  
    package yyf.java.nio.ibm;
  2.  
     
  3.  
    import java.io.*;
  4.  
    import java.net.*;
  5.  
    import java.nio.*;
  6.  
    import java.nio.channels.*;
  7.  
    import java.util.*;
  8.  
     
  9.  
    public class MultiPortEcho {
  10.  
    private int ports[];
  11.  
    private ByteBuffer echoBuffer = ByteBuffer.allocate(5);
  12.  
     
  13.  
    public MultiPortEcho(int ports[]) throws IOException {
  14.  
    this.ports = ports;
  15.  
    go();
  16.  
    }
  17.  
     
  18.  
    private void go() throws IOException {
  19.  
    Selector selector = Selector.open();
  20.  
    for (int i = 0; i < ports.length; ++i) {
  21.  
    ServerSocketChannel ssc = ServerSocketChannel.open();
  22.  
    ssc.configureBlocking(false);
  23.  
    ServerSocket ss = ssc.socket();
  24.  
    InetSocketAddress address = new InetSocketAddress(ports[i]);
  25.  
    ss.bind(address);
  26.  
    SelectionKey key = ssc.register(selector, SelectionKey.OP_ACCEPT);
  27.  
    System.out.println("Going to listen on " + ports[i]);
  28.  
    }
  29.  
     
  30.  
    while (true) {
  31.  
    int num = selector.select();
  32.  
    Set selectedKeys = selector.selectedKeys();
  33.  
    Iterator it = selectedKeys.iterator();
  34.  
    while (it.hasNext()) {
  35.  
    SelectionKey key = (SelectionKey) it.next();
  36.  
    if ((key.readyOps() & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT) {
  37.  
    ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
  38.  
    SocketChannel sc = ssc.accept();
  39.  
    sc.configureBlocking(false);
  40.  
    SelectionKey newKey = sc.register(selector, SelectionKey.OP_READ);
  41.  
    it.remove();
  42.  
    System.out.println("Got connection from " + sc);
  43.  
    } else if ((key.readyOps() & SelectionKey.OP_READ) == SelectionKey.OP_READ) {
  44.  
    SocketChannel sc = (SocketChannel) key.channel();
  45.  
    int bytesEchoed = 0;
  46.  
    while (true) {
  47.  
    echoBuffer.clear();
  48.  
    int r = sc.read(echoBuffer);
  49.  
    if (r <= 0) {
  50.  
    sc.close();
  51.  
    break;
  52.  
    }
  53.  
    echoBuffer.flip();
  54.  
    sc.write(echoBuffer);
  55.  
    bytesEchoed += r;
  56.  
    }
  57.  
    System.out.println("Echoed " + bytesEchoed + " from " + sc);
  58.  
    it.remove();
  59.  
    }
  60.  
     
  61.  
    }
  62.  
    // System.out.println( "going to clear" );
  63.  
    // selectedKeys.clear();
  64.  
    // System.out.println( "cleared" );
  65.  
    }
  66.  
    }
  67.  
     
  68.  
    static public void main(String args[]) throws Exception {
  69.  
    int ports[] = new int[] { 8080 };
  70.  
    for (int i = 0; i < args.length; ++i) {
  71.  
    ports[i] = Integer.parseInt(args[i]);
  72.  
    }
  73.  
    new MultiPortEcho(ports);
  74.  
    }
  75.  
    }

现在,我们就写个客户端去跟服务器通信,把发过去的返回来:

  1.  
    package yyf.java.nio;
  2.  
     
  3.  
    import java.io.IOException;
  4.  
    import java.net.InetSocketAddress;
  5.  
    import java.net.SocketAddress;
  6.  
    import java.nio.ByteBuffer;
  7.  
    import java.nio.channels.SocketChannel;
  8.  
     
  9.  
    import javax.swing.ButtonGroup;
  10.  
     
  11.  
    public class NioClient {
  12.  
    public static void main(String[] args) throws IOException {
  13.  
    SocketChannel socketChannel = SocketChannel.open();
  14.  
    SocketAddress socketAddress = new InetSocketAddress("127.0.0.1", 8080);
  15.  
    socketChannel.connect(socketAddress);
  16.  
    String str = "你好a";
  17.  
    ByteBuffer buffer = ByteBuffer.wrap(str.getBytes());
  18.  
    socketChannel.write(buffer);
  19.  
    socketChannel.socket().shutdownOutput();
  20.  
     
  21.  
    buffer.clear();
  22.  
    byte[] bytes;
  23.  
    int count = 0;
  24.  
    while ((count = socketChannel.read(buffer)) > 0) {
  25.  
    buffer.flip();
  26.  
    bytes = new byte[count];
  27.  
    buffer.get(bytes);
  28.  
    System.out.println(new String(buffer.array()));
  29.  
    buffer.clear();
  30.  
    }
  31.  
    socketChannel.socket().shutdownInput();
  32.  
    socketChannel.socket().close();
  33.  
    socketChannel.close();
  34.  
    }
  35.  
    }

运行结果

server:

  1.  
    Going to listen on 8080
  2.  
    Got connection from java.nio.channels.SocketChannel[connected local=/127.0.0.1:8080 remote=/127.0.0.1:63584]
  3.  
    Echoed 7 from java.nio.channels.SocketChannel[closed]

client:

你好a

对于NIO的入门就先到这里。

Java NIO理解与使用的更多相关文章

  1. java NIO理解分析与基本使用

    我前段时间的一篇博客java网络编程--多线程数据收发并行总结了服务端与客户端之间的收发并行实践.原理很简单,就是针对单一客户端,服务端起两个线程分别负责read和write操作,然后线程保持阻塞等待 ...

  2. Java nio 理解

    Java nio 称为Java new IO ,对Java io而言的.他有两个主要的概念:缓存.通道. 在程序中,数据的来源或写入,要么网络.要么硬盘.所有通道分为:文件通道.TCP通道.UDP通道 ...

  3. JAVA IO 以及 NIO 理解

    由于Netty,了解了一些异步IO的知识,JAVA里面NIO就是原来的IO的一个补充,本文主要记录下在JAVA中IO的底层实现原理,以及对Zerocopy技术介绍. IO,其实意味着:数据不停地搬入搬 ...

  4. 深入理解Java NIO

    初识NIO: 在 JDK 1. 4 中 新 加入 了 NIO( New Input/ Output) 类, 引入了一种基于通道和缓冲区的 I/O 方式,它可以使用 Native 函数库直接分配堆外内存 ...

  5. 一文让你彻底理解 Java NIO 核心组件

    背景知识 同步.异步.阻塞.非阻塞 首先,这几个概念非常容易搞混淆,但NIO中又有涉及,所以总结一下. 同步:API调用返回时调用者就知道操作的结果如何了(实际读取/写入了多少字节). 异步:相对于同 ...

  6. 一文理解 Java NIO 核心组件

    同步.异步.阻塞.非阻塞 首先,这几个概念非常容易搞混淆,但NIO中又有涉及,所以总结一下[1]. 同步:API调用返回时调用者就知道操作的结果如何了(实际读取/写入了多少字节). 异步:相对于同步, ...

  7. java nio最白话理解

    JAVA NIO是同步非阻塞io.同步和异步说的是消息的通知机制,阻塞非阻塞说的是线程的状态 .下面说说我的理解,client和服务器建立了socket连接:1.同步阻塞io:client在调用rea ...

  8. Java NIO之理解I/O模型

    前言 自己以前在Java NIO这块儿,一直都是比较薄弱的,以前还因为这点知识而错失了一个机会.所以最近打算好好学习一下这部分内容,我想应该也会有朋友像我一样,一直想闹明白这块儿内容.但是一直无从下手 ...

  9. 理解Java NIO

    基础概念• 缓冲区操作缓冲区及操作是所有I/O的基础,进程执行I/O操作,归结起来就是向操作系统发出请求,让它要么把缓冲区里的数据排干(写),要么把缓冲区填满(读).如下图• 内核空间.用户空间 上图 ...

随机推荐

  1. Hive-复制表

    非分区表复制 复制一张非分区表,使用CREATE TABLE IF NOT EXISTS AS SELECT * FROM tb_name;只复制表结构,CREATE TABLE IF NOT EXI ...

  2. C_Learning (4)

    / 预处理命令 / 宏定义 / 一般形式:#define 宏名 字符串 # 表示这是一条预处理命令 宏名是一个标识符,必须符合C语言标识符的规定 字符串可以是常数.表达式.格式化字符串等 / 注意: ...

  3. 20145333茹翔 Exp8 Web基础

    20145333茹翔 Exp8 Web基础 实验问题回答 (1)什么是表单 表单是一个包含表单元素的区域,表单元素是允许用户在表单中(比如:文本域.下拉列表.单选框.复选框等等)输入信息的元素,表单在 ...

  4. 【修改帐号信息】Eclipse中修改SVN用户名和密码方法

    由于在svn 的界面中并没有为我们提供直接更换用户名密码的地方,所以一旦我们需要更换用户名的就需要自己想一些办法. 解决方案: 在Eclipse 使用SVN 的过程中大多数人往往习惯把访问SVN 的用 ...

  5. The superclass "javax.servlet.http.HttpServlet" was not found on the Java Build Path 解决方法

    项目忽然出现 The superclass "javax.servlet.http.HttpServlet" was not found on the Java Build Pat ...

  6. 【Python】【元编程】【三】【元类】

    '''# str. type 和 LineItem 是object 的子类 str. object 和 LineItem 是 type 的实例,因为它们都是类object 类和 type 类之间的关系 ...

  7. c++ 查找数组或者容器元素是否存在(find)

    #include <iostream> // cout #include <algorithm> // find #include <vector> // vect ...

  8. Docker监控怎么做?

    http://dockone.io/article/1643 监控的价值与体系在运维体系中, 监控是非常重要的组成部分.通过监控可以实时掌握系统运行的状态,对故障的提前预警,历史状态的回放等,还可以通 ...

  9. [ios]ios画线 使用CGContextRef,CGPath和UIBezierPath来绘画

    参考 :http://www.mgenware.com/blog/?p=493 这三种东西:CGContextRef,CGPath和UIBezierPath.本质上都是一样的,都是使用Quartz来绘 ...

  10. HDU 6015 Skip the Class

    Skip the Class 代码: #include<bits/stdc++.h> using namespace std; #define ll long long #define l ...