认知了概念再来学习 API

 

1、缓冲区的 API

要彻底理解缓冲区,必须知道缓冲区的四个属性,mark,position,limit,capacity,只需要跑一遍代码就知道了。

(1)分配一定大小的缓冲区


  1. //1.分配一个指定大小的缓冲区 
  2. ByteBuffer buffer = ByteBuffer.allocate(10); 
  3. System.out.println("———alocate"); 
  4. System.out.println("position:" + buffer.position()); 
  5. System.out.println("limit:" + buffer.limit()); 
  6. System.out.println("capacity:" + buffer.capacity()); 

运行结果:


  1. ———alocate———– 
  2. position:0 
  3. limit:10 
  4. capacity:10 

这里我们分配了 10 个字节的缓冲区,也就是在 ByteBuffer 的 final byte[] hb; 属性上开辟了 10 个字节的空间。

所以容量 capacity 为 10 , limit 可读写数据的最大位置 也是 10 ,position 为可以操作数据的位置为 0 。

(2)往缓冲区写数据


  1. // 2.写入数据到缓冲区 
  2. String str = "abcde"
  3. System.out.println("————put————"); 
  4. buffer.put(str.getBytes(StandardCharsets.UTF_8)); 
  5. System.out.println("position:" + buffer.position()); 
  6. System.out.println("limit:" + buffer.limit()); 
  7. System.out.println("capacity:" + buffer.capacity()); 

运行结果:


  1. ————put———— 
  2. position:5 
  3. limit:10 
  4. capacity:10 

这里我们往缓冲区写了 5 个字节的数据,那么 capacity 和 limit 都还是10,但是 position 为 5 了,因为前面已经写入了 5 个了

(3)切换成读数据的模式


  1. // 3.切换成读数据的模式 
  2. buffer.flip(); 
  3. System.out.println("————flip————"); 
  4. System.out.println("position:" + buffer.position()); 
  5. System.out.println("limit:" + buffer.limit()); 
  6. System.out.println("capacity:" + buffer.capacity()); 

那我们现在想从缓冲区读取一些数据出来,就需要切换成 flip 模式,flip 会改变一些属性的值

运行结果:


  1. ————flip———— 
  2. position:0 
  3. limit:5 
  4. capacity:10 

flip 会改变 position 的值为 0 ,并且 limit 为5,表示我要从头开始读,并且只能读到 5 的位置

(4)读取一些数据


  1. // 4. 读取数据 
  2. System.out.println("————get————"); 
  3. byte[] dest = new byte[buffer.limit()]; 
  4. buffer.get(dest); 
  5. System.out.println(new String(dest,0,dest.length)); 
  6. System.out.println("position:" + buffer.position()); 
  7. System.out.println("limit:" + buffer.limit()); 
  8. System.out.println("capacity:" + buffer.capacity()); 

运行结果:


  1. ————get———— 
  2. abcde 
  3. position:5 
  4. limit:5 
  5. capacity:10 

读取了数据之后,position 就变成 5 了,表示我已经读取到 5 了。

(5)重复读


  1. //5.rewind() 
  2. buffer.rewind(); 
  3. System.out.println("————rewind————"); 
  4. System.out.println("position:" + buffer.position()); 
  5. System.out.println("limit:" + buffer.limit()); 
  6. System.out.println("capacity:" + buffer.capacity()); 

运行结果:


  1. ————rewind———— 
  2. position:0 
  3. limit:5 
  4. capacity:10 

rewind 表示重复读取 buffer 里面的数据

(6)清除数据


  1. //6.clear() 
  2. buffer.clear(); 
  3. System.out.println("————clear————"); 
  4. System.out.println("position:" + buffer.position()); 
  5. System.out.println("limit:" + buffer.limit()); 
  6. System.out.println("capacity:" + buffer.capacity()); 

运行结果:


  1. ————clear———— 
  2. position:0 
  3. limit:10 
  4. capacity:10 

clear() 之后,position 回到了 0 ,limit 回到了 10,又可以重头开始写数据了,能写 10 个字节。

但是要注意的是,缓冲里面的数据并没有清空掉,数据还在里面,处于被“遗忘”状态。这几个指针回到了最初的状态。

(7)标记

这是第四个属性:mark。

