java nio channel 学习笔记

Channel直译就是通道的意思,通道表示对数据源头和数据目标流经途径的抽象描述,和io中的InputStream和OutputStream类似。

首先借用网络上一张Channel的类图:


java nio channel 学习笔记

从channel的类层次结构来看在接口层面有区分读和写两种操作(ReadableByteChannel和WritableByteChannel),这点类似InputStream和OutputStream。但在实现类FileChannel和SocketChannel都实现了读写的接口,也就是既可以从通道中读取数据,又可以写数据到通道,这和io中单向的流是不同的,即nio中的通道是可以双向读写。

 

从类图中也可以看到nio中几个重要的通道实现:

 FileChannel, SocketChannel,ServerSocketChannel, DatagramChannel。


 1. FileChannel
文件的读写通道。可从现有的 FileInputStream 、 FileOutputStream 或 RandomAccessFile 对象获得文件通道,方法是调用该对象的 getChannel 方法,这会返回一个连接到相同底层文件的文件通道。

文件通道的状态与其 getChannel 返回该通道的对象密切相关。

在各种情况下指定要求“允许读取操作”、“允许写入操作”或“允许读取和写入操作”的某个实例。
通过 FileInputStream 实例的 getChannel 方法所获得的通道将允许进行读取操作。
通过 FileOutputStream 实例的 getChannel 方法所获得的通道将允许进行写入操作。
如果使用模式 "r" 创建 RandomAccessFile 实例,则通过该实例的 getChannel 方法所获得的通道将允许进行读取操作,如果使用模式 "rw" 创建实例,则获得的通道将允许进行读取和写入操作。

FileInputStream fi = new FileInputStream(infile);
	FileOutputStream fo = new FileOutputStream(outfile);

	FileChannel fiChannel = fi.getChannel();
	FileChannel foChannel = fo.getChannel();

	ByteBuffer buffer = ByteBuffer.allocate(1024);

	while (fiChannel.read(buffer) != -1) {
		buffer.flip();
		foChannel.write(buffer);
		buffer.clear();
	}

	
	
	RandomAccessFile raf = new RandomAccessFile(fileName , "rw");
	FileChannel fc = raf.getChannel();
	
	MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_WRITE, 0, 1024);
	mbb.put(0, (byte) 97);
	mbb.put(1023, (byte) 122);

 

2. SocketChannel 和 ServerSocketChannel 是对原io包中的 Socket 和 ServerSocket 的可选择的无阻塞通道实现。从类结构来看都实现了SelectableChannel,结合selector实现无阻塞io,Selector为ServerSocketChannel 监控接收客户端连接就绪事件, 为 SocketChannel 监控连接服务器就绪, 读就绪和写就绪事件。
ServerSocketChannel稍微特殊,并没有实现ReadableByteChannel,专门负责监听传入的连接和创建新的 SocketChannel对象.由于ServerSocketChannel没有bind( )方法,因此有必要取出对等的socket并使用它来绑定到一个端口以开始监听连接。
下面一个简单的例子是一阻塞等待请求连接,单线程处理的方式。无阻塞模式要结合Selector事件驱动模型使用。

 

ServerSocketChannel ssc = ServerSocketChannel.open();
        ssc.socket().bind(new InetSocketAddress(port));

        while(true){
			SocketChannel socketChannel = serverSocketChannel.accept();
			if(socketChannel != null){
				//...
			}
		}

 

结合Selector使用的简单无阻塞模式:

 

 

selector = Selector.open();
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
ssc.socket().bind(new InetSocketAddress(port));

ssc.register(selector, SelectionKey.OP_ACCEPT);

while (true) {
	int selected = selector.select();

	if (selected > 0) {
		Iterator<SelectionKey> selectedKeys = selector.selectedKeys().iterator();
		while (selectedKeys.hasNext()) {
			SelectionKey key = selectedKeys.next();

			if ((key.readyOps() & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT) {
				ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
				SocketChannel sc = ssc.accept();
				sc.configureBlocking(false);
				
				sc.register(selector, SelectionKey.OP_READ);
				selectedKeys.remove();
			} else if ((key.readyOps() & SelectionKey.OP_READ) == SelectionKey.OP_READ) {
				SocketChannel sc = (SocketChannel) key.channel();
				IoBuffer buf = IoBuffer.allocate(1024);
				int readBytes = 0;
				try {
					int ret;
					while ((ret = sc.read(buf.buf())) > 0) {
						readBytes += ret;
						if (!buf.hasRemaining()) {
							break;
						}
					}
				} catch (IOException e) {
					e.printStackTrace();
				} finally {
					buf.flip();
				}
				
				// 处理buf中的数据。。。

				sc.register(selector, SelectionKey.OP_WRITE);
				selectedKeys.remove();
			} else if ((key.readyOps() & SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE) {
				SocketChannel sc = (SocketChannel) key.channel();

				String writeInfo = "Server received success !";
				sc.write(Charset.forName("UTF-8").encode(writeInfo));
				
				sc.close();
				key.cancel();
			}
		}
	}
}

3. DatagramChannel是一个能收发UDP包的通道。因为UDP是无连接的网络协议,所以不能像其它通道那样读取和写入,它发送和接收的是数据包。

 

你可能感兴趣的:(java,nio)