Nio学习笔记(大部分网上摘抄)

Nio与IO的区别

  原有的 IO 是面向流的、阻塞的,NIO 则是面向块的、非阻塞的。

   1.IO流每次从流中读一个或多个字节,直至读完所有字节,他们没有被缓存在其他地方,并且,IO流不能移动流中的数据,如果需要前后移动从流中读取的教据,需要先将它缓存到一个缓冲区。Java NIO的缓冲导向方法略有不同。数据读取到一个它稍后处理的缓冲区,霱要时可在缓冲区中前后移动。这就增加了处理过程中的灵活性。但是,还需要检查是否该缓冲区中包含所有您需要处理的数裾。而且,需确保当更多的数据读入缓冲区时,不要覆盖缓冲区里尚未处理的数据。

   2.Java IO的各种流是阻塞的。这意味着,当一个线程调用read() 或 write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了。 Java NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取。而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。 非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。 线程通常将非阻塞IO的空闲时间用于在其它通道上执行IO操作,所以一个单独的线程现在可以管理多个输入和输出通道(channel)。、

通道Channel

  NIO的通道类似于流,但有些区别如下:

    1. 通道可以同时进行读写,而流只能读或者只能写

    2. 通道可以实现异步读写数据

    3. 通道可以从缓冲读数据,也可以写数据到缓冲: 

缓冲区Buffer类

  缓冲区是一个对象,有四个基本属性,Nio的读写都是利用Buffer来实现的,简而言之,读取数据先从缓冲区读,写数据也是先写入缓冲区的。我们最常用的是ByteBuffer这个实现类,对于Java中的基本类型都有一个对应的Buffer实现类与之对应,如CharBuffer,DoubleBuffer等等

    1丶其中的四个属性的含义分别如下:
    容量(Capacity):缓冲区能够容纳的数据元素的最大数量。这一个容量在缓冲区创建时被设定,并且永远不能改变。
    上界(Limit):缓冲区的第一个不能被读或写的元素。或者说,缓冲区中现存元素的计数。
    位置(Position):下一个要被读或写的元素的索引。位置会自动由相应的 get( )和 put( )函数更新。
    标记(Mark):下一个要被读或写的元素的索引。位置会自动由相应的 get( )和 put( )函数更新。
          2丶Buffer的常见方法如下所示:  

    flip(): 写模式转换成读模式
    rewind():将 position 重置为 0 ,一般用于重复读。
    clear() :
    compact(): 将未读取的数据拷贝到 buffer 的头部位。
    mark(): reset():mark 可以标记一个位置, reset 可以重置到该位置

   3丶读取操作

 1 FileInputStream inputStream = new FileInputStream("E:\\A.txt");
 2         /**
 3          * 拿到通道
 4          */
 5         FileChannel channel = inputStream.getChannel();
 6 
 7         /**
 8          * 创建缓存区
 9          */
10         ByteBuffer buffer = ByteBuffer.allocate(1024);
11 
12         /**
13          * 读取数据到缓冲区
14          */
15         channel.read(buffer);
16 
17         buffer.flip();
18 
19         while (buffer.remaining() > 0){
20             byte b = buffer.get();
21             System.out.println(((char)b));
22         }
23         /**
24          * 关闭流
25          */
26         inputStream.close();
View Code

            4丶写入操作

 1 static private final byte message[] = { 83,83,83,83,83,83 };
 2 
 3     static public void main( String args[] ) throws Exception {
 4         FileOutputStream fout = new FileOutputStream( "e:\\A.txt" );
 5 
 6         FileChannel fc = fout.getChannel();
 7 
 8         ByteBuffer buffer = ByteBuffer.allocate( 1024 );
 9 
10         for (int i=0; i<message.length; ++i) {
11             buffer.put( message[i] );
12         }
13 
14         buffer.flip();
15 
16         fc.write( buffer );
17 
18         fout.close();
19     }
View Code

