Как заставить серверный сокет повторно принять запрос от клиента? - PullRequest
0 голосов
/ 17 марта 2010

Для тех, кто не хочет читать длинный вопрос, вот короткая версия:

На сервере есть открытый сокет для клиента.Сервер получает запрос на открытие сокета с того же IP-адреса клиента и клиентского порта.Я хочу, чтобы сервер не отклонял такой запрос, а закрывал старый сокет и открывал новый.Как я могу сделать это?


А вот длинный (оригинальный) вопрос:

У меня следующая ситуация.Существует установленная связь между сервером и клиентом.Затем внешнее программное обеспечение (Bonjour) сообщает моему клиенту, что оно не видит сервер в локальной сети.Что ж, клиент ничего не делает с этим по следующим причинам:

  1. Если Bonjour не видит сервер, это не обязательно означает, что клиент не может видеть сервер.

  2. Даже если клиент доверяет Bonjour и закрывает сокет, это не улучшает ситуацию («не иметь открытого сокета» хуже, чем «иметь потенциально плохой сокет»).

Итак, клиент ничего не делает, если сервер становится невидимым для Bonjour.Но затем сервер снова появляется в Bonjour, и Bonjour уведомляет об этом клиента.В этой ситуации возможны следующие ситуации:

  1. Сервер вновь появляется с новым IP-адресом.Таким образом, клиент должен открыть новый сокет, чтобы иметь возможность связаться с сервером.

  2. Сервер вновь появляется на старом IP-адресе.В этом случае у нас есть два случая:

2.1.Сервер был перезапущен (выключен, а затем включен).Таким образом, он не запоминает старый сокет (который все еще используется клиентом).Таким образом, клиент должен закрыть старый сокет и открыть новый (на тот же IP-адрес сервера и тот же порт сервера).

2.2.У нас была временная проблема с сетью, и сервер работал все время.Итак, старый сокет все еще доступен для использования.В этом случае клиенту на самом деле не нужно закрывать старый сокет и заново открывать новый.

Но чтобы упростить свою жизнь, я в любом случае решил закрыть и снова открыть сокет на стороне клиента (несмотря натот факт, что это не очень нужно в последней описанной ситуации).

Но у меня могут быть проблемы с этим решением.Если я закрою сокет на стороне клиента и попытаюсь снова открыть сокет с того же IP-адреса клиента и клиентского порта, сервер не примет вызов для нового сокета.Сервер будет думать, что такой сокет уже существует.

Можно ли написать сервер таким образом, чтобы он не отказывался от таких вызовов.Например, если он (сервер) видит, что клиент отправляет запрос на сокет с того же IP-адреса клиента и клиентского порта, он (сервер) закрывает доступный сокет, связанный с этим клиентским IP-адресом и клиентским портом, ичем он открывает новую розетку.

Ответы [ 3 ]

0 голосов
/ 17 марта 2010

Основано на комментариях:

Вы не можете написать сервер таким образом, что он закроет сокет, который он по-прежнему считает подключенным, и автоматически примет новое соединение, так как код приложения не имеет такого контроля над стеком TCP, а также не существует способа восстановить соединение.

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

Но все же, если это произойдет, сервер заметит, что вы пытаетесь настроить уже подключенный сокет, и откажется от нового подключения. В этом случае ваш клиент мало что может сделать, кроме как закрыть сокет, создать новый и попытаться подключиться снова - и будет выбран другой случайный порт.

дополнительное примечание, ваш сервер должен предпринять некоторую форму действий для обнаружения и закрытия мертвых сокетов, если все, что ваш сервер делает, это читает входящие данные, то "мертвые" сокеты никогда не будут закрыты, так как они никогда не будут обнаружены как мертвые (включение tcp keepalive - это дешевая мера против мертвых сокетов, оставшихся на несколько месяцев, хотя по умолчанию потребуется несколько часов, чтобы обнаружить их как таковые).

0 голосов
/ 19 января 2016

Ситуация, которую вы описываете, невозможна. Вы не можете получить новый запрос на соединение с того же удаленного IP: порта, что и существующее соединение. Клиент не допустит этого.

0 голосов
/ 17 марта 2010

Вы не можете "открыть" сокет на вашем сервере. Если сокет уже существует и клиент пытается восстановить соединение, вы должны получить исключение BindException (см. Предыдущий вопрос). Возможный сценарий:

  • Клиент выключает сокет
  • Серверная ОС "замечает" сокет мертв на стороне клиента и закрывает свою сторону вниз
  • Клиент переподключается к тому же порту, но с «новым» сокетом

В этом случае вы можете считать это «тем же» сокетом, но на самом деле это не так. При этом стратегия, которую вы, возможно, пожелаете принять, заключается в том, чтобы иметь какую-то карту (хэш IP-адреса / порта клиента) с любым механизмом, который вы используете для обслуживания сокета, или с какими-либо постоянными данными состояния, чтобы он мог имитировать продолжение предыдущий сокет (в том же духе, что и сеанс http). Что-то вроде:

HashMap<Client, State> sessions = ...;

public void server(){
  ...
  while(true){
    Socket socket = server.accept();
    Client client = new Client(socket);
    State s = sessions.get(client);
    if(s == null){
      s = new State();
      sessions.put(client, s);
    }
    client.setState(s);
    service(client);
  }
  ...
}

и вы можете настроить поиск по карте, чтобы определить, что означает "сессия" в вашем приложении (тот же IP-адрес клиента, тот же IP-адрес клиента и клиентский порт, некоторый идентификатор сеанса, отправленный по сети и т. Д.).

Если вы просто пытаетесь дать возможность клиенту переподключиться и заставить сервер «заметить», что клиент отключен, единственный реальный способ в Java - это попытка чтения / записи данных, и если это было выключение, то это должно вызвать исключение. Поэтому, как уже упоминалось в вашем другом вопросе, вы можете добавить в протокол некую функцию ack / nak и добавить некоторую проверку, если считаете, что клиент отключен (например, если вы не читали никаких данных за последние N миллисекунд). , отправьте сообщение, которое клиент должен повторить в течение M миллисекунд, в противном случае предполагается, что он отключен). Вы также можете попробовать isConnected, isInputShutdown, isOutputShutdown, но я обнаружил, что они ненадежны в моем собственном коде для указания состояния сокета, если только вы не закрыли сокет (т.е. тот, который вы тестируете на сервере).

...