NIO学习笔记五

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lzx_longyou/article/details/51980285

文件锁定

    要获取文件的一部分上的锁,需要调用FileChannel上的locak()方法。注意,如果要获取一个排它锁,您必须以写方式打开文件。

    第二行中调用lock方法来获取锁,position参数表示锁的起始位置,size表示锁的范围,shared是一个boolean值,true表示是一个共享锁,false表示是一个排它锁。

FileChannel channel = out.getChannel();
FileLock lock = channel.lock(position, size, shared);
lock.release();

异步I/O

    通常程序中调用read()方法时,程序会阻塞直至有可供读取的数据。同样,调用write()将会阻塞直至数据能够写入。而异步I/O是一种没有阻塞的读写数据的方法,我们将感兴趣的特性的I/O事件注册到选择器上面,当这些事件发生时,系统将会告诉我们。异步 I/O 的一个优势在于,它允许您同时根据大量的输入和输出执行 I/O。同步程序常常要求助于轮询,或者创建许许多多的线程以处理大量的连接。使用异步 I/O,您可以监听任何数量的通道上的事件,不用轮询,也不用额外的线程。

    异步I/O中的核心对象是Selector,表示一个选择器,它就是我们注册感兴趣I/O事件的地方,当这些事件发生,这个选择器就会通知我们。每次返回主循环,我们都要调用 select 的 Selector()方法,并取得一组 SelectionKey。每个键代表一个 I/O 事件。我们处理事件,从选定的键集中删除 SelectionKey,然后返回主循环的顶部。

public class MultiPortEcho {
	private int ports[];	//用于保存端口号
	private ByteBuffer echoBuffer = ByteBuffer.allocate(1024);

	public MultiPortEcho(int ports[]) throws IOException {
		this.ports = ports;
		go();
	}

	private void go() throws IOException {
		//创建一个选择器对象
		Selector selector = Selector.open();

		//在每个端口上开启监听,绑定端口号,并注册到选择器上
		for (int i = 0; i < ports.length; ++i) {
			ServerSocketChannel ssc = ServerSocketChannel.open();
			//设置为false表示非阻塞
			ssc.configureBlocking(false);
			ServerSocket ss = ssc.socket();
			InetSocketAddress address = new InetSocketAddress(ports[i]);
			ss.bind(address);
			
			//将新打开的 ServerSocketChannels 注册到 Selector上
			//SelectionKey 代表这个通道在此 Selector 上的这个注册。
			//当某个 Selector 通知您某个传入事件时,它是通过提供对应于该事件的 SelectionKey 来进行的。
			//SelectionKey 还可以用于取消通道的注册。
			SelectionKey key = ssc.register(selector, SelectionKey.OP_ACCEPT);

			System.out.println("Going to listen on " + ports[i]);
		}

		while (true) {
			//这个方法会阻塞,直到至少有一个已注册的事件发生。
			//当一个或者更多的事件发生时, select() 方法将返回所发生的事件的数量。
			int num = selector.select();

			Set selectedKeys = selector.selectedKeys();
			Iterator it = selectedKeys.iterator();

			while (it.hasNext()) {
				SelectionKey key = (SelectionKey) it.next();

				if ((key.readyOps() & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT) {
					//接受新连接
					ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
					SocketChannel sc = ssc.accept();
					//将新连接设置为非阻塞
					sc.configureBlocking(false);

					//将连接注册到选择器上, OP_READ 参数表示将 SocketChannel注册用于读取而不是接受新连接。
					SelectionKey newKey = sc.register(selector, SelectionKey.OP_READ);
					//将处理过的 SelectionKey 从选定的键集合中删除
					//如果我们没有删除处理过的键,那么它仍然会在主集合中以一个激活的键出现,这会导致我们尝试再次处理它.
					it.remove();

					System.out.println("Got connection from " + sc);
				} else if ((key.readyOps() & SelectionKey.OP_READ) == SelectionKey.OP_READ) {
					//接收数据
					SocketChannel sc = (SocketChannel) key.channel();
					
					int bytesEchoed = 0;
					while (true) {
						echoBuffer.clear();

						int r = sc.read(echoBuffer);

						if (r <= 0) {
							break;
						}

						echoBuffer.flip();

						sc.write(echoBuffer);
						bytesEchoed += r;
					}

					System.out.println("Echoed " + bytesEchoed + " from " + sc);

					it.remove();
				}
			}
		}
	}

	public static void main(String args[]) throws Exception {
		if (args.length <= 0) {
			System.err.println("Usage: java MultiPortEcho port [port port ...]");
			System.exit(1);
		}

		int ports[] = new int[args.length];

		for (int i = 0; i < args.length; ++i) {
			ports[i] = Integer.parseInt(args[i]);
		}

		new MultiPortEcho(ports);
	}
}



猜你喜欢

转载自blog.csdn.net/lzx_longyou/article/details/51980285