什么是选择器Selector
选择器是NIO技术的核心组件(缓冲区Buffer、通道Channel、选择器Selector)之一。选择器的主要作用是使用1个线程来对多个通道中的已经就绪通道进行选择,选择出就绪的通道后,就可以对通道中的数据进行处理。选择器核心类包括Selector、SelectionKey、SelectableChannel。
通道的标记SelectionKey
每个注册到选择器中的通道,都有一个代表通道的SelectionKey对象。
可选择通道SelectableChannel
- ServerSocketChannel
- SocketChannel
- DatagramChannel
- SctpChannel
- SctpMultiChannel
- SctpServerChannel
- SinkChannel
- SourceChannel
通道的操作类型
- OP_ACCEPT
- OP_CONNECT
- OP_READ
- OP_WRITE
ServerSocketChannel和SocketChannel使用
Server端:
try {
ServerSocketChannel channel = ServerSocketChannel.open();
//设置为非阻塞模式
channel.configureBlocking(false);
channel.bind(new InetSocketAddress("localhost",8088));
//打开选择器
Selector selector = Selector.open();
//注册连接通道
channel.register(selector, SelectionKey.OP_ACCEPT);
while (true){
//选择已就绪的key
selector.select();
//获取已就绪的key的集合
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()){
SelectionKey readyKey = iterator.next();
iterator.remove();
//判断当前已就绪key是否是连接通道的key
if(readyKey.isAcceptable()) {
//接收客户端的连接
SocketChannel accept = channel.accept();
accept.configureBlocking(false);
//注册读取客户端数据的通道
accept.register(selector, SelectionKey.OP_WRITE);
}
//判断当前key是否是可写入的通道
if(readyKey.isWritable()){
SocketChannel socketChannel = (SocketChannel) readyKey.channel();
File file = new File("D:\\test001.txt");
FileInputStream fileInputStream = new FileInputStream(file);
FileChannel fileChannel = fileInputStream.getChannel();
// 设置帧头写入缓冲区,回写消息给客户端
boolean isFrame=true;
while (fileChannel.position()<fileChannel.size()){
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
// 读取要发送的文件的内容,放入缓冲区
if(isFrame){
// 设置是否包含frame
byteBuffer.putInt(0);
// 写入文件名长度到帧头缓冲区 int为4字节
byteBuffer.putInt(file.getName().length());
// 写入文件名到帧头缓冲区
byteBuffer.put(file.getName().getBytes());
// 写入文件文件大小到帧头缓冲区
byteBuffer.putLong(fileChannel.size());
isFrame=false;
}else {
byteBuffer.putInt(1);
}
fileChannel.read(byteBuffer);
// 将文件内容写入缓冲区
byteBuffer.flip();
socketChannel.write(byteBuffer);
byteBuffer.clear();
}
socketChannel.close();
fileChannel.close();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
Client端
try {
SocketChannel socketChannel_1 = SocketChannel.open();
socketChannel_1.configureBlocking(false);
socketChannel_1.connect(new InetSocketAddress("localhost", 8088));
Selector open = Selector.open();
socketChannel_1.register(open, SelectionKey.OP_CONNECT);
while (true) {
open.select();
Set<SelectionKey> selectionKeys = open.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()) {
SelectionKey next = iterator.next();
iterator.remove();
if (next.isConnectable()) {
while (!socketChannel_1.finishConnect()) {
}
socketChannel_1.register(open, SelectionKey.OP_READ);
}
if (next.isReadable()) {
ByteBuffer allocate = ByteBuffer.allocate(1024);
int read = socketChannel_1.read(allocate);
while (read !=-1) {
read = socketChannel_1.read(allocate);
}
allocate.flip();
if(allocate.remaining() > 0 ) {
int isFrame = allocate.getInt();
//读取frame
if (isFrame == 0) {
int anInt = allocate.getInt();
System.out.println("filename length = " + anInt);
byte[] bytes = new byte[anInt];
allocate.get(bytes);
System.out.println("filename : " + new String(bytes, "utf8"));
long length = allocate.getLong();
System.out.println("content length = " + length);
}
//读取数据,写入本地文件
File file = new File("E:\\test001.txt");
if(!file.exists()){
file.createNewFile();
}
FileOutputStream fileInputStream = new FileOutputStream(file);
FileChannel fileChannel = fileInputStream.getChannel();
fileChannel.write(allocate);
}
allocate.clear();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}