Как проверить, подключен или отключен сокет в С#?
Как вы можете проверить, подключен ли сетевой сокет (System.Net.Sockets.Socket), если другой хост не отправляет вам пакет, когда он отключается (например, потому что он отключен несправедливо)?
Ответы
Ответ 1
Как Пол Тернер ответил 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, если
- соединение закрыто, reset, завершено или ожидает (что означает отсутствие активного соединения)
- активно и доступны данные для чтения
-
s.Available
возвращает количество байтов, доступных для чтения
- если оба истины:
- нет данных для чтения, поэтому соединение неактивно
Ответ 2
Как 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;
*/
}
Ответ 3
Свойство Socket.Connected
сообщит вам, считает ли сокет его подключением. Он фактически отражает состояние последней операции отправки/получения, выполняемой в сокете.
Если сокет был закрыт вашими собственными действиями (удалив сокет, вызывая методы для отключения), Socket.Connected
вернет false
. Если сокет был отключен другими средствами, свойство вернет true
, пока вы не попытаетесь отправить или получить информацию, после чего будет выведено значение SocketException
или ObjectDisposedException
.
Вы можете проверить свойство после возникновения исключения, но оно не является надежным до этого.
Ответ 4
Принятый ответ не работает, если вы отключите сетевой кабель. Или сервер сбой. Или ваш маршрутизатор падает. Или если вы забудете оплатить свой интернет-счет. Задайте параметры сохранения TCP для лучшей надежности.
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);
Значение времени задает время ожидания с момента последнего отправки данных. Затем он пытается отправить и получить пакет keep-alive. Если он терпит неудачу, он повторяет попытку 10 раз (номер жестко запрограммирован с Vista AFAIK) в интервале, указанном до принятия решения о отключении соединения.
Таким образом, приведенные выше значения приведут к обнаружению 2 + 10 * 1 = 12 секунд. После этого любые операции чтения /wrtie/poll не будут выполняться в сокете.
Ответ 5
Я сделал метод расширения, основанный на этой статье 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;
}
}
Ответ 6
Лучший способ - просто отправить клиенту PING каждые X секунд, и для сервера предположить, что он отключен после того, как он не получил какое-то время.
Я столкнулся с той же проблемой, что и при использовании сокетов, и это был единственный способ сделать это. Свойство socket.connected никогда не было правильным.
В конце концов, я переключился на использование WCF, потому что он был намного надежнее, чем сокеты.
Ответ 7
Следуя советам NibblyPig и zendar, я придумал код ниже, который работает на каждом испытании, которое я сделал. В итоге мне потребовались как пинг, так и опрос. Пинг сообщит мне, отсоединен ли кабель или физический уровень в противном случае нарушен (отключен маршрутизатор и т.д.). Но иногда после повторного подключения я получаю RST, ping в порядке, но состояние tcp не является.
#region CHECKS THE SOCKET 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
Ответ 8
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 на свой сокет - socket.SetKeepAlive(1000, 2);
- Добавить таймер для проверки функции IsConnected
Ответ 9
Используйте Socket.Connected
Свойство.