架构师养成记--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 }

 

posted on 2017-01-18 19:56  司广孟  阅读(515)  评论(0编辑  收藏  举报

导航