.Net Socket не отвечает на удаленное отключение? - PullRequest
4 голосов
/ 13 марта 2012

Я пишу небольшое (C #) клиентское приложение, которое отправляет данные с использованием соединения TCP / IP на удаленный сервер.Я использую стандартный объект .Net TcpClient и хочу оставить соединение открытым со стороны клиента, так как я регулярно отправляю пакеты данных на сервер.Однако возможно, что сервер может закрыть соединение, и в этом случае мне нужно знать, чтобы повторно подключиться перед отправкой моего следующего пакета.

Используя Wireshark, я вижу (только) следующий диалог, когдасервер прерывает соединение:

server >>> FIN, ACK<br> ACK <<< client

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

Можно ли каким-либо образом настроить TcpClient или его основной сокет, чтобы завершить отключение, и предоставитькакая-то обратная связь, чтобы мой клиентский код знал о повторном подключении перед отправкой следующего пакета?

Добавлено в ответ на комментарий ниже: Мой код отправки очень прост - объект, который поддерживает TcpClientи переменные-члены NetworkStream, имеет функцию-член, содержащую (по существу) следующее:

bool sent = false;
byte[] buffer = Encoding.UTF8.GetBytes(dataString);
while (!sent)
{
    try
    {
        m_outStream.Write(buffer, 0, buffer.Length);
        sent = true;
    }
    catch (Exception ex)
    {
        if (m_outStream != null) { m_outStream.Dispose(); }
        m_client = new TcpClient(AddressFamily.InterNetwork);
        m_client.Connect(ipAddress, ipPort);
        m_outStream = m_client.GetStream();
    }
}

С инициализированными m_client и m_outStream это просто выполняет один проход каждый раз.Затем, используя Wireshark, я вижу, как сервер отправляет пакет с флагами FIN, ACK, на который клиент отвечает ACK.

В следующий раз, когда я вызываю свою функцию, данные отправляются с PSH, ACK,и сервер отвечает RST, ACK, но не читает входящие данные.Клиент не вызывает никаких исключений.

Затем я вызываю свою функцию во второй раз, и возникает исключение, вызывающее перезапуск соединения.

Ответы [ 4 ]

5 голосов
/ 13 марта 2012

Как правило, вы должны иметь возможность использовать свойство Connected в экземпляре TcpCient:

Смотрите здесь:
http://msdn.microsoft.com/en-us/library/system.net.sockets.tcpclient.connected.aspx

Тем не менее:

Поскольку свойство Connected отражает только состояние подключение с самой последней операции, вы должны попытаться отправить или получите сообщение для определения текущего состояния. После сообщения отправить не удалось, это свойство больше не возвращает true. Обратите внимание, что это Поведение - это дизайн. Вы не можете достоверно проверить состояние соединение, потому что во время между тестом и отправкой / получением, связь могла быть потеряна. Ваш код должен предполагать сокет подключен, и изящно обрабатывать неудачные передачи.

Попробуйте следующее, чтобы убедиться, что флаг Connected содержит самое последнее состояние:

var tcpClient = new TcpClient ();
tcpClient.Connect();

var stream = tcpClient.GetStream();

// buffer size need to be > 0
int[] buffer = new int[1];
stream.Read(buffer, 0, 0);

if(!tcpClient.Connected)
    // do something

На основе декомпиляции должна быть возможность прочитать 0 байт из потока, по крайней мере, в .NET Framework TcpClient нет проверки, которая предотвращает это. Однако во внешнем коде, который вызывается из фреймворка, он может быть не слышен для фактического чтения из сетевого потока.

Обязательно наберите Dispose как TcpClient, так и Stream после того, как вы закончили, утилизация TcpClient не избавляет от Stream, поэтому вам нужно сделать это вручную, после чего все ресурсы будут освобождены (после ГХ).

3 голосов
/ 05 сентября 2014

Из MSDN TcpClient.Connected свойство:
Тип: System.Boolean
true, если сокет клиента был подключен к удаленному ресурсу на момент последней операции;иначе ложно.

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

См. Мой ответ по связанному вопросу (https://stackoverflow.com/a/25680975/2505186),
, в котором приведен ответ на вопрос другого человека, где описан подходящий способ определения состояния подключения.: Как проверить, закрыто ли соединение TcpClient?

Важно для вас:
Клиент не закрывает соединение автоматически, когда это делает сервер. Соединение находится в состоянии CLOSE_WAITзатем на стороне клиента и в состоянии FIN_WAIT2 на стороне сервера. См. соответствующий раздел в статье Википедии Протокол управления передачей . Используя код из приведенного выше ответа, вы можете обнаружить, что соединение собираетсязакройте. Кроме того, вы можете завершить процедуру закрытия и открыть ее при необходимости.

1 голос
/ 26 января 2016

Этот метод используется для определения состояния подключения.

static class SocketExtensions
    {
        /// <summary>
        /// Extension method to tell if the Socket REALLY is closed
        /// </summary>
        /// <param name="socket"></param>
        /// <returns></returns>
        public static bool IsConnected(this Socket socket)
        {
            try
            {
                return !(socket.Poll(1, SelectMode.SelectRead) && socket.Available == 0);
            }
            catch (SocketException) { return false; }
        }
    }

Когда я хочу отключить соединение, я вызываю следующее.Закрытие нижележащего потока, а затем клиентского объекта сверху.Я включаю его в попытки и уловы, чтобы попытаться закрыть их на каждом.Примечание. В этом случае PeerStream - это NetworkStream (из Client.GetStream ())

/// <summary>
        /// Method will disconnect this peer forcefully
        /// </summary>
        public void Disconnect()
        {
            try
            {
                PeerStream.Close();
            }
            catch (Exception ee)
            {
            }
            try
            {
                _client.Client.Disconnect(false);
            }
            catch (Exception ee)
            {

            }
        }
0 голосов
/ 13 марта 2012

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

Хотя я до сих пор не знаю, смогу ли я заставить свой TcpClient завершить отключение, я могу надежно определить, отключилось ли сокет, используя следующий код:

if (m_client.Client.Poll(1000, SelectMode.SelectRead) 
&& (m_client.Client.Available == 0))
{
    // Connection has gone - reconnect!
    m_client = new TcpClient(AddressFamily.InterNetwork);
    m_client.Connect(ipAddress, ipPort);
}
else
{
    // Connection is good, nothing to do
}
...