选择器Selector

  可以检测多个NIO channel,看看读或者写事件是否就绪。多个Channel以事件的方式可以注册到同一个Selector,从而达到用一个线程处理多个请求成为可能。

  使用NIO中非阻塞IO编写服务器处理程序,有三个步骤

    1.向Selector对象注册感兴趣的事件

    2.从Selector中获取感兴趣的事件

    3.根据不同事件进行相应的处理

 简单API介绍

  open:创建selector
       selectKeys:获取可用channel的集合
       select:选择就绪的通道

简单聊天室实现思路代码

服务器代码

  1 public class NioServer {
  2 
  3 
  4     public void start() throws Exception {
  5         /**
  6          * 1.创建selector
  7          */
  8         Selector selector = Selector.open();
  9         /**
 10          * 2.通过ServerSocketChannel创建channel
 11          */
 12         ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
 13 
 14         /**
 15          * 3.为channel通道绑定监听端口
 16          */
 17         serverSocketChannel.bind(new InetSocketAddress(8000));
 18         /**
 19          * 4.设置channel 为非阻塞模式
 20          */
 21         serverSocketChannel.configureBlocking(false);
 22         /**
 23          * 5.将channel 注册到selector,监听连接
 24          */
 25         serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT);
 26         System.out.println("服务器启动成功");
 27         /**
 28          * 6.循环等待新接入的连接
 29          */
 30         for(;;){
 31             int select = selector.select();
 32             if (select == 0){
 33                 continue;
 34             }
 35 
 36             /**
 37              * 7.获取可用channel的集合
 38              */
 39             Set<SelectionKey> keys = selector.selectedKeys();
 40             Iterator<SelectionKey> iterator = keys.iterator();
 41             while (iterator.hasNext()){
 42                 SelectionKey selectionKey = (SelectionKey) iterator.next();
 43                 /**
 44                  * 8.移除selctionKey
 45                  */
 46                 iterator.remove();
 47                 /**
 48                  * 处理具体业务逻辑
 49                  */
 50                 /**
 51                  * 接入事件
 52                  */
 53                 if (selectionKey.isAcceptable()){
 54                     acceptHandler(serverSocketChannel,selector);
 55                 }
 56                 /**
 57                  * 可读事件
 58                  */
 59                 if(selectionKey.isReadable()){
 60                     readHandler(selectionKey, selector);
 61                 }
 62             }
 63 
 64 
 65         }
 66 
 67 
 68         /**
 69          * 根据就绪状态,调用对应方法处理业务逻辑
 70          */
 71 
 72     }
 73 
 74     /**
 75      * 接入事件处理器
 76      */
 77     private void acceptHandler(ServerSocketChannel serverSocketChannel, Selector selector) throws Exception {
 78         /**
 79          * 如果是接入事件 创建serverSocket
 80          */
 81         SocketChannel socketChannel = serverSocketChannel.accept();
 82         /**
 83          * 设置非阻塞
 84          */
 85         socketChannel.configureBlocking(false);
 86         /**
 87          * 注册进selector中
 88          */
 89         socketChannel.register(selector, SelectionKey.OP_READ);
 90         /**
 91          * 回复服务端信息
 92          */
 93         socketChannel.write(Charset.forName("UTF-8").encode("你与聊天室里其他人都不是朋友关系,请注意隐私安全"));
 94 
 95     }
 96 
 97     private void  readHandler(SelectionKey selectionKey, Selector selector) throws Exception{
 98         /**
 99          * 要用selectionKey中获取已经就绪的channel
100          */
101         SocketChannel channel = (SocketChannel)selectionKey.channel();
102         /**
103          * 创建buffer
104           */
105         ByteBuffer buffer = ByteBuffer.allocate(1024);
106         /**
107          * 循环读取客户端数据
108          */
109         String request = "";
110         while (channel.read(buffer) > 0){
111             /**
112              * 切换读模式
113              */
114             buffer.flip();
115             /**
116              * 读取buffer中的内容
117              */
118             request  += Charset.forName("UTF-8").decode(buffer);
119 
120         }
121         /**
122          * 讲channel注册到selector上
123          */
124         channel.register(selector, SelectionKey.OP_READ);
125         /**
126          * 讲客户端发送的请求信息,广播给其他客户端
127          */
128         if (request.length() > 0){
129             broadCast(selector, channel, request);
130         }
131     }
132 
133     private void broadCast(Selector selector, SocketChannel socketChannel, String request){
134         /**
135          * 获取到已接入的客户端hannel
136          */
137         Set<SelectionKey> selectionKeys = selector.keys();
138         selectionKeys.forEach(selectionKey -> {
139             Channel channel = selectionKey.channel();
140             if (channel  instanceof SocketChannel &&
141                     channel != socketChannel){
142                 try {
143                     //将信息发送到channel客户端
144                     ((SocketChannel) channel).write(Charset.forName("UTF-8").encode(request));
145                 } catch (IOException e) {
146                     e.printStackTrace();
147                 }
148             }
149         });
150         /**
151          * 循环向所有channel广播信息
152          */
153     }
154     /**
155      *
156      * @param args
157      */
158     public static void main(String[] args) throws Exception {
159         NioServer server = new NioServer();
160         server.start();
161     }
162 }
View Code

