NIO缓冲区

原创
2018/06/16 10:58
阅读数 24
  1. 缓冲区的基本类
ByteBuffer  
MappedByteBuffer  
CharBuffer  
DoubleBuffer  
FloatBuffer  
IntBuffer  
LongBuffer  
ShortBuffer 
  1. 缓冲区的基本属性
容量(Capacity):缓冲区能够容纳的数据元素的最大数量。这一个容量在缓冲区创建时被设定,并且永远不能改变。
上界(Limit):缓冲区的第一个不能被读或写的元素。或者说,缓冲区中现存元素的计数。
位置(Position):下一个要被读或写的元素的索引。位置会自动由相应的 get( )和 put( )函数更新。
标记(Mark):下一个要被读或写的元素的索引。位置会自动由相应的 get( )和 put( )函数更新。
0 <= mark <= position <= limit <= capacity 
  1. API方法讲解

创建缓冲区

ByteBuffer bf=ByteBuffer.allocate(10);
位置被设为 0,而且容量和上界被设为 10,刚好经过缓冲区能够容纳的最后一个字节。
标记最初未定义。容量是固定的,但另外的三个属性可以在使用缓冲区时改变

allocateDirect()
的区别在于这块内存不由java堆管理, 但仍然在同一用户进程内.
"交互都在内核空间中发生"是说read / write等系统调用过程中对这块内存的读写.

缓冲区添加数据

buffer.put((byte)'H').put((byte)'e').put((byte)'l').put((byte)'l').put((byte)'o');

修改后的

buffer.put(0,(byte)'M').put((byte)'w'); 

反转 Buffer.flip();

/** 
 * 填充和释放缓冲区 
 */  
public class BufferFillDrain  
{  
    public static void main(String[] args)  
    {  
    	//ByteBuffer bf=ByteBuffer.allocate(10);
        CharBuffer buffer = CharBuffer.allocate(100);  
        while(fillBuffer(buffer)){  
            buffer.flip();  
            drainBuffer(buffer);  
            buffer.clear();  
        }  
          
    }  
      
    private static void drainBuffer(CharBuffer buffer)  
    {  
        while(buffer.hasRemaining())  
        {  
            System.out.println(buffer.get());  
        }  
        System.out.println(" ");  
    }  
    private static boolean fillBuffer(CharBuffer buffer)  
    {  
        if(index >= strings.length)  
        {  
            return (false);  
        }  
        String str = strings[index++];  
        for(int i=0;i<str.length();i++)  
        {  
            buffer.put(str.charAt(i));  
        }  
        return (true);  
    }  
    private static int index = 0;  
      
    private static String[] strings={"A random string value",  
        "The product of an infinite number of monkeys",  
        "if you like nio please go  go go",  
        "Help me Help me"  
    };  
} 

 compact() compact(): 将未读取完的数据(position 与 limit 之间的数据)移动到缓冲区开头,并将 position 设置为这段数据末尾的下一个位置。其实就等价于重新向缓冲区中写入了这么一段数据。
这里发生了几件事。您会看到数据元素 2-5 被复制到 0-3 位置。位置 4 和 5 不受影响,
但现在正在或已经超出了当前位置,因此是“死的”。它们可以被之后的 put()调用重写。
还要注意的是,位置已经被设为被复制的数据元素的数目。也就是说,缓冲区现在被定位在缓
冲区中最后一个“存活”元素后插入数据的位置。最后,上界属性被设置为容量的值,因此缓
冲区可以被再次填满。调用 compact()的作用是丢弃已经释放的数据,保留未释放的数据,
并使缓冲区对重新填充容量准备就绪

buffer.position(2).mark().position(4); 

reset( )

两个缓冲区被认为相等的充要条件是: 
32 
 
 
两个对象类型相同。包含不同数据类型的 buffer 永远不会相等,而且 buffer
绝不会等于非 buffer 对象。 
 
两个对象都剩余同样数量的元素。Buffer 的容量不需要相同,而且缓冲区中剩
余数据的索引也不必相同。但每个缓冲区中剩余元素的数目(从位置到上界)必须相
同。 
 
在每个缓冲区中应被 Get()函数返回的剩余数据元素序列必须一致

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Date;
import java.util.Iterator;
import java.util.Scanner;

import org.junit.Test;

/*
 * 一、使用 NIO 完成网络通信的三个核心:
 * 
 * 1. 通道(Channel):负责连接
 * 		
 * 	   java.nio.channels.Channel 接口:
 * 			|--SelectableChannel
 * 				|--SocketChannel
 * 				|--ServerSocketChannel
 * 				|--DatagramChannel
 * 
 * 				|--Pipe.SinkChannel
 * 				|--Pipe.SourceChannel
 * 
 * 2. 缓冲区(Buffer):负责数据的存取
 * 
 * 3. 选择器(Selector):是 SelectableChannel 的多路复用器。用于监控 SelectableChannel 的 IO 状况
 * 
 */
public class TestNonBlockingNIO {
	
	//客户端
	@Test
	public void client() throws IOException{
		//1. 获取通道
		SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898));
		
		//2. 切换非阻塞模式
		sChannel.configureBlocking(false);
		
		//3. 分配指定大小的缓冲区
		ByteBuffer buf = ByteBuffer.allocate(1024);
		
		//4. 发送数据给服务端
		Scanner scan = new Scanner(System.in);
		
		while(scan.hasNext()){
			String str = scan.next();
			buf.put((new Date().toString() + "\n" + str).getBytes());
			buf.flip();
			sChannel.write(buf);
			buf.clear();
		}
		
		//5. 关闭通道
		sChannel.close();
	}

	
	
	//服务端
	@Test
	public void server() throws IOException{
		//1. 获取通道
		ServerSocketChannel ssChannel = ServerSocketChannel.open();
		
		//2. 切换非阻塞模式
		ssChannel.configureBlocking(false);
		
		//3. 绑定连接
		ssChannel.bind(new InetSocketAddress(9898));
		
		//4. 获取选择器
		Selector selector = Selector.open();
		
		//5. 将通道注册到选择器上, 并且指定“监听接收事件”
		ssChannel.register(selector, SelectionKey.OP_ACCEPT);
		
		//6. 轮询式的获取选择器上已经“准备就绪”的事件
		while(selector.select() > 0){
			
			//7. 获取当前选择器中所有注册的“选择键(已就绪的监听事件)”
			Iterator<SelectionKey> it = selector.selectedKeys().iterator();
			
			while(it.hasNext()){
				//8. 获取准备“就绪”的是事件
				SelectionKey sk = it.next();
				
				//9. 判断具体是什么事件准备就绪
				if(sk.isAcceptable()){
					//10. 若“接收就绪”,获取客户端连接
					SocketChannel sChannel = ssChannel.accept();
					
					//11. 切换非阻塞模式
					sChannel.configureBlocking(false);
					
					//12. 将该通道注册到选择器上
					sChannel.register(selector, SelectionKey.OP_READ);
				}else if(sk.isReadable()){
					//13. 获取当前选择器上“读就绪”状态的通道
					SocketChannel sChannel = (SocketChannel) sk.channel();
					
					//14. 读取数据
					ByteBuffer buf = ByteBuffer.allocate(1024);
					
					int len = 0;
					while((len = sChannel.read(buf)) > 0 ){
						buf.flip();
						System.out.println(new String(buf.array(), 0, len));
						buf.clear();
					}
				}
				
				//15. 取消选择键 SelectionKey
//				it.remove();
			}
		}
	}
}

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部