• 架构师养成记--18.NIO


    有人叫new IO 我这里就叫Non-block IO

    经典概念:

    Buffer(缓冲区):之前直接通过流,现在提供一个buffer存放数据。

    Channel:管道,包括ServerSocketChannel和SocketChannel

    Selecor(选择器、多路复用器):SocketChannel注册到选择器上,轮询selector上的所有socketChannel ,根据通道的状态来执行相关操作。通道有链接状态、阻塞状态、可读状态、可写状态(Connect、Accept、Read、Write)。

    Buffer:

    和原来的IO的一个重要区别,面向流到面向缓冲区的转变。ByteBuffer、CharBuffer、ShortBuffer、IntBuffer、LongBuffer、FloatBuffer、DoubleBuffer

    pos位置、limit长度、cap容量

    朝pos装一个元素,pos增加1,get(Index)时pos时不发生变化的。所以在每批put后要flit()一下。put(value)和get()都会让pos发生递增。

    IntBuffer.wrap(arr)可以将Int数组转换成IntBuffer,wrap方法影响limit;

    put(arr)不影响limit;

    buffer1.position(1);

    buffer1.duplicate()复制;

    Buffer1.remaining()可读长度;

    Channel:

    通道是双向的,SocketChannel、ServerSocketChannel,Selector不断轮询通道的状态,一旦有通道数据准备好了,selector就把这个通道拿出来(通过key可以拿出来)。

    selector:

    特别强调selector,理论上讲selector可以负责超大数据量的Channel,没有限制多少个客户端,因为只有一个线程在轮询Channel,也用epoll代替传统的select。

    但是还是同步的,因为读写操作还是通过java代码实现的。

    客户端:

      1 import java.io.IOException;
      2 import java.net.InetSocketAddress;
      3 import java.nio.ByteBuffer;
      4 import java.nio.channels.SelectionKey;
      5 import java.nio.channels.Selector;
      6 import java.nio.channels.ServerSocketChannel;
      7 import java.nio.channels.SocketChannel;
      8 import java.util.Iterator;
      9 
     10 public class Server implements Runnable{
     11     //1 多路复用器(管理所有的通道)
     12     private Selector seletor;
     13     //2 建立缓冲区
     14     private ByteBuffer readBuf = ByteBuffer.allocate(1024);
     15     //3 
     16     private ByteBuffer writeBuf = ByteBuffer.allocate(1024);
     17     public Server(int port){
     18         try {
     19             //1 打开路复用器
     20             this.seletor = Selector.open();
     21             //2 打开服务器通道
     22             ServerSocketChannel ssc = ServerSocketChannel.open();
     23             //3 设置服务器通道为非阻塞模式
     24             ssc.configureBlocking(false);
     25             //4 绑定地址
     26             ssc.bind(new InetSocketAddress(port));
     27             //5 把服务器通道注册到多路复用器上,并且监听阻塞事件
     28             ssc.register(this.seletor, SelectionKey.OP_ACCEPT);
     29             
     30             System.out.println("Server start, port :" + port);
     31             
     32         } catch (IOException e) {
     33             e.printStackTrace();
     34         }
     35     }
     36 
     37     @Override
     38     public void run() {
     39         while(true){
     40             try {
     41                 //1 必须要让多路复用器开始监听
     42                 this.seletor.select();
     43                 //2 返回多路复用器已经选择的结果集
     44                 Iterator<SelectionKey> keys = this.seletor.selectedKeys().iterator();
     45                 //3 进行遍历
     46                 while(keys.hasNext()){
     47                     //4 获取一个选择的元素
     48                     SelectionKey key = keys.next();
     49                     //5 直接从容器中移除就可以了
     50                     keys.remove();
     51                     //6 如果是有效的
     52                     if(key.isValid()){
     53                         //7 如果为阻塞状态
     54                         if(key.isAcceptable()){
     55                             this.accept(key);
     56                         }
     57                         //8 如果为可读状态
     58                         if(key.isReadable()){
     59                             this.read(key);
     60                         }
     61                         //9 写数据
     62                         if(key.isWritable()){
     63                             //this.write(key); //ssc
     64                         }
     65                     }
     66                     
     67                 }
     68             } catch (IOException e) {
     69                 e.printStackTrace();
     70             }
     71         }
     72     }
     73     
     74     private void write(SelectionKey key){
     75         //ServerSocketChannel ssc =  (ServerSocketChannel) key.channel();
     76         //ssc.register(this.seletor, SelectionKey.OP_WRITE);
     77     }
     78 
     79     private void read(SelectionKey key) {
     80         try {
     81             //1 清空缓冲区旧的数据
     82             this.readBuf.clear();
     83             //2 获取之前注册的socket通道对象
     84             SocketChannel sc = (SocketChannel) key.channel();
     85             //3 读取数据
     86             int count = sc.read(this.readBuf);
     87             //4 如果没有数据
     88             if(count == -1){
     89                 key.channel().close();
     90                 key.cancel();
     91                 return;
     92             }
     93             //5 有数据则进行读取 读取之前需要进行复位方法(把position 和limit进行复位)
     94             this.readBuf.flip();
     95             //6 根据缓冲区的数据长度创建相应大小的byte数组,接收缓冲区的数据
     96             byte[] bytes = new byte[this.readBuf.remaining()];
     97             //7 接收缓冲区数据
     98             this.readBuf.get(bytes);
     99             //8 打印结果
    100             String body = new String(bytes).trim();
    101             System.out.println("Server : " + body);
    102             
    103             // 9..可以写回给客户端数据 
    104             
    105         } catch (IOException e) {
    106             e.printStackTrace();
    107         }
    108         
    109     }
    110 
    111     private void accept(SelectionKey key) {
    112         try {
    113             //1 获取服务通道
    114             ServerSocketChannel ssc =  (ServerSocketChannel) key.channel();
    115             //2 执行阻塞方法
    116             SocketChannel sc = ssc.accept();
    117             //3 设置阻塞模式
    118             sc.configureBlocking(false);
    119             //4 注册到多路复用器上,并设置读取标识
    120             sc.register(this.seletor, SelectionKey.OP_READ);
    121         } catch (IOException e) {
    122             e.printStackTrace();
    123         }
    124     }
    125     
    126     public static void main(String[] args) {
    127         
    128         new Thread(new Server(8765)).start();;
    129     }
    130     
    131     
    132 }

    客户端:

     1 import java.io.IOException;
     2 import java.net.InetSocketAddress;
     3 import java.nio.ByteBuffer;
     4 import java.nio.channels.SocketChannel;
     5 
     6 public class Client {
     7 
     8     //需要一个Selector 
     9     public static void main(String[] args) {
    10         
    11         //创建连接的地址
    12         InetSocketAddress address = new InetSocketAddress("127.0.0.1", 8765);
    13         
    14         //声明连接通道
    15         SocketChannel sc = null;
    16         
    17         //建立缓冲区
    18         ByteBuffer buf = ByteBuffer.allocate(1024);
    19         
    20         try {
    21             //打开通道
    22             sc = SocketChannel.open();
    23             //进行连接
    24             sc.connect(address);
    25             
    26             while(true){
    27                 //定义一个字节数组,然后使用系统录入功能:
    28                 byte[] bytes = new byte[1024];
    29                 System.in.read(bytes);
    30                 
    31                 //把数据放到缓冲区中
    32                 buf.put(bytes);
    33                 //对缓冲区进行复位
    34                 buf.flip();
    35                 //写出数据
    36                 sc.write(buf);
    37                 //清空缓冲区数据
    38                 buf.clear();
    39             }
    40         } catch (IOException e) {
    41             e.printStackTrace();
    42         } finally {
    43             if(sc != null){
    44                 try {
    45                     sc.close();
    46                 } catch (IOException e) {
    47                     e.printStackTrace();
    48                 }
    49             }
    50         }
    51         
    52     }
    53     
    54 }
  • 相关阅读:
    NAT(NAPT)地址转换过程
    关于路由、AP、交换机的小总结
    交换机、集线器、路由器区别和作用
    系统调用与API的区别
    课程设计
    Python学习之format函数的使用
    等边三角形
    Hello 2018
    PyCharm idea clion webstorm phpstorm激活
    Educational Codeforces Round 35 (Rated for Div. 2)(ABC)
  • 原文地址:https://www.cnblogs.com/sigm/p/6298014.html
Copyright © 2020-2023  润新知