客户端代码

 1 public class NioClient {
 2 
 3 
 4     public void start() throws Exception {
 5         /**
 6          * 连接服务器
 7          */
 8         SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 8000));
 9         /**
10          * 接收服务器地址
11          */
12         Selector selector = Selector.open();
13         socketChannel.configureBlocking(false);
14         socketChannel.register(selector, SelectionKey.OP_READ);
15         new Thread(new NioClientHandler(selector)).start();
16         /**
17          * 向服务器发送数据
18          */
19         Scanner scanner = new Scanner(System.in);
20         while (scanner.hasNextLine()){
21             String next = scanner.nextLine();
22             if (StringUtils.isNotBlank(next)){
23                 socketChannel.write(Charset.forName("UTF-8").encode(next));
24             }
25         }
26     }
27 
28     public static void main(String[] args) throws Exception {
29         new NioClient().start();
30     }
31 }
View Code

客户端线程类

 1 public class NioClientHandler implements Runnable {
 2 
 3     private Selector selector;
 4 
 5     public NioClientHandler(Selector selector) {
 6         this.selector = selector;
 7     }
 8 
 9     @Override
10     public void run() {
11         /**
12          * 循环等待新接入的连接
13          */
14         try {
15             for(;;){
16                 int select = 0;
17                 select = selector.select();
18 
19                 if (select == 0){
20                     continue;
21                 }
22 
23                 /**
24                  * 获取可用channel的集合
25                  */
26                 Set<SelectionKey> keys = selector.selectedKeys();
27                 Iterator<SelectionKey> iterator = keys.iterator();
28                 while (iterator.hasNext()){
29                     SelectionKey selectionKey = (SelectionKey) iterator.next();
30                     /**
31                      * 移除selctionKey
32                      */
33                     iterator.remove();
34                     /**
35                      * 可读事件
36                      */
37                     if(selectionKey.isReadable()){
38                         readHandler(selectionKey, selector);
39                     }
40                 }
41             }
42         } catch (Exception e) {
43             e.printStackTrace();
44         }
45 
46 
47     }
48 
49     private void  readHandler(SelectionKey selectionKey, Selector selector) throws Exception{
50         /**
51          * 要用selectionKey中获取已经就绪的channel
52          */
53         SocketChannel channel = (SocketChannel)selectionKey.channel();
54         /**
55          * 创建buffer
56          */
57         ByteBuffer buffer = ByteBuffer.allocate(1024);
58         /**
59          * 循环读取客户端数据
60          */
61         String request = "";
62         while (channel.read(buffer) > 0){
63             /**
64              * 切换读模式
65              */
66             buffer.flip();
67             /**
68              * 读取buffer中的内容
69              */
70             request  += Charset.forName("UTF-8").decode(buffer);
71 
72         }
73         /**
74          * 讲channel注册到selector上
75          */
76         channel.register(selector, SelectionKey.OP_READ);
77         /**
78          * 讲客户端发送的请求信息,广播给其他客户端
79          */
80         if (request.length() > 0){
81             System.out.println(request);
82         }
83     }
84 }
View Code

 

猜你喜欢

转载自www.cnblogs.com/yangFly/p/11985421.html