首页 > 解决方案 > ServerSocketChannel 在 Linux 上一段时间后停止接受

问题描述

我注意到,当我在 Linux 操作系统上运行我的应用程序时,一段时间后,服务器停止接受客户端。线鲨结果 这是 Wireshark 在停止接受后尝试从我的主机连接到服务器时的屏幕截图。如您所见,第一个请求是 [FIN,ACK] ,我的服务器似乎无法处理。

启动服务器,打开选择器,绑定,阻塞模式设置为 false,服务器通道注册 OP_ACCEPT。(通常的标准东西)这是网络的核心代码:

private void update(int timeout) throws IOException {

    updateThread = Thread.currentThread();

    synchronized (updateLock) {
    }

    long startTime = System.currentTimeMillis();
    int select = 0;

    if (timeout > 0) {
        select = selector.select(timeout);
    }
    else {
        select = selector.selectNow();
    }

    if (select == 0) {
        ++emptySelects;
        if (emptySelects == 100) {

            emptySelects = 0;
            long elapsedTime = System.currentTimeMillis() - startTime;

            try {
                if (elapsedTime < 25)
                    Thread.sleep(25 - elapsedTime);
            } catch (InterruptedException ie) {
                log.error(ie.getMessage());
            }
        }
    } else {

        emptySelects = 0;
        Set<SelectionKey> keys = selector.selectedKeys();

        synchronized (keys) {
            for (Iterator<SelectionKey> iter = keys.iterator(); iter.hasNext();) {

                // keepAlive();
                SelectionKey selectionKey = iter.next();
                iter.remove();
                Connection fromConnection = (Connection) selectionKey.attachment();

                try {
                    if (!selectionKey.isValid())
                        continue;

                    if (fromConnection != null) {
                        if(selectionKey.isReadable()) {
                            try {
                                while (true) {

                                    Packet packet = fromConnection.getTcpConnection().readPacket();
                                    if(packet == null)
                                        break;
                                     fromConnection.notifyReceived(packet);
                                }
                            } catch (IOException ioe) {
                                fromConnection.notifyException(ioe);
                                fromConnection.close();
                            }
                        } else if (selectionKey.isWritable()) {

                            try {
                                fromConnection.getTcpConnection().writeOperation();
                            } catch (IOException ioe) {
                                fromConnection.notifyException(ioe);
                                fromConnection.close();
                            }
                        }
                    }
                    else {
                        if (selectionKey.isAcceptable()) {
                            ServerSocketChannel serverSocketChannel = this.serverChannel;

                            if (serverSocketChannel != null) {
                                try {
                                    SocketChannel socketChannel = serverSocketChannel.accept();
                                    if (socketChannel != null)
                                        acceptOperation(socketChannel);
                                }
                                catch (IOException ioe) {
                                    log.error(ioe.getMessage());
                                }
                            }
                        }
                        else {
                            selectionKey.channel().close();
                        }
                    }
                }
                catch (CancelledKeyException cke) {
                    if(fromConnection != null) {
                        fromConnection.notifyException(cke);
                        fromConnection.close();
                    }
                    else {
                        selectionKey.channel().close();
                    }
                }
            }
        }
    }

    long time = System.currentTimeMillis();
    List<Connection> connections = this.connections;

    for (Connection connection : connections) {
        if (connection.getTcpConnection().isTimedOut(time)) {
            connection.close();
        }
        /* else {

            if (connection.getTcpConnection().needsKeepAlive(time))
                connection.sendTCP(new Packet((char) 0x0FA3));
        }*/
        if (connection.isIdle())
            connection.notifyIdle();
    }
}

当写入操作完成时,选择键相应地更改为 OP_READ 并且在部分写入的情况下 OP_READ | OP_WRITE。

当抛出异常时,从连接对象调用 close() ,它主要执行以下操作:

try {
    if(socketChannel != null) {
        socketChannel.close();
        socketChannel = null;

        if(selectionKey != null)
            selectionKey.selector().wakeup();
    }
} catch (IOException ioe) {
    // empty..
}

acceptOperation - 方法这样做:

private void acceptOperation(SocketChannel socketChannel) {
    Connection connection = newConnection();
    connection.initialize(4096, 4096);
    connection.setServer(this);

    try {
        SelectionKey selectionKey = connection.getTcpConnection().accept(selector, socketChannel);
        selectionKey.attach(connection);

        int id = nextConnectionID++;
        if(nextConnectionID == -1)
            nextConnectionID = 1;

        connection.setId(id);
        connection.setConnected(true);
        connection.addConnectionListener(dispatchListener);

        addConnection(connection);
        connection.notifyConnected();
    } catch (IOException ioe) {
        log.error(ioe.getMessage());
        connection.close();
    }
}

连接对象的 accept-Method 执行此操作:

try {
    this.socketChannel = socketChannel;
    socketChannel.configureBlocking(false);
    Socket socket = socketChannel.socket();
    socket.setTcpNoDelay(true);
    socket.setKeepAlive(true);

    selectionKey = socketChannel.register(selector, SelectionKey.OP_READ);

    lastReadTime = lastWriteTime = System.currentTimeMillis();

    return selectionKey;
} catch (IOException ioe) {
    close();
    throw ioe;
}

请注意,我对客户端网络代码没有影响。
netstat -tulnp服务器突然停止接受后执行此操作时,我可以看到每次连接尝试时 Recv-Q 列都会增加 1。
我希望你能帮助我,因为我不知道为什么会这样。

对不起,可能很长的文字,并感谢转发!

标签: javalinuxsocketsniosocketchannel

解决方案


推荐阅读