Spec-Zone .ru
спецификации, руководства, описания, API
Содержание документации

Аккуратный Против Прерванного Выпуска Соединения в Java

Эта статья затрагивает следующие темы:

Краткий обзор

Гарантировать аккуратный или корректный выпуск соединений 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 если это тогда пытается отправить или получить данные от сокета.

Как избежать проблемы

Есть много простых способов избежать удивляться этой проблемой.

  1. Данные структуры с высокоуровневым протоколом. Протоколы, такие как HTTP, которые создаются сверху TCP, соглашения с проблемой, проясняя обеим сторонам, когда безопасно закрыть сокет.
  2. Всегда используйте данные от сокета прежде, чем закрыть это. Это поможет избежать второго сценария, описанного выше.
  3. Использовать shutdownOutput(). Этот метод имеет тот же самый эффект как close() в этом a FIN отправляется, чтобы указать, что эта коллега закончила передаваться, но все еще возможно читать из сокета до тех пор, пока удаленная коллега отправляет a FIN и конец файла читается из потока. Затем с сокетом можно согласиться Socket.close().

Oracle и/или его филиалы Авторское право © 1993, 2012, Oracle и/или его филиалы. Все права защищены.
Свяжитесь с Нами