Spec-Zone .ru
спецификации, руководства, описания, API
|
Эта статья затрагивает следующие темы:
Гарантировать аккуратный или корректный выпуск соединений TCP - проблема с TCP сетевые приложения. Прерванный или неизящный выпуск может закончиться, если специальная забота не проявляется. В Java неожиданный прерванный выпуск может проявиться приложением, получающим a java.net.SocketException
читая или при записи в сокет. read()
и write()
обычно возвращайте указание числового значения, соответственно, число байтов, полученных или отправленных. Если исключение получается вместо этого, это указывает, что соединение было прервано и также что данные, возможно, были потеряны или отброшены. Эта статья объясняет, какие сокетные соединения причин быть прерванным и обеспечивает подсказки для того, чтобы они избежали ситуации, за исключением случая, где приложение намеревается прервать соединение.
Во-первых, мы должны отличить различия между прерванным и аккуратным выпуском соединения. Чтобы понять это различие, мы должны смотреть на то, что происходит на уровне протокола TCP. Полезно вообразить установленное соединение TCP как фактически два отдельных, полуавтономных потока данных. Если две коллеги являются A и B, то один поток поставляет данные от до B, и другого потока от B до A. Аккуратный выпуск происходит на двух этапах. Сначала одна сторона (говорят A) решает прекратить отправлять данные и отправляет a FIN
сообщение через к B. Когда стек TCP в стороне Б получает FIN
это знает, что больше данных не прибывает из A, и всякий раз, когда B читает все предыдущие данные от сокета, дальнейшие чтения возвратят значение -1
указать на конец файла. Эта процедура известна как TCP полублизко, потому что только одна половина соединения закрывается. Не удивительно, процедура для другой половины является точно тем же самым. B отправляет a FIN
обменивайтесь сообщениями к A, кто в конечном счете получает a -1
после чтения всех предыдущих данных, отправленных от сокета.
В отличие от этого, прерванное близкое использование RST (Сброс) сообщение. Если или второстепенные вопросы RST, это означает, что все соединение прерывается, и стек TCP может выбросить любые данные с очередями, которые не были отправлены или получены любым приложением.
Так, как приложения Java выполняют аккуратные и прерванные выпуски? Давайте рассматривать прерванные выпуски сначала. Соглашение, которое существовало со дней исходных сокетов BSD, состоит в том, что "задерживаться" опция сокета может использоваться, чтобы вызвать прерванный выпуск соединения. Любое приложение может вызвать Socket.setLinger
(истина, 0), чтобы сказать стек TCP, что, когда этот сокет закрывается, прерванная процедура (RST) должна использоваться. Установка задерживаться опция не имеет никакого непосредственного эффекта, за исключением того, что когда Socket.close()
вызывается впоследствии, соединение прерывается с сообщением RST. Как мы будем видеть коротко, есть другие пути, которые могут заставить соединение быть прерванным, но это - самый простой способ заставить его произойти.
close()
метод также используется, чтобы выполнить аккуратный выпуск, так же как прерванный. Так, в его самом простом различие между аккуратным выпуском и прерванным выпуском не могло всего устанавливать задержание (0) опция, описанная выше, до вызова Socket.close()
. Возьмите пример двух соединенных коллег A и B: Если вызовы Socket.close()
, a FIN
сообщение отправляется от до B; и когда B вызывает Socket.close()
, a FIN
отправляется от B до A. Фактически, настройка по умолчанию для задерживаться опции не должна использовать прерванное завершение; так, если два приложения завершают свое соединение только при использовании Socket.close()
, тогда результатом должен быть аккуратный выпуск. Так, какова, тогда, проблема?
Проблемой является небольшое несоответствие между семантикой Socket.close()
и TCP FIN
сообщение. Отправка TCP FIN
сообщение означает, что "Я заканчиваюсь, передаваясь", тогда как Socket.close()
означает, что "Я заканчиваюсь, передаваясь и получая." Когда Вы вызываете Socket.close()
, ясно больше не возможно отправить данные; ни, однако, это возможный получить данные. Так, что происходит, например, когда попытки аккуратный выпуск, закрывая сокет, но B продолжает отправлять данные? Это совершенно допустимо в спецификации TCP, потому что, насколько TCP обеспокоен, что только одна половина соединения была закрыта. Но так как сокет А закрывается нет никого, чтобы считать данные, если B должен продолжать передаваться. В этой ситуации стек TCP А должен отправить RST, чтобы насильственно завершить соединение.
Другой общий сценарий, который может привести к непреднамеренному SocketException
, следующее: Скажите, что A отправил данные B, но B закрывает сокет, не читая все данные. В этой ситуации стек TCP Б знает, что некоторые данные эффективно теряются, и это будет насильственно завершаться с RST, а не использовать санитара FIN
процедура. Желание получает a SocketException
если это тогда пытается отправить или получить данные от сокета.
Есть много простых способов избежать удивляться этой проблемой.
shutdownOutput()
. Этот метод имеет тот же самый эффект как close()
в этом a FIN
отправляется, чтобы указать, что эта коллега закончила передаваться, но все еще возможно читать из сокета до тех пор, пока удаленная коллега отправляет a FIN
и конец файла читается из потока. Затем с сокетом можно согласиться Socket.close()
.