NIO问题记录-客户端断开连接,服务端循环读取到OP_READ事件

NIO问题记录-客户端断开连接,服务端循环读取到read事件

问题: client连接上server后日志正常,我想要再次发送请求,所以重启了client。重启后 server无限监听到read事件,buffer读取长度为-1
日志截图:

在这里插入图片描述

客户端代码

package org.example.io.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;

/**
 * @Des NIO服务端练习 buffer、channel、selector
 */
public class NIOServer {
    public static void main(String[] args) throws IOException {
        //创建serverSocketChannel
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        //创建一个selector
        Selector selector = Selector.open();
        //绑定一个端口 6666,在服务器端监听
        serverSocketChannel.socket().bind(new InetSocketAddress(6666));
        //设置为非阻塞
        serverSocketChannel.configureBlocking(false);

        //serverSocketChannel 注册到selector
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        //循环等待客户端连接
        while (true) {
            //判断是否有事件发生
            if (selector.select(1000) == 0) {
                System.out.println("服务器等待了1秒,无连接。");
                continue;
            }
            //如果有accept事件发生  注册到selector 类型是 读数据
            //获取selectKey
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> it = selectionKeys.iterator();
            while (it.hasNext()) {
                SelectionKey key = it.next();
                //判断事件类型 如果是accept
                if (key.isAcceptable()) {
                    //该客户端生成 SocketChannel
                    SocketChannel socketChannel = serverSocketChannel.accept();
                    //将 SocketChannel 设置为非阻塞
                    socketChannel.configureBlocking(false);
                    //注册到selector 关注事件是 OP_READ 给SocketChannel关联一个Buffer
                    socketChannel.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024));
                } else if (key.isReadable()) {
                    //如果是 read 类型 通过key获取channel
                    SocketChannel socketChannel = (SocketChannel) key.channel();
                    //获取该channel关联的buffer
                    ByteBuffer byteBuffer = (ByteBuffer) key.attachment();
                    //从channel里读数据
                    int len = socketChannel.read(byteBuffer);
                    System.out.println("from 客户端 读取数据长度:" + len +
                            " 数据内容:" + new String(byteBuffer.array()));
                }
                //手动从集合中移除 SelectionKey,防止重复操作
                it.remove();
            }
        }
    }
}


通过测试发现,只要中断client就会发生这种情况

经过查找原因了解到,当客户端主动连接断开时,为了让服务器知道断开了连接,会产生OP_READ事件。所以需要判断读取长度,当读到-1时,关闭channel。
改正后的代码如下

...
while (it.hasNext()) {
                SelectionKey key = it.next();
                //判断事件类型 如果是accept
                if (key.isAcceptable()) {
                    //该客户端生成 SocketChannel
                    SocketChannel socketChannel = serverSocketChannel.accept();
                    //将 SocketChannel 设置为非阻塞
                    socketChannel.configureBlocking(false);
                    //注册到selector 关注事件是 OP_READ 给SocketChannel关联一个Buffer
                    socketChannel.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024));
                } else if (key.isReadable()) {
                    //如果是 read 类型 通过key获取channel
                    SocketChannel socketChannel = (SocketChannel) key.channel();
                    //获取该channel关联的buffer
                    ByteBuffer byteBuffer = (ByteBuffer) key.attachment();
                    //从channel里读数据
                    int len = socketChannel.read(byteBuffer);
                    // !!!注意这里
                    if(len == -1){
                        //说明连接断开
                        socketChannel.close();
                        it.remove();
                        continue;
                    }
                    System.out.println("from 客户端 读取数据长度:" + len +
                            " 数据内容:" + new String(byteBuffer.array()));
                }
                //手动从集合中移除 SelectionKey,防止重复操作
                it.remove();
            }
            ···
  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值