Java NIO(4) Selector 选择器,Pipe 通道
Java NIO
4. Java NIO 核心组件:Selector 选择器,Pipe 管道
Selector 选择器
Java NIO 的 Selector 选择器用于监视操控通道的行为,可用作可以进入非阻塞模式的特殊类型的通道,它可以检查一个或多个NIO通道,并确定哪个通道准备好可以进行通信,可以进行写入、读取等;
Selector 常用的 API
static Selectoropen() | 创建一个选择器; |
voidclose() | 关闭选择器; |
intselect([long time]) | 返回一组 key,其相应的通道已经为 I/O 操作准备就绪; |
intselectNow() | 同上,不过立即执行,不等待; |
Set<SelectionKey>selectedKeys() | 返回该选择器已经选择的 key set; |
一个基本的使用如下:
Selector selector = Selector.open(); //创建选择器
selector.select(); //选择当下的一组 key
Set<SelectionKey> selectedKeys = selector.selectedKeys(); //或者这些 key 的set
Iterator<SelectionKey> iter = selectedKeys.iterator(); //获取迭代器并进行迭代操作
while(iter.hasNext()){
SelectionKey key = keyIterator.next();
if(key.isConnectable()) {
// The connection was established with a remote server.
} else if (key.isAcceptable()) {
// The connection was accepted by a ServerSocketChannel.
} else if (key.isWritable()) {
// The channel is ready for writing
} else if (key.isReadable()) {
// The channel is ready for reading
}
keyIterator.remove();
}
实际示例
以下以 Selector 在 ServerSocketChannel,SocketChannel 中的使用,示例 Selector 的实际使用场景:
服务端 TCPServer
public class TcpServer {
public static void main(String[] args) throws IOException {
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
ssc.bind(new InetSocketAddress("127.0.0.1",2333));
Selector selector = Selector.open(); //创建选择器
ssc.register(selector, ssc.validOps()); // 向ServerSocketChannel 注册 Selector
while(true){
System.out.println("the number of selected keys are: " + selector.select()); //获取已经准备好的Channel数量
Set keys = selector.selectedKeys();
Iterator<SelectionKey> iter = keys.iterator(); // 获取 selectedKeys 遍历器
while(iter.hasNext()){
SelectionKey key = iter.next();
if(key.isAcceptable()){ //当客户端通道可接收时
SocketChannel client = ssc.accept(); //获取客户端 SocketChannel
client.configureBlocking(false); //设置为非阻塞
client.register(selector, SelectionKey.OP_READ); //标记客户端通道可读
System.out.println("new connection from client: "+ client);
}else if(key.isReadable()){ //当客户端通道可读时
SocketChannel client = (SocketChannel) key.channel(); //获取客户端通道
ByteBuffer buffer = ByteBuffer.allocate(1024);
client.read(buffer);
buffer.flip();
System.out.println("message from client: " + Charset.forName("UTF-8").decode(buffer).toString() );
client.register(selector,SelectionKey.OP_WRITE); //标记客户端为可写
}else if(key.isWritable()){ //当客户端可写时
SocketChannel client = (SocketChannel) key.channel(); //获取客户端通道
ByteBuffer buffer = ByteBuffer.wrap("Hi, My friend!".getBytes("UTF-8"));
client.write(buffer);
}
iter.remove();
}
}
}
}
客户端 TCPClient
public class TCPClient {
public static void main(String[] args) throws IOException {
//创建 SocketChannel,并链接 TCP 端口
SocketChannel sc = SocketChannel.open();
sc.connect(new InetSocketAddress("127.0.0.1", 2333));
ByteBuffer buffer = ByteBuffer.allocate(1024);
//向 SocketChannel 写入缓冲区数据
buffer.put("Hello world!".getBytes("UTF-8")).flip();
sc.write(buffer);
//从 SocketChannel 读取数据到缓冲区
buffer.clear();
sc.read(buffer);
buffer.flip();
System.out.println("from server[" + sc.getRemoteAddress() + "] : "
+ Charset.forName("UTF-8").decode(buffer).toString());
sc.close();
}
}
Pipe 管道
Java NIO Pipe 管道用于在两个线程之间建立单向数据连接,一个 Pipe 包含一个 SinkChannel 接收通道和一个 SourceChannel 源通道,数据从 SinkChannel 写入,从 SourceChannel 读取;
Pipe 主要 API
static Pipeopen() | 打开一个管道 |
Pipe.SinkChannelsink() | 获取该管道的接收器通道; |
Pipe.SourceChannelsource() | 获取该管道的源通道 |
使用示例
//创建管道
Pipe pipe = Pipe.open();
//向管道写入数据
Pipe.SinkChannel sinkChannel = pipe.sink(); //打开 pipe 的接收管道
ByteBuffer buffer = ByteBuffer.wrap("Hello world!".getBytes("UTF-8"));
sinkChannel.write(buffer); //向 pipe 的接收管道写入缓冲区数据
//从管道读取数据
Pipe.SourceChannel sourceChannel = pipe.source(); //打开 pipe 的源管道
buffer = ByteBuffer.allocate(1024);
sourceChannel.read(buffer); //从 pipe 的源管道读取数据到缓冲区
buffer.flip();
System.out.println(Charset.forName("UTF-8").decode(buffer).toString());