selector模型
使用一个线程去监控多个IO请求,如果哪一个IO数据准备完毕后就通知相应的线程来处理
select模型,它的基本原理是采用轮询和遍历的方式。也就是说,在客户端操作服务器时,会创建三种文件描述符,简称FD。分别是writefds(写描述符)、readfds(读描述符)和 exceptfds(异常描述符)
demo,让主线程监听IO事件然后进行处理
server
public class NioServerExample {
public static void main(String[] args) throws IOException, InterruptedException {
Selector selector = getSelector();
listen(selector);
}
public static Selector getSelector() throws IOException {
Selector selector = Selector.open();
//创建可选通道,设置非阻塞
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
//绑定通道到指定端口
ServerSocket socket = serverSocketChannel.socket();
socket.bind(new InetSocketAddress(8080));
//向selector注册IO事件,首先注册SelectionKey.OP_ACCEPT让server accept监听
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
return selector;
}
public static void listen(Selector selector) throws IOException, InterruptedException {
while (selector.select() > 0) {
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
if (iterator.hasNext()) {
SelectionKey key = iterator.next();
//判断IO事件类型进行处理
process(selector, key);
iterator.remove();
}
}
}
private static void process(Selector selector, SelectionKey key) throws IOException, InterruptedException {
if (key.isAcceptable()) {
System.out.println(Thread.currentThread().getName()+" =>事件类型 accept");
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel channel = server.accept();
channel.configureBlocking(false);
channel.register(selector, SelectionKey.OP_READ);
} else if (key.isReadable()) {
System.out.println(Thread.currentThread().getName()+" =>事件类型 read");
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer byteBuffer = ByteBuffer.allocate(500);
int len = channel.read(byteBuffer);
if (len > 0) {
String content = new String(byteBuffer.array(), 0, len);
System.out.println(content);
channel.register(selector, SelectionKey.OP_WRITE);
}
byteBuffer.clear();
} else if (key.isWritable()) {
System.out.println(Thread.currentThread().getName()+" =>事件类型 write");
SocketChannel channel = (SocketChannel) key.channel();
String str = "client fuck you I am Nio Server";
channel.write(ByteBuffer.wrap(str.getBytes()));
channel.register(selector, SelectionKey.OP_READ);
// channel.close(); //向客户端发送数据后管别此通道连接
}
}
}
client
public class NioClientExample0 {
public static void main(String[] args) throws IOException, InterruptedException {
Selector selector = Selector.open();
SocketChannel socketChannel = SocketChannel.open();
socketChannel.setOption(StandardSocketOptions.TCP_NODELAY, true);
socketChannel.connect(new InetSocketAddress("127.0.0.1", 8080));
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_WRITE);
String info = null;
Scanner scanner = new Scanner(System.in);
ByteBuffer byteBuffer0 = ByteBuffer.allocate(100);
ByteBuffer byteBuffer1 = ByteBuffer.allocate(100);
while (true) {
int len = socketChannel.read(byteBuffer1);
System.out.println(new String(byteBuffer1.array(), 0, len));
byteBuffer1.clear();
info = scanner.next();
byteBuffer0.put(info.getBytes());
byteBuffer0.flip();
socketChannel.write(byteBuffer0);
byteBuffer0.clear();
}
}
}
参考
举例如下,开启服务端和一个客户端后,用客户端向服务端发送一条数据
当服务端接收到数据并回写给客户端后,又被注册上读事件,准备接收数据
这时再开启一个客户端,会发现服务端没有一直等待读事件的IO,而是转身处理accept接收新的连接去了
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。