Я должен закрыть сокеты от обоих концов?

У меня есть следующая проблема. Моя клиентская программа контролирует для доступности сервера в локальной сети (использующий Добрый день, но это не сплачивает мать). Как только сервер "замечен" клиентским приложением, клиент пытается создать сокет: Socket(serverIP,serverPort);.

В какой-то момент клиент может освободить сервер (Добрый день говорит, что сервер больше не видим в сети). Так, клиент решают close сокет, потому что это больше не допустимо.

В некоторый момент сервер появляется снова. Так, клиент пытается создать новый сокет, связанный с этим сервером. Но! Сервер может отказаться создавать этот сокет начиная с него (сервер) уже, имеет сокет, связанный с клиентским IP и клиентским портом. Это происходит, потому что сокет был закрыт клиентом, не сервером. Это может произойти? И если имеет место, как эта проблема может быть решена?

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

6
задан tshepang 14 May 2014 в 20:44
поделиться

6 ответов

Краткий ответ: да, вы должны закрыть сокеты на обоих концах.

Хотя ответ прост, в действительности может быть очень трудно обнаружить, что одноранговый узел перестал отвечать, если вы не встроите какую-либо схему ACK / NACK в свой протокол клиент-сервер.

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

Если вы используете блокировку ввода-вывода, я бы начал с установки таймаутов чтения для сокета. К сожалению, если одноранговый узел перестает отвечать, соответствующего тайм-аута для записи нет. Один грубый инструмент, который, как я обнаружил, имеет ценность в нашей среде, - это создание блокирующих сокетов с помощью методов java.nio, а затем прерывание обработки поток через настраиваемые интервалы.

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

Однако с неблокирующим вводом-выводом вы можете более детально проверять состояние вашего соединения и более разумно реагировать на медленные / не отвечающие соединения.

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

1
ответ дан 17 December 2019 в 02:26
поделиться

клиентская операционная система не назначит тот же порт новому сокету так скоро. Есть несколько механизмов, которые этому препятствуют. одним из которых является состояние TIME_WAIT, которое резервирует порт на некоторое время после закрытия соединения.

Я бы не стал об этом беспокоиться.

если вам действительно нужно обнаружить отключение, вам придется реализовать протокол ping / pong, инициируемый как клиентом, так и сервером.

1
ответ дан 17 December 2019 в 02:26
поделиться

Быстрая / грязная иллюстрация того, почему этого не может произойти (обратите внимание, что клиент принудительно использует один и тот же локальный порт в своем соединении):

public class Server{

public static void main(String[] args) throws Exception {
    new Thread(){
        java.net.ServerSocket server = new java.net.ServerSocket(12345);
        java.util.ArrayList<java.net.Socket> l = new java.util.ArrayList<java.net.Socket>();
        public void run() {
            try{
            while(true){
                 java.net.Socket client = server.accept();
                System.out.println("Connection Accepted: S: "+client.getLocalPort()+", C: "+client.getPort());
                l.add(client);
            }
            }catch(Exception e){e.printStackTrace();}
        }
    }.start();
}

и клиент (замените адрес сервера на что-то действительное):

import java.net.InetAddress;
import java.net.Socket;


public class SocketTest {
    public static void main(String[] args) throws Exception {
        InetAddress server = InetAddress.getByName("192.168.0.256");
        InetAddress localhost = InetAddress.getLocalHost();
        Socket s = new Socket(server, 12345, localhost, 54321);
        System.out.println("Client created socket");
        s.close();
        s = null;
        System.gc();
        System.gc();
        Thread.sleep(1000);
        s = new Socket(server, 12345, localhost, 54321);
        System.out.println("Client created second socket");
        s.close();
        System.exit(55);
    }
}

Если вы запустите сервер, а затем попробуйте запустить клиент, первое соединение будет успешным, но второе не будет выполнено с ошибкой "java.net.BindException: Адрес уже используется: соединение"

2
ответ дан 17 December 2019 в 02:26
поделиться

Да, закройте сокет, как только обнаружите сбой.

Сокет будет «застрять» в «close_wait», если не будет закрыт должным образом. Даже если сокет закрыт, его состояние будет в time_wait на короткое время.

Однако, если вы разрабатываете приложение для использования разных локальных портов для каждого нового соединения, нет необходимости ждать закрытия старого сокета.

(Поскольку вы затем создаете совершенно другой сокет, поскольку сокет идентифицируется удаленным IP, удаленным портом, локальным IP и локальным портом.)

2
ответ дан 17 December 2019 в 02:26
поделиться

Похоже, ваш клиент обнаруживает потерю связи с сервером (используя Bonjour), но у вас нет соответствующей возможности в другом направлении.

Вам наверняка понадобится какой-то тайм-аут для неактивных соединений на стороне сервера, иначе мертвые соединения будут висеть вечно. Помимо проблемы потенциальных конфликтов IP-адресов и номеров портов, о которых вы упомянули, существует также тот факт, что неработающие соединения потребляют ресурсы ОС и приложений (например, дескрипторы открытых файлов)

. И наоборот, вы также можете рассмотреть возможность того, чтобы не быть too агрессивно закрывает соединение со стороны клиента, когда Bonjour сообщает, что служба больше не отображается. Если вы используете беспроводную сеть, временная потеря подключения не такая уж редкость, и TCP-соединение может оставаться открытым и действительным после восстановления подключения (при условии, что у клиента все еще есть тот же IP-адрес). Оптимальная стратегия зависит от того, о каком типе связи вы говорите.Если это соединение относительно без сохранения состояния, когда стоимость отказа от соединения и повторной попытки низка (например, HTTP), то имеет смысл отказаться от соединения при первых признаках проблемы. Но если это долгоживущее соединение со значительным пользовательским состоянием (например, сеанс входа в систему по SSH), имеет смысл приложить больше усилий, чтобы поддерживать соединение.

1
ответ дан 17 December 2019 в 02:26
поделиться

Если вы закрываете сокет сервера только в случае блокировки сокета, тогда клиентский сокет будет закрыто, но не наоборот.

в противном случае лучше было бы гнездо на обоих концах. Потому что сокет - это тяжелый груз для вашей системы. Он всегда будет использовать локальный порт и удаленный порт вашей системы.

Спасибо Сунил Кумар Саху

-1
ответ дан 17 December 2019 в 02:26
поделиться
Другие вопросы по тегам:

Похожие вопросы: