Сбросить TCP-соединение, если сервер закрывается / падает в середине соединения
У меня есть TCP-соединение, как показано ниже:
public void ConnectToServer()
{
string mac = GetUID();
while(true)
{
try
{
tcpClient = new TcpClient("xx.x.xx.xxx", xxxx);
networkstream = new SslStream(tcpClient.GetStream());
networkstream.AuthenticateAsClient("xx.x.xx.xxx");
networkstream.Write(Encoding.UTF8.GetBytes("0002:" + mac + "\r\n"));
networkstream.Flush();
string serverMessage = ReadMessage(networkstream);
Console.WriteLine("MESSAGE FROM SERVER: " + serverMessage);
}
catch (Exception e)
{
tcpClient.GetStream().Close();
tcpClient.Close();
}
}
}
Это прекрасно работает и может отправлять полученные данные на / с сервера.
Что мне нужно помочь, если сервер не работает, когда клиент запускается, он будет ждать, а затем подключится, когда сервер будет запущен. Но если и клиент, и сервер работают, и все работает, если я закрою сервер, клиент не будет повторно подключаться (потому что у меня пока нет ничего, чтобы обработать событие).
Я видел некоторые ответы здесь, которые предлагают опрос и тому подобное. Это единственный способ? Метод ReadMessage, который я вызываю, также попадает в бесконечный цикл. Я могу опубликовать этот код, если это будет необходимо.
Я действительно хотел бы определить, когда сервер закрывается / падает, закрывает поток и tcpclient и повторно подключается как можно скорее.
Вот мое чтение сообщения:
static string ReadMessage(SslStream sslStream)
{
if (sslStream.CanRead)
{
byte[] buffer = new byte[2048];
StringBuilder messageData = new StringBuilder();
int bytes = -1;
string message_type = null;
string actual_message = null;
do
{
try
{
Console.WriteLine("LENGTH: " + buffer.Length);
bytes = sslStream.Read(buffer, 0, buffer.Length);
Decoder decoder = Encoding.UTF8.GetDecoder();
char[] chars = new char[decoder.GetCharCount(buffer, 0, bytes)];
decoder.GetChars(buffer, 0, bytes, chars, 0);
messageData.Append(chars);
message_type = messageData.ToString().Substring(0, 5);
actual_message = messageData.ToString().Substring(5);
if (message_type.Equals("0001:"))
{
m_Window pop = new m_Window();
pop.callHttpPost(null, new EventArgs());
}
if (messageData.ToString().IndexOf("\r\n") != -1)
{
break;
}
}
catch (Exception e)
{
Console.WriteLine("ERROR: " + e.Message);
}
} while (bytes != 0);
return messageData.ToString();
}
return("CONNECTION HAS BEEN LOST");
}
1 ответ
С TCP у вас есть 2 вида отключения сервера:
- сервер закрыт
- сервер падает
Когда сервер закрыт, вы получите 0 байтов на своем клиентском сокете, и вы узнаете, что узел закрыл свой конец сокета, который называется половинным закрытием. Но все становится еще страшнее, если сервер падает. Когда это случится снова, у вас есть несколько возможностей. Если вы ничего не отправляете с клиента на сервер, у вас нет способа узнать, что сервер действительно вышел из строя. Единственный способ выяснить, что сервер потерпел крах, это разрешить клиенту что-то отправить или активировать поддержку активности. Если вы отправите в серверный сокет что-то, что не существует, вам придется подождать довольно длительный период, потому что TCP будет пытаться выполнить несколько раз, с повторной передачей, пока не будет получен ответ сервера. Если TCP повторил попытку несколько раз, он, наконец, выйдет из строя, и если у вас есть блокирующий сокет, вы увидите, что отправка не удалась, что означает, что вы должны закрыть свой сокет.
На самом деле существует третье возможное отключение сервера, то есть сброс, но он используется исключительно. Здесь я предполагаю, что если происходит корректное завершение работы сервера, выполняется нормальное закрытие сокета на стороне сервера. Который в конечном итоге будет отправлять FIN вместо RST, что является исключительным случаем.
Теперь вернемся к вашей ситуации: если сервер выходит из строя, он изначально заложен в конструкцию TCP из-за всех этих таймаутов повторной передачи и увеличивающихся задержек, что вам придется подождать некоторое время, чтобы действительно обнаружить, что есть проблема. Если сервер изящно закрыт и снова запускается, это не тот случай, это вы сразу обнаружите, получив 0 байтов.