Ваш отчет об ошибке закрыт как «не ошибка» с объяснением.Вы игнорируете результат connect()
, который при значении true означает, что OP_CONNECT
никогда не сработает, потому что канал уже подключен.Вам понадобится только целая OP_CONNECT/finishConnect()
мегилла, если она вернет false.Поэтому вам даже не следует регистрировать OP_CONNECT
, если connect()
не возвращает false, не говоря уже о том, чтобы зарегистрировать его, прежде чем вы даже позвоните connect().
Дополнительные замечания:
Под капотом OP_CONNECTи OP_WRITE - это одно и то же, что объясняет его часть.
Поскольку для этого у вас есть один поток, обходным решением будет выполнить подключение в режиме блокировки, а затем переключиться на неблокирование для I /O.
Вы выполняете select () после регистрации канала с помощью селектора?
Правильный способ обработки неблокирующего соединения заключается в следующем:
channel.configureBlocking(false);
if (!channel.connect(...))
{
channel.register(sel, SelectionKey.OP_CONNECT, ...); // ... is the attachment, or absent
}
// else channel is connected, maybe register for OP_READ ...
// select() loop runs ...
// Process the ready keys ...
if (key.isConnectable())
{
if (channel.finishConnect())
{
key.interestOps(0); // or SelectionKey.OP_READ or OP_WRITE, whatever is appropriate
}
}
Несколько неисчерпывающих комментариев после просмотра вашего расширенного кода:
Закрытие канала отменяет ключ.Вам не нужно выполнять оба этих действия.
Нестатический метод removeInterest () реализован неправильно.
TYPE_DEREGISTER_OBJECT также закрывает канал,Не уверен, что это то, что вы действительно хотели.Я бы подумал, что он должен просто отменить ключ, и должна быть отдельная операция для закрытия канала.
Вы перешли слишком далеко на маленьких методах и обработке исключений.addInterest () и removeInterest () являются хорошими примерами.Они перехватывают исключения, регистрируют их, затем поступают так, как если бы исключение не произошло, когда все, что они на самом деле делают, это устанавливает или сбрасывает немного: одна строка кода.Кроме того, многие из них имеют как статические, так и нестатические версии.То же самое относится ко всем маленьким методам, которые вызывают key.cancel (), channel.close () и т. Д. Нет никакого смысла во всем этом, это просто объединение строк кода.Это только добавляет неясности и усложняет понимание вашего кода.Просто выполните требуемую операцию в строке и поместите один перехватчик внизу цикла выбора.
Если finishConnect () возвращает false, это не ошибка соединения, простозавершено еще.Если выдается исключение , , что означает сбой соединения.
Вы регистрируетесь для OP_CONNECT и OP_READ одновременно.Это не имеет смысла, и это может вызвать проблемы.Нечего читать, пока не сработает OP_CONNECT.Сначала зарегистрируйтесь для OP_CONNECT.
Вы выделяете ByteBuffer для чтения.Это очень расточительно.Используйте тот же самый для жизни соединения.
Вы игнорируете результат read ().Это может быть ноль.Это может быть -1, указывая EOS, на котором вы должны закрыть канал.Вы также предполагаете, что получите единственное сообщение приложения за одно чтение.Вы не можете предполагать это.Это еще одна причина, почему вы должны использовать один ByteBuffer для жизни соединения.
Вы игнорируете результат write ().Может быть меньше, чем buffer.remaining (), когда вы его вызывали.Это может быть ноль.
Вы можете значительно упростить это, сделав NetSelectable ключевым вложением.Тогда вы можете покончить с несколькими вещами, включая, например, карту каналов и утверждение, потому что канал ключа всегда должен быть равен каналу вложения ключа.
Я бы также определенно переместил код finishConnect () в NetSelector, а connectEvent () был бы просто уведомлением об успехе / неудаче.Вы не хотите распространять подобные вещи.Сделайте то же самое с readEvent (), т. Е. Выполните само чтение в NetSelector с буфером, предоставленным NetSelectable, и просто уведомите NetSelectable о результате чтения: count или -1 илиисключение. То же самое при записи: если канал доступен для записи, получите что-то для записи из NetSelectable, запишите это в NetSelector и сообщите результат. Вы можете сделать так, чтобы обратные вызовы уведомлений возвращали что-то, чтобы указать, что делать дальше, например, закрыть канал.
Но на самом деле все это в пять раз сложнее, чем нужно, и тот факт, что у вас есть эта ошибка, доказывает это. Упростите свою голову.