Selector的使用方法
Selector的创建
//1.获取Selector选择器
Selector selector = Selector.open();
/** * (1)与 Selector 一起使用时,Channel 必须处于非阻塞模式下,否则将抛出异常 * IllegalBlockingModeException。这意味着,FileChannel 不能与 Selector 一起使用,因 * 为 FileChannel 不能切换到非阻塞模式,而套接字相关的所有的通道都可以。 * * (2)一个通道,并没有一定要支持所有的四种操作。比如服务器通道 * ServerSocketChannel 支持 Accept 接受操作,而 SocketChannel 客户端通道则不支持。 * 可以通过通道上的 validOps()方法,来获取特定通道下所有支持的操作集合。 * */
//Selector的使用方法
public void CreateSelectorTest() throws IOException {
//1.获取Selector选择器
Selector selector = Selector.open();
//注册Channels到Selectors
//2.获取通道
ServerSocketChannel s1 = ServerSocketChannel.open();
//3.设置为非阻塞
s1.configureBlocking(false);
//4.绑定连接
s1.bind(new InetSocketAddress(9999));
//5.将通道注册到选择器上,并指定监听时间:"接受"事件
s1.register(selector, SelectionKey.OP_ACCEPT);
//查询已经就绪通道操作
Set<SelectionKey> selectKeys = selector.selectedKeys();
//遍历集合
Iterator<SelectionKey> iterator = selectKeys.iterator();
while (iterator.hasNext()){
SelectionKey selectionKey = iterator.next();
//判断Key的就绪状态操作
if (selectionKey.isAcceptable()){
} else if (selectionKey.isConnectable()) {
} else if (selectionKey.isReadable()) {
} else if (selectionKey.isWritable()) {
}
iterator.remove();
}
}
接下来是一个例子:关于如何实现selector选择器 客户端发送数据,服务端接受数据
简单的思路
- 首先服务端代码中注册一个SocketChanel(TCP)通道,向通道里面传送数据并且绑定一个url和端口号port,注意一定要开启非阻塞模式configureBlocking(false)
- 在服务器端注册一个通道监听器ServerSocketChannel,并且开启通道监听,然后获取选择器,并将ServerSocketChannel注册到监听器,注意注册到选择器Selector上面的是ServerSocketChannel。同时注册的时候指定感兴趣的事件
- 此时有个思维误区,我一直在想监听器进行轮询查看的时候我客户端都没有设置他的感兴趣事件,如何接受我的客户端,其实选择器轮询的时候,一开始查看的是监听器的ServerSocketChannel注册到Selector上面的感兴趣事件,然后在从监听器中取出接受客户端channel,这个时候再把客户端的channel注册到选择器Selector
- 服务端进行选择器轮询,在轮询第一次获取的就绪操作ServerSocketChannel的感兴趣事件,然后在ServerSocketChannel取出监听端口发送的的SocketChanel,注册到选择器中,指定感兴趣事件,并且下一次选择器轮询的时候处理其中的Buffer中的数据。
package Selector;
import org.junit.jupiter.api.Test;
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.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Iterator;
import java.util.Set;
public class SelectorDemo2 {
//客户端代码
@Test
public void clientDemo() throws IOException {
//1 获取通道,绑定主机和端口号
SocketChannel channel = SocketChannel.open(new InetSocketAddress("127.0.0.1",8080));
//2 切换到非阻塞模式
channel.configureBlocking(false);
//3 创建Buffer
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
//4 写入buffer数据
byteBuffer.put(new Date().toString().getBytes());
//5 模式切换
byteBuffer.flip();
//6 写入通道
channel.write(byteBuffer);
//7关闭
byteBuffer.clear();
}
//服务端代码
//服务器端监听客户端的请求,同时在选择器中注册一个通道,状态为接受,选择器便利查询到有接受的状态
//serverSocketChannel同时将监听的客户端请求注册为Read状态,然后在下一次循环中处理这个,客户端请求。
@Test
public void serverDemo() throws IOException {
//1.获取服务端通道,开启监听通道
ServerSocketChannel ssc = ServerSocketChannel.open();
//2.切换非阻塞模式
ssc.configureBlocking(false);
//3.创建Buffer
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
//4.绑定端口
//开启通道监听
ssc.bind(new InetSocketAddress("127.0.0.1",8080));
//5.获取Selector选择器
//注册选择器
Selector selector = Selector.open();
//6通道注册到选择器,经行监听
//注册Channel,并且指定感兴趣的事件是Accept
ssc.register(selector, SelectionKey.OP_ACCEPT);
//7.选择器进行轮询,进行后续操作
//select方法:阻塞到至少有一个通道就绪
while (selector.select()>0){
//获取所有就绪通道的就绪操作
Set<SelectionKey> selectionKeys = selector.selectedKeys();
//遍历
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()){
//获取就绪操作
SelectionKey next = iterator.next();
//判断什么操作
if (next.isAcceptable()){
//获取连接
SocketChannel accept = ssc.accept();
//切换非阻塞模式
accept.configureBlocking(false);
//注册
accept.register(selector,SelectionKey.OP_READ);
}else if (next.isReadable()){
SocketChannel socketChannel = (SocketChannel) next.channel();
ByteBuffer byteBuffer1 = ByteBuffer.allocate(1024);
byteBuffer1.clear();
socketChannel.read(byteBuffer1);
byteBuffer1.flip();
System.out.println(new String(StandardCharsets.UTF_8.decode(byteBuffer1).array()));
}
iterator.remove();
}
}
}
public static void main(String[] args) throws IOException, InterruptedException {
//1 获取通道,绑定主机和端口号
SocketChannel channel = SocketChannel.open(new InetSocketAddress("127.0.0.1",8080));
//2 切换到非阻塞模式
channel.configureBlocking(false);
while (true){
Thread.sleep(10000);
//3 创建Buffer
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
//4 写入buffer数据
byteBuffer.put(new Date().toString().getBytes());
//5 模式切换
byteBuffer.flip();
//6 写入通道
channel.write(byteBuffer);
//7关闭
byteBuffer.clear();
}
}
}