Я заметил, что когда я запускаю свое приложение в ОС Linux, через некоторое время сервер просто перестает принимать клиентов. результат wirehark Это снимок экрана wirehark при попытке подключиться к серверу с моего хоста после того, как он перестал принимать. Как видите, первый запрос - [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.
Когда выбрасывается Exception, 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 при каждой попытке подключения. Надеюсь, вы поможете мне, так как я понятия не имею, почему это происходит.
Извините за возможно длинный текст и спасибо в пересылке!