选择器
1.介绍
- Selector能够同时检测对个注册的通道上是否有事件发生
多个Channel以事件的方式注册到同一个Selector。 - 如果有事件发生,就获取事件,然后对事件进行处理,只使用一个线程就管理了多个通道。
- 只有在一个通道真正有读写事件时,才会执行。
Selector、SelectionKey、ServerSocketChannel和SocketChannel的关系
- 当客户端连接时,会通过ServerSocketChannel得到SocketChannel
- 将SocketChannel注册到Selector上(通过SelectableChannel方法注册)
- 注册后返回一个SelectionKey被Selector以Set集合管理起来
- Selector使用select方法进行监听
- 返回有事件发生的通道的SelectionKey进而获取到Channel
2.案例
模拟客户端和服务器端的通信实例
服务端代码
public class NIOService {
public static void main(String[] args) throws IOException {
// 1.创建ServerSocketChannel ->ServerSocket
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
// 1.1得到一个Selector对象
Selector selector = Selector.open();
// 1.2绑定端口 7000,在服务端监听
serverSocketChannel.socket().bind(new InetSocketAddress(7000));
// 1.3设置为非阻塞
serverSocketChannel.configureBlocking(false);
// 2.注册服务端通道到 selector 关心事件为连接时间
serverSocketChannel.register(selector, SelectionKey.OP_CONNECT);
// 3.循环等待客户端连接
while(true){
// 3.1等待1秒,没有连接事件,返回
if (selector.select(1000)==0){
System.out.println("服务器等待了一秒钟,无连接");
continue;
}
// 3.2如果返回>0 就获取到相关的selectionKey集合
// >0 表示已经获取到关注的事件了
Set<SelectionKey> selectionKeys = selector.selectedKeys();
// 3.3遍历set集合
Iterator<SelectionKey> keyIterator = selectionKeys.iterator();
while (keyIterator.hasNext()){
// 获取到SelectionKey
SelectionKey key = keyIterator.next();
// 根据key对应的通道发生的事
// 3.3.1如果是连接事件
if (key.isAcceptable()){
// 给该客户端生成一个SocketChannel
SocketChannel socketChannel = serverSocketChannel.accept();
// 将socketChannel注册到selector,关注读事件,同时给socketChannel关联一个buffer
socketChannel.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024));
}
// 3.3.2如果是读事件
if (key.isReadable()){
// 通过key获取到channel
SocketChannel socketChannel = (SocketChannel) key.channel();
// 获取到该channel关联的buffer
ByteBuffer buffer = (ByteBuffer) key.attachment();
// 将channel的数据读入buffer
socketChannel.read(buffer);
System.out.println("form 客户端" + new String(buffer.array()));
}
// 3.4从集合中移除当前的key,防止重复操作
keyIterator.remove();
}
}
}
}
服务端代码
public class NiOClient {
public static void main(String[] args) throws IOException {
// 得到一个网络通道
SocketChannel socketChannel = SocketChannel.open();
// 设置非阻塞
socketChannel.configureBlocking(false);
// 提供服务器端的 ip和端口号
InetSocketAddress inetSocketAddress = new InetSocketAddress("127.0.0.1", 7000);
// 连接服务器(非阻塞)
if (!socketChannel.connect(inetSocketAddress)) {
while (!socketChannel.finishConnect()) {
System.out.println("连接中...");
}
}
// 连接成功准备发送数据
String str = "Hello GxD";
// 直接封装字节数组到buffer
ByteBuffer buffer = ByteBuffer.wrap(str.getBytes());
// 发送数据,将buffer数据写入到channel
socketChannel.write(buffer);
System.in.read();
}
}
3.结果
服务端运行结果,等待连接
客户端运行后服务端输出