This article is intended to be progressive, but just read the last one
Introduction to Selector
【1】Create Selector
Selector selector = Selector.open();
【2】channel registered to Selector
First of all, the channel must be non-blocking
channel.register(selector, type of operation, bound component); returns the selection key
【3】Poll query ready operation
【4】Method to stop selection
NIO programming steps
Code 1.0
public class SelectorServer {
public static void main(String[] args) throws Exception {
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.bind(new InetSocketAddress(8080));
ssc.configureBlocking(false);
//创建selector选择器
Selector selector = Selector.open();
//将ssc注册到选器器(建立两者的联系)
SelectionKey sscKey = ssc.register(selector, 0, null);
//选择哪种监听的事件
sscKey.interestOps(SelectionKey.OP_ACCEPT);
while (true){
selector.select();//阻塞方法,如果没有事件发生,线程将在此处停止
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();//返回所有可能发生事件的key集合(set)
while (iterator.hasNext()){
SelectionKey key = iterator.next();
System.out.println("key::"+key);
ServerSocketChannel channel = (ServerSocketChannel) key.channel();//获取相对应的channel
SocketChannel sc = channel.accept();
sc.configureBlocking(false);
SelectionKey scKey = sc.register(selector, 0, null);
System.out.println("scKey---->"+scKey);
scKey.interestOps(SelectionKey.OP_READ);
System.out.println("sc已经在selector中注册了!");
}
}
}
}
Result analysis:
solution
[1] Distinguish the event that triggers selector.select()
[2] After processing an event, delete it in the corresponding registered keys set.
Code 2.0
public class SelectorServer {
public static void main(String[] args) throws Exception {
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.bind(new InetSocketAddress(8080));
ssc.configureBlocking(false);
Selector selector = Selector.open();
SelectionKey sscKey = ssc.register(selector, 0, null);
sscKey.interestOps(SelectionKey.OP_ACCEPT);
while (true){
selector.select();//阻塞方法,如果没有事件发生,线程将在此处停止
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();//返回所有可能发生事件的key集合(set)
while (iterator.hasNext()){
SelectionKey key = iterator.next();
iterator.remove();//解决处理后事件在set集合中还有的现象
if (key.isAcceptable()){//区分不同事件触发的结果
ServerSocketChannel channel = (ServerSocketChannel) key.channel();//获取相对应的channel
SocketChannel sc = channel.accept();
sc.configureBlocking(false);
SelectionKey scKey = sc.register(selector, 0, null);
System.out.println("scKey---->"+scKey);
scKey.interestOps(SelectionKey.OP_READ);
System.out.println("sc已经在selector中注册了!");
}else if (key.isReadable()){
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(32);
int len = channel.read(buffer);
buffer.flip();
System.out.println(StandardCharsets.UTF_8.decode(buffer).toString());
buffer.clear();
}
}
}
}
}
New question What will happen if the space allocated by ByteBuffer is not enough (analysis)
When a reading is not completed, the next reading will be triggered.
Solution: Dynamically expand the ByteBuffer space of each channel connected to the client (it can avoid the situation of sticking and half-packing!)
new question:
When the client is forced to shut down, the server stops
When the client ends normally, the server enters an infinite loop
solution:
The client is forced to shut down, the server reports an exception, use the try--catch statement key.cancel() to do nothing
The client ends normally, the client sends data to the server, read = -1;
Code 3.0 (Final)
public class SelectorServer {
public static void main(String[] args) throws Exception {
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.bind(new InetSocketAddress(8080));
ssc.configureBlocking(false);
Selector selector = Selector.open();
SelectionKey sscKey = ssc.register(selector, 0, null);
sscKey.interestOps(SelectionKey.OP_ACCEPT);
while (true){
selector.select();//阻塞方法,如果没有事件发生,线程将在此处停止
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();//返回所有可能发生事件的key集合(set)
while (iterator.hasNext()){
SelectionKey key = iterator.next();
iterator.remove();//解决处理后事件在set集合中还有的现象
if (key.isAcceptable()){//区分不同事件触发的结果
ServerSocketChannel channel = (ServerSocketChannel) key.channel();//获取相对应的channel
SocketChannel sc = channel.accept();
sc.configureBlocking(false);
SelectionKey scKey = sc.register(selector, 0, null);
System.out.println("scKey---->"+scKey);
ByteBuffer buffer = ByteBuffer.allocate(4);
scKey.attach(buffer);//为每一个注册到set集合中的channel分配独立的缓冲区
scKey.interestOps(SelectionKey.OP_READ);
System.out.println("sc已经在selector中注册了!");
}else if (key.isReadable()){
try {
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer buffer = (ByteBuffer) key.attachment();
int read = channel.read(buffer);
// System.out.println("read::"+read);
// System.out.println("positon:"+buffer.position()+"limit:"+buffer.limit());
System.out.println(buffer);
if (read == -1){//客户端正常结束,read的值等于-1
key.cancel();
continue;
}
if(read == buffer.capacity()){
ByteBuffer newBuffer = ByteBuffer.allocate(buffer.capacity()*2);
newBuffer.flip();
newBuffer.put(buffer);
key.attach(newBuffer);
}else{
buffer.flip();
System.out.println(StandardCharsets.UTF_8.decode(buffer).toString());
buffer.clear();
}
} catch (IOException e) {
e.printStackTrace();
key.cancel();
}
}
}
}
}
}
result:
Client ends normally
Client forced to end
Selector write events are similar to read events
server
public class SelectorWriter {
public static void main(String[] args) throws Exception {
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
ssc.bind(new InetSocketAddress(8080));
Selector selector = Selector.open();
ssc.register(selector, SelectionKey.OP_ACCEPT, null);
while (true){
selector.select();
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()){
SelectionKey key = iterator.next();
iterator.remove();
if (key.isAcceptable()){
SocketChannel sc = ssc.accept();
sc.configureBlocking(false);
SelectionKey sckey = sc.register(selector, SelectionKey.OP_READ);
//发送大量的数据
StringBuilder str = new StringBuilder();
for (int i = 0; i < 300000; i++) {
str.append("a");
}
ByteBuffer buffer = Charset.defaultCharset().encode(str.toString());
int write = sc.write(buffer);
System.out.println(write);
if (buffer.hasRemaining()){
// 4. 关注可写事件
sckey.interestOps(sckey.interestOps() + SelectionKey.OP_WRITE);
// sckey.interestOps(sckey.interestOps() | SelectionKey.OP_WRITE);
// 5. 把未写完的数据挂到 sckey 上
sckey.attach(buffer);
}
}else if(key.isWritable()){
SocketChannel sc = (SocketChannel) key.channel();
ByteBuffer buffer = (ByteBuffer) key.attachment();
int write = sc.write(buffer);
System.out.println(write);
//清理工作
if (!buffer.hasRemaining()){
key.attach(null);
key.interestOps(key.interestOps() - SelectionKey.OP_WRITE);
}
}
}
}
}
}
client
public class WriterClient {
public static void main(String[] args) throws Exception {
SocketChannel sc = SocketChannel.open();
sc.connect(new InetSocketAddress("localhost", 8080));
// 3. 接收数据
int count = 0;
while (true) {
ByteBuffer buffer = ByteBuffer.allocate(1024 * 1024);
count += sc.read(buffer);
System.out.println(count);
buffer.clear();
}
}
}