Лучшая практика для обнаружения отключения клиента в .NET?

Я разрабатываю сервер на C #, который может принимать только одного клиента, и мне нужно знать, когда этот клиент отключен, чтобы иметь возможность принимать другие запросы на подключение.

Я использую первый сокет, который постоянно прослушивает запрос на соединение с Socket.BeginAccept и принимает или отклоняет клиентов. Когда клиент принят, новый Socket, который возвращается Socket.EndAccept, используется для связи между клиентом и сервером. Затем сервер ждет команд от клиента с Socket.Begin/EndReceive и отправляет ответы. Сервер использует протокол, подобный Telnet, что означает, что каждая команда и каждая строка ответа должны заканчиваться на \r\n.

Чтобы определить, был ли клиент отключен, я установил таймер, который каждые 500 мс отправляет клиенту пустое сообщение («\r\n»). Если клиент отключен, Socket выдает исключение. Это исключение перехватывается сервером, который закрывает текущий сеанс и принимает новое соединение. Это надежное решение, но подразумевает ненужный трафик по сети и должно правильно обрабатываться клиентом, который должен фильтровать фиктивные сообщения перед получением фактического ответа.

Я пытался отправить пустой буфер (Socket.Send(new byte[1], 0, 0)), но похоже, что он не работает в направлении сервер-> клиент.

Другим решением может быть случай, когда Socket.EndReceive возвращает 0 байт. Он отлично работает в случае отключения, которое происходит во время "простоя". Но если клиент отключается во время передачи сообщения, сервер не всегда его видит и ждет бесконечно.

Я уже видел несколько тем и вопросов по этой проблеме, но никогда не видел хорошего решения.

Итак, мой вопрос: как лучше всего обнаружить отключение в .Net?


person cedrou    schedule 02.09.2009    source источник
comment
Единственное, что меня действительно раздражает, - это когда вы читаете сетевой поток, и он возвращает ноль. Обычно это указывает на отключение сети, НО по какой-то странной причине это не вызывает исключения. Итак, ваш прекрасно написанный блок try-catch не работает. Мое решение заключалось в создании метода чтения оболочки, в котором вы проверяете нулевой возврат и вызываете собственное исключение. Мне все еще кажется, что нулевой доход должен вызывать исключение.   -  person Bing Bang    schedule 11.10.2019


Ответы (4)


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

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

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

person Chris Chilvers    schedule 02.09.2009
comment
Хорошо, я добавил код, который включает KeepAlive для моего сокета. Но чего мне теперь ожидать? Ничего не происходит, когда клиент разрывает соединение ... Я ожидал получить исключение где-то ... Должен ли я что-то периодически читать или писать? - person cedrou; 03.09.2009
comment
Я думал, вы использовали Begin / EndReceive? Если это так, BeginReceive должен запустить обратный вызов, и когда вы вызываете EndReceive для получения результата, должно быть вызвано исключение, это или конец чтения нулевого байта потока. - person Chris Chilvers; 03.09.2009
comment
Хорошо, теперь у меня исключение ... Спасибо - person cedrou; 07.09.2009

Вы можете использовать метод ниже, чтобы узнать, подключен ли все еще клиент. Этот

public static bool IsConnected(this TcpClient client)
{
    try
    {
        bool connected = !(client.Client.Poll(1, SelectMode.SelectRead) && client.Client.Available == 0);

        return connected;
    }
    catch
    {
        return false;
    }
}

Это answer предназначен для тестирования сокета, из которого я получил свой фрагмент кода.

person David Basarab    schedule 02.09.2009
comment
Этот метод отлично работает, если клиент явно закрыл соединение, но не в том случае, если соединение было разорвано (например, отключен кабель). - person cedrou; 02.09.2009
comment
Вот тут-то и появляется keep alive, ОС и протокол TCP будут обрабатывать периодические сообщения клиенту, чтобы узнать, жив ли он, а опрос сокета будет проверять локальный статус. В случае сбоя проверки активности сокет должен сообщить, что он больше не подключен. - person Chris Chilvers; 02.09.2009
comment
Хотя при использовании async Begin / EndReceive опрос не требуется, так как вы должны получить обратный вызов с ошибкой, когда сокет не поддерживает сохранение активности. - person Chris Chilvers; 02.09.2009
comment
Этот метод страдает от состояния гонки, которое приводит к ложным срабатываниям, как отмечено здесь. - person Ian; 15.11.2018

Вы контролируете клиента? Если да, то не можете ли вы просто попросить клиента отправить на сервер специальный пакет с сообщением о том, что он отключается, а затем отключить сокет?

Вы пытаетесь определить случай, когда клиент действительно отключился (с помощью Socket.Shutdown () или Socket.Close ()) или клиент простаивает в течение огромного количества времени и, следовательно, должен быть удален?

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

person feroze    schedule 10.09.2009