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

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

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

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

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

Я пробовал:

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

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

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

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

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

 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, и вам нужно жить с ним.

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

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

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

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

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

  void SetKeepAlive(bool on, uint keepAliveTime , uint keepAliveInterval ) { int size = Marshal.SizeOf(new uint()); var inOptionValues = new byte[size * 3]; BitConverter.GetBytes((uint)(on ? 1 : 0)).CopyTo(inOptionValues, 0); BitConverter.GetBytes((uint)time).CopyTo(inOptionValues, size); BitConverter.GetBytes((uint)interval).CopyTo(inOptionValues, size * 2); socket.IOControl(IOControlCode.KeepAliveValues, inOptionValues, null); } 

Я также использую синхронное чтение:

 socket.BeginReceive(packet.dataBuffer, 0, 128, SocketFlags.None, new AsyncCallback(OnDataReceived), packet); 

А в SocketException здесь выловлен тайм-аут SocketException , который возникает, когда сокет не получает ACK-сигнал после пакета keep-alive.

 public void OnDataReceived(IAsyncResult asyn) { try { SocketPacket theSockId = (SocketPacket)asyn.AsyncState; int iRx = socket.EndReceive(asyn); catch (SocketException ex) { SocketExceptionCaught(ex); } } 

Таким образом, я могу безопасно обнаруживать разъединение между TCP-клиентом и сервером.

«Это только то, как работает TCP, и вам нужно жить с ним».

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

Вы можете использовать предложенные методы здесь или реализовать «сердцебиение», также как и предлагалось. Я выбираю первое. Но если бы я выбрал последнее, я бы просто запустил сервер «ping» каждого клиента каждый раз с одним байтом и посмотрел, есть ли у нас тайм-аут или нет ответа. Вы могли бы даже использовать фоновый stream для достижения этого с точным временем. Возможно, даже комбинация может быть реализована в каком-то списке опций (флаги enums или что-то еще), если вы действительно волнуетесь об этом. Но не так уж сложно договориться о небольшой задержке в обновлении сервера, если вы ДОЛЖНЫ обновляться. Это интернет, и никто не ожидает, что он будет волшебным! 🙂

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

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

Если вы используете асинхронные методы для чтения данных из сетевого сокета (я имею в виду, используйте BeginReceiveEndReceive ), всякий раз, когда соединение завершается; появляется одна из этих ситуаций: либо сообщение отправляется без данных (вы можете увидеть его с помощью 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. } 

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

 //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 на подключенном сокете. Если выбор возвращается с вашим сокетом Ready, а последующий Receive возвращает 0 байт, что означает, что клиент отключил соединение. AFAIK, это самый быстрый способ определить, отключен ли клиент.

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

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

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

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

 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 сокета, если вы хотите опросить.

  • Правильное закрытие SSLSocket
  • Передача изображения OpenCV на C ++ через сокет
  • Как обнаружить разъединение сокета TCP (с гнездом C Berkeley)
  • Клиент сервера посылает / получает простой текст
  • Как проверить, закрыто ли соединение TcpClient?
  • сокет программирует несколько клиентов на один сервер
  • Почему писать закрытый TCP-разъем хуже, чем читать?
  • Java Создание новых блоков ObjectInputStream
  • Как найти полное имя хоста текущего компьютера в C (имя хоста и информация о домене)?
  • Как установить тайм-аут блокировки сокетов в boost asio?
  • как получить файл передачи между клиентом и сервером с помощью java socket
  • Давайте будем гением компьютера.