2014-02-07 2 views
0

У нас возникают проблемы каждый день с одним из наших клиентов, чьи подключения отключены через определенное время. Исключение на стороне клиента показывает сообщение «Прочитанное время». Поскольку мы не используем тайм-ауты сокетов в нашем Java-приложении (мы используем значения по умолчанию 0), наша первая мысль заключается в том, что таймауты клиента должны поступать от прокси-сервера или маршрутизатора на их стороне. Их ИТ-отдел изучает эту проблему.Закрытие входного потока сокета занимает слишком много времени.

Однако, когда соединение прерывается, наше клиентское приложение закрывает сокет, а затем устанавливает новое соединение с нашим сервером. После приема нового соединения от того же клиента сервер начнет операцию очистки старого соединения, прежде чем он начнет использовать новый. Эта очистка происходит, когда сервер обнаруживает EOS или любое другое исключение для чтения или когда новое соединение принимается одним и тем же клиентом, а старый не был очищен по какой-либо причине (это происходит здесь из-за сбоя нашего сервера для обнаружения EOS). Эта операция очистки включает в себя фактически некоторую регистрацию, очистку различных структур данных маршрутизации и окончательное закрытие входных и выходных потоков. В этот момент мы заметили, что закрытие входного потока старых блоков сокетов в течение 15 минут и времени всегда одно и то же. Как вы можете понять, все идет не так, потому что новое соединение начинает голодать, и одна и та же проблема начинается с начала несколько раз.

Теперь, я полагаю, что наш сокет на стороне сервера страдает неопределенным FIN_WAIT_2 или TIME_WAIT, в котором сокет остается в одном из этих состояний без получения необходимых ACK (возможно, они отбрасываются), которые могут перемещаться это состояние ЗАКРЫТО. Хотя наш сервер не был первым, кто нарушил соединение, я полагаю, что это прокси-сервер или маршрутизатор со стороны клиента, который, возможно, инициировал и сделал его похожим на наш сервер. Я прочитал, что настройка SO_LINGER на стороне сервера на 0 может помочь в такой ситуации (хотя в целом это не рекомендуется). FYI: мы до сих пор не испортили опцию SO_LINGER, но мы думаем сделать это из-за проблемы.

Не могли бы вы объяснить мне, правда ли эта теория? Более того, почему операция закрытия сокета занимает 15 минут? Это намного выше обычной продолжительности жизни 2 * MSL (максимальный срок службы сегмента), которая, как предполагается, является временем, в течение которого сокет ждет закрытия. Должно ли мы установить значение параметра SO_LINGER (метод setSoLinger в Java) на значение, превышающее 0? В любом случае клиент уже прервал соединение на другой стороне и, таким образом, установив опцию linger на 0 непосредственно перед закрытием сокета, не приведет к какому-либо другому исключению или неправильному состоянию на стороне клиента. Кроме того, есть ли у вас какие-либо инструменты, с помощью которых мы могли бы моделировать упавшие пакеты в нашей среде?

Код, создающий и очищающий сокет, не является чем-то особенным. Вот отрывок:

Socket socket = new Socket(ipAddress, port); 
OutputStream dataOutputStream = new BufferedOutputStream(socket.getOutputStream(), 64000); 
InputStream dataInputStream = new BufferedInputStream(socket.getInputStream(), 64000); 

код закрытия потока

try { 
     if (dataInputStream != null) { 
      LOGGER.info("Going to close input stream...."); 
      dataInputStream.close(); 
     } 
    } finally { 
     if (dataOutputStream != null) { 
      LOGGER.info(".closeReaderWriter()", "Going to close output stream..."); 
      dataOutputStream.close(); 
     } 
    } 
+0

Можете ли вы поделиться кодом, в котором вы создаете экземпляр сокета, где вы закрываете сокет, и где вы пытаетесь снова его повторно подключить? – Rainbolt

ответ

0

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

Вывод: вы столкнулись с опцией SO_LINGER.

Решение: не следует.

Вы говорите, что сервер не может обнаружить проблему с клиентом, пока он не восстановится.

Заключение: вы не закрываете сокет в клиентском приложении, когда происходят таймауты чтения.

Решение: закрыть. Затем сервер прочитает EOS, закроет сокет и продолжит свой веселый путь.

Закрытие входного потока разъема не требуется. Просто закройте внешний выходной поток/запись, которую вы обернули вокруг потока вывода сокета, чтобы убедиться, что он покраснел, и, возможно, сам сокет в блоке finally.

Остальная часть этого вопроса вызывает больше вопросов, чем ответов.

  1. Как вы полностью отключили тайм-ауты сокетов в своем приложении Java?
  2. Вы говорите, что ваш сервер «очищает», но вы также говорите, что он «не инициировал закрытие в первую очередь». Почему нет?
+0

Привет EJP. Прежде всего, спасибо за ваш ответ. Возможно, вначале я не был ясен. Пожалуйста, просмотрите мой отредактированный вопрос для ответов на ваши вопросы. – ggkekas

+0

Привет EJP. Наше клиентское приложение закрывается в сокете, но наш сервер терпит неудачу - по какой-то причине - обнаружить EOS. И здесь наши предположения основаны на какой-то странной реакции на прокси клиента. Если прокси-сервер удаляет пакеты FIN или RST, отправленные нам, наш сервер не может обнаружить ситуацию. Я также не считаю, что закрытие - это асинхронная операция. Закрытие должно также ждать некоторых ACK с клиентской стороны. Если эти ACK отбрасываются, то закрытие занимает много времени. Исходный код Java на закрытии содержит комментарии, предупреждающие о возможных длительных операциях. – ggkekas

+0

Вы, кажется, утверждаете, что DataInputStream.close() занимает 15 минут. Это не так. Так что же? Ваш вопрос остается неясным. – EJP

Смежные вопросы