java - 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。
我希望你能帮助我,因为我不知道为什么会这样。
对不起,可能很长的文字,并感谢转发!
解决方案
推荐阅读
- php - 无法使用 codeigniter 验证码助手创建验证码
- android-studio - 简单的调试断点条件给了我“未使用的等于表达式”
- python-3.x - 有没有一种有效的方法来过滤列表中的最大数字
- python - python删除大多数列是nans的行
- angular - 在 Angular 模板中递增和连接整数列表
- gitlab - 恢复备份后 GitLab 错误获取变量
- java - 在 Eclipse 插件的 ViewPart 中更改 Swing JPanel 的大小(尽管 SwingUtilities.invokeLater(Runnable()))
- java - 有没有办法在 Java 图形形状上投射光线?
- java - Netbeans 8.2 安装本机库问题
- android - 带有许多按钮的自定义对话框颤振