Мгновенное обнаружение отключения клиента от сокета сервера


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

у меня есть следующий код в моей AcceptCallBack метод

static Socket handler = null;
public static void AcceptCallback(IAsyncResult ar)
{
  //Accept incoming connection
  Socket listener = (Socket)ar.AsyncState;
  handler = listener.EndAccept(ar);
}

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

Я пробовал:

  1. handler.Available;
  2. handler.Send(new byte[1], 0, SocketFlags.None);
  3. handler.Receive(new byte[1], 0, SocketFlags.None);

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

любая помощь будет оценили.

11 62

11 ответов:

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

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

static class SocketExtensions
{
  public static bool IsConnected(this Socket socket)
  {
    try
    {
      return !(socket.Poll(1, SelectMode.SelectRead) && socket.Available == 0);
    }
    catch (SocketException) { return false; }
  }
}

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

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

поэтому "мгновенно" нереально. Лучшее, что вы можете сделать, это в течение периода ожидания, который зависит от платформы, на которой выполняется код.

изменить: Если вы ищете только изящные соединения, то почему бы просто не отправить команду "Отключить" на сервер от вашего клиента?

кто-то упомянул возможность keepAlive сокета TCP. Вот это красиво описано:

http://tldp.org/HOWTO/TCP-Keepalive-HOWTO/overview.html

Я использую его таким образом: после того, как сокет подключен, я вызываю эту функцию, которая устанавливает keepAlive on. Элемент keepAliveTime параметр указывает тайм-аут в миллисекундах без активности до отправки первого пакета keep-alive. Элемент

" Это просто способ TCP работает, и вы должны жить с ним."

Да, ты прав. Это факт жизни, который я осознал. Вы увидите, что такое же поведение проявляется даже в профессиональных приложениях, использующих этот протокол (и даже другие). Я даже видел как это происходит в онлайн играх, ты приятель говорит "До свидания", и он оказывается в интернете еще 1-2 минуты, пока сервер не "чистит дом".

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

внедрение heartbeat в вашу систему может быть решением. Это возможно только если клиент и сервер находятся под вашим контролем. У вас может быть объект DateTime, отслеживающий время, когда последние байты были получены из сокета. И предположим, что сокет не ответил в течение определенного интервала теряются. Это будет работать только в том случае, если у вас есть heartbeat/custom keep alive.

Я нашел довольно полезным, еще один обходной путь для этого!

если вы используете асинхронные методы для чтения данных из сетевого гнезда (я имею в виду, использовать BeginReceive -EndReceive методы), всякий раз, когда соединение прекращается; одна из этих ситуаций появляется: либо сообщение отправляется без данных (вы можете увидеть его с Socket.Available - даже если BeginReceive срабатывает, его значение будет равно нулю) или Socket.Connected значение становится ложным в этом вызове (не пытайтесь использовать EndReceive потом).

Я разместив функцию, которую я использовал, я думаю, вы можете лучше понять, что я имел в виду:


private void OnRecieve(IAsyncResult parameter) 
{
    Socket sock = (Socket)parameter.AsyncState;
    if(!sock.Connected || sock.Available == 0)
    {
        // Connection is terminated, either by force or willingly
        return;
    }

    sock.EndReceive(parameter);
    sock.BeginReceive(..., ... , ... , ..., new AsyncCallback(OnRecieve), sock);

    // To handle further commands sent by client.
    // "..." zones might change in your code.
}

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

//open or receive a server socket - TODO your code here
socket = new Socket(....);

//enable the keep alive so we can detect closure
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);

//create a thread that checks every 5 seconds if the socket is still connected. TODO add your thread starting code
void MonitorSocketsForClosureWorker() {
    DateTime nextCheckTime = DateTime.Now.AddSeconds(5);

    while (!exitSystem) {
        if (nextCheckTime < DateTime.Now) {
            try {
                if (socket!=null) {
                    if(socket.Poll(5000, SelectMode.SelectRead) && socket.Available == 0) {
                        //socket not connected, close it if it's still running
                        socket.Close();
                        socket = null;    
                    } else {
                        //socket still connected
                    }    
               }
           } catch {
               socket.Close();
            } finally {
                nextCheckTime = DateTime.Now.AddSeconds(5);
            }
        }
        Thread.Sleep(1000);
    }
}

вы не можете просто использовать Select?

используйте select на подключенном сокете. Если select возвращается с вашим сокетом как готовый, но последующий Receive возвращает 0 байт, это означает, что клиент отключил соединение. AFAIK, это самый быстрый способ определить, отключен ли клиент.

Я не знаю C# так что просто игнорировать, если мое решение не вписывается в C# (C# предоставляет выберите Хотя) или если бы я неправильно понял контекст.

вот пример кода http://msdn.microsoft.com/en-us/library/system.net.sockets.socket.connected.aspx показывает, как определить, подключен ли сокет по-прежнему без отправки каких-либо данных.

Если вы вызвали сокет.BeginReceive () на серверной программе, а затем клиент закрыл соединение "изящно", ваш обратный вызов receive будет вызван, и EndReceive () вернет 0 байт. Эти 0 байт означают, что клиент "возможно" отключился. Затем вы можете использовать метод, показанный в примере кода MSDN, чтобы точно определить, было ли соединение закрыто.

используя метод SetSocketOption, вы сможете установить KeepAlive, который позволит вам знать, когда сокет отключается

Socket _connectedSocket = this._sSocketEscucha.EndAccept(asyn);
                _connectedSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, 1);

http://msdn.microsoft.com/en-us/library/1011kecd (v=VS. 90). aspx

надеюсь, что это помогает! Рамиро Ринальди

вы также можете проверить .IsConnected свойство сокета, если вы должны были опросить.