Как проверить, подключен ли сокет / отключен в C#?
Как вы можете проверить, если сетевой сокет (System.Net.Sockets.Socket) по-прежнему подключен, если другой хост не отправляет вам пакет, когда он отключается (например, потому, что он отключен нелюбезно)?
11 ответов:
как Пол Тернер ответил
Socket.Connected
не может использоваться в этой ситуации. Вам нужно опрашивать соединение каждый раз, чтобы увидеть, если соединение все еще активно. Это код, который я использовал:bool SocketConnected(Socket s) { bool part1 = s.Poll(1000, SelectMode.SelectRead); bool part2 = (s.Available == 0); if (part1 && part2) return false; else return true; }
это работает так:
s.Poll
возвращает true, если
- соединение закрыто, сброшено, прервано или ожидает (что означает отсутствие активного соединения)
- соединение активно и данные доступны для чтения
s.Available
возвращает количество байтов, доступных для чтения- если оба истинны:
- нет данных, доступных для чтения, поэтому соединение не активно
как zendar написал, приятно использовать
Socket.Poll
иSocket.Available
, но вы должны принять во внимание, что сокет, возможно, не был инициализирован в первую очередь. Это последняя (я считаю) часть информации, и она предоставляетсяSocket.Connected
собственность. Пересмотренная версия метода будет выглядеть примерно так:static bool IsSocketConnected(Socket s) { return !((s.Poll(1000, SelectMode.SelectRead) && (s.Available == 0)) || !s.Connected); /* The long, but simpler-to-understand version: bool part1 = s.Poll(1000, SelectMode.SelectRead); bool part2 = (s.Available == 0); if ((part1 && part2 ) || !s.Connected) return false; else return true; */ }
The
Socket.Connected
свойство скажет вам, является ли сокет думает это связано. Он фактически отражает состояние последней операции отправки / получения, выполненной на сокете.если сокет был закрыт вашими собственными действиями (удаление сокета, вызов методов для отключения),
Socket.Connected
вернутсяfalse
. Если сокет был отключен другим способом, свойство вернетtrue
до следующей попытки отправить или получить информация, в какой момент либо aSocketException
илиObjectDisposedException
будет брошен.вы можете проверить свойство после того, как произошло исключение, но оно не является надежным раньше.
принятый ответ, похоже, не работает, если вы отключите сетевой кабель. Или сервер аварийно завершает работу. Или ваш маршрутизатор аварийно завершает работу. Или если вы забыли оплатить счет за интернет. Установите параметры TCP keep-alive для повышения надежности.
public static class SocketExtensions { public static void SetSocketKeepAliveValues(this Socket instance, int KeepAliveTime, int KeepAliveInterval) { //KeepAliveTime: default value is 2hr //KeepAliveInterval: default value is 1s and Detect 5 times //the native structure //struct tcp_keepalive { //ULONG onoff; //ULONG keepalivetime; //ULONG keepaliveinterval; //}; int size = Marshal.SizeOf(new uint()); byte[] inOptionValues = new byte[size * 3]; // 4 * 3 = 12 bool OnOff = true; BitConverter.GetBytes((uint)(OnOff ? 1 : 0)).CopyTo(inOptionValues, 0); BitConverter.GetBytes((uint)KeepAliveTime).CopyTo(inOptionValues, size); BitConverter.GetBytes((uint)KeepAliveInterval).CopyTo(inOptionValues, size * 2); instance.IOControl(IOControlCode.KeepAliveValues, inOptionValues, null); } } // ... Socket sock; sock.SetSocketKeepAliveValues(2000, 1000);
значение времени задает время ожидания с момента последней отправки данных. Затем он пытается отправить и получить контрольный пакет. Если это не удается, он повторяет попытку 10 раз (номер жестко закодирован с Vista AFAIK) в интервале, указанном до принятия решения о подключении мертв.
таким образом, приведенные выше значения приведут к 2+10*1 = 12 секунд обнаружения. После этого любые операции чтения / wrtie / poll должны завершиться неудачей на сокете.
Я сделал метод расширения на основе этой статья MSDN. Таким образом можно определить, подключен ли сокет по-прежнему.
public static bool IsConnected(this Socket client) { bool blockingState = client.Blocking; try { byte[] tmp = new byte[1]; client.Blocking = false; client.Send(tmp, 0, 0); return true; } catch (SocketException e) { // 10035 == WSAEWOULDBLOCK if (e.NativeErrorCode.Equals(10035)) { return true; } else { return false; } } finally { client.Blocking = blockingState; } }
лучший способ-просто заставить вашего клиента отправлять пинг каждые X секунд, и для сервера предположить, что он отключен после того, как он не получил его некоторое время.
Я столкнулся с той же проблемой, что и вы при использовании сокетов, и это был единственный способ, которым я мог это сделать. Розетка.подключенное свойство никогда не было правильным.
в конце концов, я переключился на использование WCF, потому что он был гораздо более надежным, чем сокеты.
следуя совету от NibblyPig и zendar, Я придумал код ниже, который работает на каждом тесте, который я сделал. В итоге мне понадобился и пинг, и опрос. Пинг даст мне знать, если кабель был отключен, или физический уровень в противном случае нарушен (маршрутизатор выключен и т. д.). Но иногда после повторного подключения я получаю первый, пинг в порядке, но состояние tcp-нет.
#region CHECKS THE SOCKET'S HEALTH if (_tcpClient.Client.Connected) { //Do a ping test to see if the server is reachable try { Ping pingTest = new Ping() PingReply reply = pingTest.Send(ServeripAddress); if (reply.Status != IPStatus.Success) ConnectionState = false; } catch (PingException) { ConnectionState = false; } //See if the tcp state is ok if (_tcpClient.Client.Poll(5000, SelectMode.SelectRead) && (_tcpClient.Client.Available == 0)) { ConnectionState = false; } } } else { ConnectionState = false; } #endregion
public static class SocketExtensions { private const int BytesPerLong = 4; // 32 / 8 private const int BitsPerByte = 8; public static bool IsConnected(this Socket socket) { try { return !(socket.Poll(1000, SelectMode.SelectRead) && socket.Available == 0); } catch (SocketException) { return false; } } /// <summary> /// Sets the keep-alive interval for the socket. /// </summary> /// <param name="socket">The socket.</param> /// <param name="time">Time between two keep alive "pings".</param> /// <param name="interval">Time between two keep alive "pings" when first one fails.</param> /// <returns>If the keep alive infos were succefully modified.</returns> public static bool SetKeepAlive(this Socket socket, ulong time, ulong interval) { try { // Array to hold input values. var input = new[] { (time == 0 || interval == 0) ? 0UL : 1UL, // on or off time, interval }; // Pack input into byte struct. byte[] inValue = new byte[3 * BytesPerLong]; for (int i = 0; i < input.Length; i++) { inValue[i * BytesPerLong + 3] = (byte)(input[i] >> ((BytesPerLong - 1) * BitsPerByte) & 0xff); inValue[i * BytesPerLong + 2] = (byte)(input[i] >> ((BytesPerLong - 2) * BitsPerByte) & 0xff); inValue[i * BytesPerLong + 1] = (byte)(input[i] >> ((BytesPerLong - 3) * BitsPerByte) & 0xff); inValue[i * BytesPerLong + 0] = (byte)(input[i] >> ((BytesPerLong - 4) * BitsPerByte) & 0xff); } // Create bytestruct for result (bytes pending on server socket). byte[] outValue = BitConverter.GetBytes(0); // Write SIO_VALS to Socket IOControl. socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); socket.IOControl(IOControlCode.KeepAliveValues, inValue, outValue); } catch (SocketException) { return false; } return true; } }
- скопируйте класс SocketExtensions в свой проект
- вызовите SetKeepAlive на вашем сокет-сокете.SetKeepAlive(1000, 2);
- добавить таймер для проверки IsConnected функции
Как Александр Логгер указал на zendars ответ, вы должны отправить что-то, чтобы быть полностью уверенным. Если ваш подключенный партнер вообще читает этот сокет, вы можете использовать следующий код.
bool SocketConnected(Socket s) { // Exit if socket is null if (s == null) return false; bool part1 = s.Poll(1000, SelectMode.SelectRead); bool part2 = (s.Available == 0); if (part1 && part2) return false; else { try { int sentBytesCount = s.Send(new byte[1], 1, 0); return sentBytesCount == 1; } catch { return false; } } }
но даже тогда, это может занять несколько секунд, пока не обнаружил обрыв сетевого кабеля или что-то подобное.
просто используйте KeepAlive, как говорит @toster-cx, а затем используйте состояние подключения сокета, чтобы проверить, подключен ли сокет. Набор Вы получаете тайм-аут в то же время ожидания функции keepalive. Если у вас есть вопросы я всегда рад помочь!
использовать
Socket.Connected
собственность.