黑马程序员_O‘Reilly java nio学习笔记之选择器_通过一个程序初识选择器

---------------------- android培训java培训、期待与您交流! ---------------------- 

应用程序可调用SelectableChannel的register方法将其注册到指定的Selector上,当Selector上某些SelectableChannel上有需要处理的IO操作是,程序可以调用Selector实例的select方法获取他们的数量,并通过selectedKeys方法返回他们对应的SelectableChannel集合,通过该集合就可以获取所有需要处理的IO操作的SelectableChannel集。先看一个示例,以增加对选择器的理解:

客户端:

有两条线程,一条获取键盘输入,并将其发送到服务器,另外一条接受服务器数据,并处理。

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.SocketChannel;
import java.util.Scanner;

public class NClient {
	// 定义检测SocketChannel的Selector对象
	private Selector selector = null;
	// 客户端SocketChannel
	private SocketChannel sc = null;
	//键盘读取缓冲区
	ByteBuffer keyRead = null;
	public void init() throws IOException {
		//实例化选择器
		selector = Selector.open();
		InetSocketAddress isa = new InetSocketAddress("127.0.0.1", 30000);
		sc = SocketChannel.open(isa);
		sc.configureBlocking(false);
		// 将SocketChannel对象注册到指定Selector
		sc.register(selector, SelectionKey.OP_READ);
		// 启动读取服务器端数据的线程
		new ClientThread().start();
		// 创建键盘输入流
		Scanner scan = new Scanner(System.in);
		while (scan.hasNextLine()) {
			// 读取键盘输入
			String line = scan.nextLine();
			keyRead = ByteBuffer.wrap(line.getBytes());
			sc.write(keyRead);
		}
	}

	// 定义读取服务器数据的线程
	private class ClientThread extends Thread {
		public void run() {
			try {
				//检查与selector相关的通道是否有需要处理的IO操作,阻塞方法。
				while (selector.select() > 0) {
					// 遍历每个有可用IO操作Channel对应的SelectionKey
					for (SelectionKey sk : selector.selectedKeys()) {
						// 删除正在处理的SelectionKey
						selector.selectedKeys().remove(sk);
						// 如果该SelectionKey对应的Channel中有可读的数据
						if (sk.isReadable()) {
							SocketChannel sc = (SocketChannel) sk.channel();
							ByteBuffer buff = ByteBuffer.allocate(1024);
							String content = "";
							while (sc.read(buff) > 0) {								
								buff.flip();
								// buff.array()将整个缓冲区都解析为数组,下面取有内容的一部分。
								content += new String(buff.array(), 0,buff.limit());
								buff.clear();
							}
							System.out.println("聊天信息:" + content);
						}
					}
				}
			} catch (IOException ex) {
				ex.printStackTrace();
			}
		}
	}

	public static void main(String[] args) throws IOException {
		new NClient().init();
	}
}


服务器端:
接受客户端数据,然后将数据发送给所有客户端。


import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.net.*;

public class NServer {
	// 用于检测所有Channel状态的Selector
	private Selector selector = null;
	public void init() throws IOException {
		selector = Selector.open();
		ServerSocketChannel server = ServerSocketChannel.open();
		InetSocketAddress isa = new InetSocketAddress("127.0.0.1", 30000);
		server.socket().bind(isa);
		server.configureBlocking(false);
		// 将server注册到指定Selector对象
		server.register(selector, SelectionKey.OP_ACCEPT);
		while (selector.select() > 0) {
			// 依次处理selector上的每个已选择的SelectionKey
			for (SelectionKey sk : selector.selectedKeys()) {
				// 从selector上的已选择Key集中删除正在处理的SelectionKey
				selector.selectedKeys().remove(sk);
				// 如果sk对应的通道包含客户端的连接请求
				if (sk.isAcceptable()) {
					// 调用accept方法接受连接,产生服务器端对应的SocketChannel
					SocketChannel sc = server.accept();
					// 设置采用非阻塞模式
					sc.configureBlocking(false);
					// 将该SocketChannel也注册到selector
					sc.register(selector, SelectionKey.OP_READ);
				}
				// 如果sk对应的通道有数据需要读取
				if (sk.isReadable()) {
					// 获取该SelectionKey对应的Channel,该Channel中有可读的数据
					SocketChannel sc = (SocketChannel) sk.channel();
					// 定义准备执行读取数据的ByteBuffer
					ByteBuffer buff = ByteBuffer.allocate(1024);
					String content = "";
					// 开始读取数据
					try {
						while (sc.read(buff) > 0) {
							buff.flip();
							content += new String(buff.array(), 0, buff.limit());
							buff.clear();
						}
						// 打印从该sk对应的Channel里读取到的数据
						System.out.println("=====" + content);
					}
					// 如果捕捉到该sk对应的Channel出现了异常,即表明该Channel
					// 对应的Client出现了问题,所以从Selector中取消sk的注册
					catch (IOException ex) {
						// 从Selector中删除指定的SelectionKey
						sk.cancel();
						if (sk.channel() != null) {
							sk.channel().close();
						}
					}
					// 如果content的长度大于0,即聊天信息不为空
					if (content.length() > 0) {
						// 遍历该selector里注册的所有SelectKey
						for (SelectionKey key : selector.keys()) {
							// 获取该key对应的Channel
							Channel targetChannel = key.channel();
							// 如果该channel是SocketChannel对象
							if (targetChannel instanceof SocketChannel) {
								// 将读到的内容写入该Channel中
								SocketChannel dest = (SocketChannel) targetChannel;
								dest.write(ByteBuffer.wrap(content.getBytes()));
							}
						}
					}
				}
			}
		}
	}

	public static void main(String[] args) throws IOException {
		new NServer().init();
	}
}


以上程序是选择器的基本应用,他可以以一条线程处理多个客户端请求。后面有关于选择器的详解。



---------------------- android培训java培训、期待与您交流! ----------------------

详细请查看:http://edu.csdn.net/heima



    
    


      
      





      
      



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值