`
salever
  • 浏览: 250297 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Java nio 整理整理

阅读更多

看了看Java的nio类库,整理一下思路。

 

1,Buffer

jdk官方文档上对Buffer的描述为:

Sun 写道
缓冲区是特定基本类型元素的线性有限序列。除内容外,缓冲区的基本属性还包括容量、限制和位置:

缓冲区的容量 是它所包含的元素的数量。缓冲区的容量不能为负并且不能更改。

缓冲区的限制 是第一个不应该读取或写入的元素的索引。缓冲区的限制不能为负,并且不能大于其容量。

缓冲区的位置 是下一个要读取或写入的元素的索引。缓冲区的位置不能为负,并且不能大于其限制。

对于每个非 boolean 基本类型,此类都有一个子类与之对应。

 

Buffer里面的这个几个变量,控制了buffer的put和get行为,之间的相关关系为:

0 <= 标记 <= 位置 <= 限制 <= 容量

新创建的缓冲区总有一个 0 位置和一个未定义的标记。初始限制可以为 0,也可以为其他值,这取决于缓冲区类型及其构建方式。一般情况下,缓冲区的初始内容是未定义的。

 

几个常见的操作说明:

  • clear() 使缓冲区为一系列新的通道读取或相对放置 操作做好准备:它将限制设置为容量大小,将位置设置为 0。

  • flip() 使缓冲区为一系列新的通道写入或相对获取 操作做好准备:它将限制设置为当前位置,然后将位置设置为 0。 这个操作尤其在read完buffer以后,使用之前调用一次

  • rewind() 使缓冲区为重新读取已包含的数据做好准备:它使限制保持不变,将位置设置为 0。

 

我们常用的也就是ByteBuffer、CharBuffer了。

 

2,Channel

写道
用于 I/O 操作的连接。

通道表示到实体,如硬件设备、文件、网络套接字或可以执行一个或多个不同 I/O 操作(如读取或写入)的程序组件的开放的连接。

通道可处于打开或关闭状态。创建通道时它处于打开状态,一旦将其关闭,则保持关闭状态。一旦关闭了某个通道,试图对其调用 I/O 操作就会导致 ClosedChannelException 被抛出。通过调用通道的 isOpen 方法可测试通道是否处于打开状态。

 

 

Channel有很多实现,AbstractInterruptibleChannel, AbstractSelectableChannel, DatagramChannel, FileChannel, Pipe.SinkChannel, Pipe.SourceChannel, SelectableChannel, ServerSocketChannel, SocketChannel ,下面演示一下用FileChannel进行文件复制:

 

	/**
	 * Copy file using nio.
	 * @param inFile
	 * @param outFile
	 */
	public static void copyFile(String inFile, String outFile) {
		try {
			FileInputStream in = new FileInputStream(inFile);
			FileOutputStream out = new FileOutputStream(outFile);
			FileChannel cIn = in.getChannel();
			FileChannel cOut = out.getChannel();
			
			ByteBuffer buffer = ByteBuffer.allocate(1024);
			int length = 0;
			while(true){
				buffer.clear();
				length = cIn.read(buffer);
				if(length == -1){
					break;
				}
				buffer.flip();
				cOut.write(buffer);
			}
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

 

3,Selector

selector是nio实现非阻塞式通信的核心,它是SelectableChannel 对象的多路复用器。

 

关于selector的介绍,SUN的jdk 文档里面已经有很多了,这里不赘述了,下面演示一下如何使用selector和channel实现非阻塞的网络通信。

 

ServerDemo.java

/**
 * @author
 */
public class ServerDemo {

	public static void main(String[] args) throws Exception {
		boolean readAllready = true;
		Charset charset = Charset.forName("utf-8");
		ByteBuffer buffer = ByteBuffer.allocate(1024);
		ServerSocketChannel ssocketChannel = ServerSocketChannel.open();
		ssocketChannel.socket().bind(new InetSocketAddress(6018));
		LogUtil.info("启动了一个ServerSocketChannel");
		ssocketChannel.configureBlocking(false);
		Selector selector = Selector.open();
		ssocketChannel.register(selector, SelectionKey.OP_ACCEPT);
		LogUtil.info("等待客户端连接......");

		String content = "";
		
		while (selector.select() > 0) {
			Set<SelectionKey> set = selector.selectedKeys();
			for (SelectionKey key : set) {
				SocketChannel channel;
				if(key.isAcceptable()){
					channel = ssocketChannel.accept();
					LogUtil.error("有新的客户端连接:" + channel);
					LogUtil.error("地址是: " + channel.socket());
					channel.configureBlocking(false);
					channel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
				}
				
				if(key.isReadable()){
					readAllready = true;
					LogUtil.error("有新的读取");
					channel = (SocketChannel) key.channel();
					channel.read(buffer);
					buffer.flip();
					content = charset.decode(buffer).toString() + "\r\n";
					LogUtil.info("Read from clent <<<<<<<<<<< " + content);
					buffer.clear();
				}
				
				if(key.isWritable()){
					if(readAllready){
						channel = (SocketChannel) key.channel();
						buffer.put(("Write into client >>>>>>>>> " + content).getBytes());
						buffer.flip();
						channel.write(buffer);
						buffer.clear();
						Thread.sleep(1000);
						readAllready = false;
					}

				}
			}
			set.clear();
		}
		LogUtil.info("服务器推出");

	}

}

 

其中LogUtil是一个打印日志的泪类,可以用System.out代替。

 

测试这个服务器,可以自己启动一个连接到6018端口的客户端,比如telnet 本地IP 6018,就可以看到效果了

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics