欢迎访问悦橙教程(wld5.com),关注java教程。悦橙教程  java问答|  每日更新
页面导航 : > > 文章正文

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

来源: javaer 分享于  点击 7649 次 点评:169

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

 

 

相关文章

    暂无相关文章
相关栏目:

用户点评