博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
NIO 服务器端不阻塞的一个Bug解决
阅读量:3905 次
发布时间:2019-05-23

本文共 3569 字,大约阅读时间需要 11 分钟。

NIO 服务器端不阻塞的一个Bug解决

@author:Jingdai

@date:2021.06.27

今天用 NIO 时出现了一个Bug,搞了半天,现在记录一下。

使用 NIO 进行通信时,在客户端正常关闭时(即调用close() 方法关闭),会触发服务器的一个读事件,此时服务器的 read 方法会返回 -1,根据此我们就可以做一些处理,如下代码。

int n = socketChannel.read(byteBuffer);if (n == -1) {
selectionKey.cancel();}

但是今天写代码发现客户端正常关闭后服务器端总是读不到 -1,无法取消这个 key,导致服务器端无法阻塞,一直空转。示例代码如下。

Server.java

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.Charset;import java.util.Iterator;public class Server {
public static void main(String[] args) {
int port = 8888; try {
ServerSocketChannel ssc = ServerSocketChannel.open(); ssc.configureBlocking(false); ssc.bind(new InetSocketAddress(port)); Selector selector = Selector.open(); ssc.register(selector, SelectionKey.OP_ACCEPT); while (true) {
selector.select(); Iterator
iterator = selector.selectedKeys().iterator(); while (iterator.hasNext()) {
SelectionKey selectionKey = iterator.next(); if (selectionKey.isAcceptable()) {
SocketChannel sc = ssc.accept(); sc.configureBlocking(false); sc.register(selector, SelectionKey.OP_READ); } else if (selectionKey.isReadable()) {
SocketChannel sc = (SocketChannel) selectionKey.channel(); ByteBuffer byteBuffer; if (selectionKey.attachment() == null) {
byteBuffer = ByteBuffer.allocate(512); selectionKey.attach(byteBuffer); } else {
byteBuffer = (ByteBuffer) selectionKey.attachment(); } int n = sc.read(byteBuffer); System.out.println(n); if (n == -1) {
selectionKey.cancel(); } else {
byteBuffer.flip(); System.out.println(Charset.defaultCharset().decode(byteBuffer)); // byteBuffer.clear(); } } iterator.remove(); } } } catch (IOException e) {
e.printStackTrace(); } }}

Client.java

import java.io.IOException;import java.io.OutputStream;import java.net.Socket;public class Client {
public static void main(String[] args) {
String ip = "localhost"; int port = 8888; try (Socket socket = new Socket(ip, port)) {
OutputStream os = socket.getOutputStream(); os.write("Hello Server!".getBytes()); os.flush(); } catch (IOException e) {
e.printStackTrace(); } }}

最后发现在客户端正常关闭时,如果 read(ByteBuffer dst) 方法的 dst 里面没有数据,则 read() 方法就会正常的返回 -1;而如果 dst 里面已经有了数据的话,那么 read() 方法就会返回 0 。所以上面代码中就无法进入到 n == -1 的那个分支,即无法取消这个 key,这就导致 select 无法阻塞,一直空转。知道了原因,也就很好解决了,只要将上面代码中那行注释的代码去掉注释就行,读取完之后 clear 会重置ByteBuffer ,就可以使 read 方法返回 -1了,当然每次读取都使用一个新的 ByteBuffer 也是可以的。

结论

当客户端正常关闭时,会触发服务器的读事件。如果服务器端的 read(ByteBuffer dst) 方法的 dst 里面没有数据,则 read() 方法就会正常的返回 -1;而如果 dst 里面已经有了数据的话,那么 read() 方法就会返回 0 。

转载地址:http://gomen.baihongyu.com/

你可能感兴趣的文章
自媒体的风格
查看>>
宅米网性能优化实践
查看>>
Python sort and sorted
查看>>
Python List Operation
查看>>
python auto-increment
查看>>
Python List Comprehensions
查看>>
Python 递归 list不正确
查看>>
Python copy a list
查看>>
Iteration Vs Recursion Java
查看>>
What are some of the differences between using recursion to solve a problem versus using iteration?
查看>>
subsets
查看>>
Python Nested List Operation
查看>>
Python Binary Search
查看>>
How to append list to second list
查看>>
Write a program to print all permutations of a given string
查看>>
递归回溯
查看>>
穷举递归和回溯算法终结篇
查看>>
Exhaustive recursion and backtracking
查看>>
递归算法的时间复杂度终结篇
查看>>
全排列算法的递归与非递归实现
查看>>