mark 可以记录 position 的位置。可以通过 reset() 方法回到 mark 的位置。


  1.  @Test 
  2.     public void test2() { 
  3.         // 分配 10 个字节 
  4.         String str = "abcde"
  5.         ByteBuffer buffer = ByteBuffer.allocate(10); 
  6.         buffer.put(str.getBytes(StandardCharsets.UTF_8)); 
  7.  
  8.         // 切换到读模式,读取 2 个字节 
  9.         buffer.flip(); 
  10.         byte[] dest = new byte[buffer.limit()]; 
  11.         buffer.get(dest, 0, 2); 
  12.         System.out.println(new String(dest, 0, 2)); 
  13.         System.out.println(buffer.position()); 
  14.  
  15.         // mark 一下记录当前位置 
  16.         buffer.mark(); 
  17.  
  18.         // 又读取两个字节 
  19.         buffer.get(dest, 2, 2); 
  20.         System.out.println(new String(dest, 2, 2)); 
  21.         System.out.println(buffer.position()); 
  22.  
  23.         // reset,回到 mark 的位置 
  24.         buffer.reset(); 
  25.         System.out.println(buffer.position()); 
  26.     } 
  27.  
  28. 执行结果: 
  29.  
  30. “`tex 
  31. ab 
  32. cd 

2、使用通道、缓冲区、选择器完成一个网络程序

(1)服务端


  1. @Test 
  2.  public void testServer() throws IOException { 
  3.      ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); 
  4.      serverSocketChannel.configureBlocking(false); 
  5.  
  6.      serverSocketChannel.bind(new InetSocketAddress(8989)); 
  7.  
  8.      Selector selector = Selector.open(); 
  9.      serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); 
  10.  
  11.      while (selector.select() > 0) { 
  12.          Iterator<SelectionKey> iterator = selector.selectedKeys().iterator(); 
  13.          while (iterator.hasNext()) { 
  14.              SelectionKey key = iterator.next(); 
  15.              if (key.isAcceptable()) { 
  16.                  SocketChannel socketChannel = serverSocketChannel.accept(); 
  17.                  socketChannel.configureBlocking(false); 
  18.                  socketChannel.register(selector, SelectionKey.OP_READ); 
  19.              } else if (key.isReadable()) { 
  20.                  SocketChannel channel = (SocketChannel) key.channel(); 
  21.                  ByteBuffer byteBuffer = ByteBuffer.allocate(1024); 
  22.                  int len = 0; 
  23.                  while ((len = channel.read(byteBuffer)) > 0) { 
  24.                      byteBuffer.flip(); 
  25.                      System.out.println(new String(byteBuffer.array(), 0, len)); 
  26.                      byteBuffer.clear(); 
  27.                  } 
  28.              } 
  29.          } 
  30.  
  31.          iterator.remove(); 
  32.      } 
  33.  } 

1、首先使用 ServerSocketChannel.open(),打开一个通道,设置成非阻塞模式;

2、绑定到 8989 端口上;

3、把通道注册到选择器上;

4、while 循环,选择器上是否有事件,如果事件是客户端的连接事件,则打开一个 SocketChannel,注册成非阻塞模式,并且往选择器上注册一个读数据的事件;

5、当客户端发送数据过来的时候,就可以打开一个通道,读取缓冲区上的数据;

6、并且此时,服务端是可以同时接受多个客户端的请求的。

(2)客户端


  1. @Test 
  2.  public void testClient() throws IOException { 
  3.      SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 8989)); 
  4.      socketChannel.configureBlocking(false); 
  5.  
  6.      ByteBuffer byteBuffer = ByteBuffer.allocate(1024); 
  7.      byteBuffer.put(new Date().toString().getBytes(StandardCharsets.UTF_8)); 
  8.      byteBuffer.flip(); 
  9.      socketChannel.write(byteBuffer); 
  10.      byteBuffer.clear(); 
  11.  
  12.      socketChannel.close(); 
  13.  
  14.  } 

1、客户端打开一个 SocketChannel,配置成非阻塞模式;

2、使用 ByteBuffer 发送数据(注意发送之前,要 flip);

3、关闭通道。

【声明】:芜湖站长网内容转载自互联网,其相关言论仅代表作者个人观点绝非权威,不代表本站立场。如您发现内容存在版权问题,请提交相关链接至邮箱:bqsm@foxmail.com,我们将及时予以处理。

相关文章