c# обнаружение отключения tcp

у меня есть два простых приложения:

  • серверное приложение, ожидающее подключения клиента к определенному tcp-порту. Затем прислушивается к тому, что он говорит, отправляет обратную связь и отключает этого клиента.

  • приложение формы, которое подключается к серверному приложению, затем что-то говорит, затем дождитесь обратной связи и отключитесь от сервера, затем покажите обратную связь в форме.

хотя серверное приложение, похоже, ведет себя правильно (я тестировал его с Telnet, и я вижу обратную связь, и я вижу, что отключение происходит непосредственно после обратной связи), однако приложение формы похоже, не замечает отключения от сервера. ( объект TcpClient.Connected, кажется, остается верным даже после отключения сервера)

мой вопрос: почему TcpClient.Подключено оставаясь истинным, и как я могу знать, если / когда сервер отключен?

здесь мой полный код:

анкета:

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace Sender
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void sendButton_Click(object sender, EventArgs e)
        {
            TcpClient tcpClient = new TcpClient();
            tcpClient.Connect(IPAddress.Parse("127.0.0.1"), 81);
            responseLabel.Text = "waiting for response...";
            responseLabel.Invalidate();

            // write request
            NetworkStream networkStream = tcpClient.GetStream();
            byte[] buffer = (new ASCIIEncoding()).GetBytes("Hello World! ");
            networkStream.Write(buffer, 0, buffer.Length);
            networkStream.Flush();

            // read response
            Thread readThread = new Thread(new ParameterizedThreadStart(ReadResponse));
            readThread.Start(tcpClient);
        }

        void ReadResponse(object arg)
        {
            TcpClient tcpClient = (TcpClient)arg;
            StringBuilder stringBuilder = new StringBuilder();
            NetworkStream networkStream = tcpClient.GetStream();
            bool timeout = false;
            DateTime lastActivity = DateTime.Now;
            while (tcpClient.Connected && !timeout)
            {
                if (networkStream.DataAvailable)
                {
                    lastActivity = DateTime.Now;
                    while (networkStream.DataAvailable)
                    {
                        byte[] incomingBuffer = new byte[1024];
                        networkStream.Read(incomingBuffer, 0, 1024);
                        char[] receivedChars = new char[1024];
                        (new ASCIIEncoding()).GetDecoder().GetChars(incomingBuffer, 0, 1024, receivedChars, 0);
                        stringBuilder.Append(receivedChars);
                    }
                }
                else
                {
                    if (DateTime.Now > lastActivity.AddSeconds(60))
                        timeout = true;
                }
                System.Threading.Thread.Sleep(50);
            }
            Invoke((MethodInvoker)delegate
            {
                responseLabel.Text = "Response from Listener:n" + stringBuilder.ToString();
                responseLabel.Invalidate();
            });

            if (timeout)
            {
                Console.Write("A timeout occuredn");
                networkStream.Close();
                tcpClient.Close();
            }
        }

    }
}

сервер приложений:

using System.Net;
using System.Net.Sockets;
using System.Text;
using System;
using System.Threading;

namespace Listener
{
    class Program
    {
        static void Main(string[] args)
        {
            var tcpListener = new TcpListener(IPAddress.Any, 81);
            tcpListener.Start();
            Thread clientThread = new Thread(new ParameterizedThreadStart(Listen));
            clientThread.Start(tcpListener);
        }

        static void Listen(object arg)
        {
            TcpListener tcpListener = (TcpListener)arg;
            while (true)
            {
                TcpClient tcpClient = tcpListener.AcceptTcpClient();
                Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClient));
                clientThread.Start(tcpClient);
            }
        }

        static void HandleClient(object arg)
        {
            TcpClient tcpClient = (TcpClient)arg;
            StringBuilder stringBuilder = new StringBuilder();
            ASCIIEncoding encoder = new ASCIIEncoding();
            DateTime lastActivity = DateTime.Now;

            // read request
            NetworkStream networkStream = tcpClient.GetStream();
            int timeout = 5; // gives client some time to send data after connecting
            while (DateTime.Now < lastActivity.AddSeconds(timeout) && stringBuilder.Length==0)
            {
                if (!networkStream.DataAvailable)
                {
                    System.Threading.Thread.Sleep(50);
                }
                else
                {
                    while (networkStream.DataAvailable)
                    {
                        lastActivity = DateTime.Now;
                        byte[] incomingBuffer = new byte[1024];
                        networkStream.Read(incomingBuffer, 0, 1024);
                        char[] receivedChars = new char[1024];
                        encoder.GetDecoder().GetChars(incomingBuffer, 0, 1024, receivedChars, 0);
                        stringBuilder.Append(receivedChars);
                    }
                }
            }
            string request = stringBuilder.ToString();

            // write response
            string response = "The listener just received: " + request;
            byte[] outgoingBuffer = encoder.GetBytes(response);
            networkStream.Write(outgoingBuffer, 0, outgoingBuffer.Length);
            networkStream.Flush();

            networkStream.Close();
            tcpClient.Close();
        }
    }

}

2 ответов


TcpClient / NetworkStream не получает уведомление, когда соединение закрыто. Единственный доступный вам вариант-поймать исключения при записи в поток.

несколько лет назад мы перешли на использование сокетов вместо tcp-клиента. сокет более удобен по сравнению с tcpclient.

есть несколько методов, которые можно использовать

опрос является одним из их!--1-->

http://msdn.microsoft.com/en-us/library/system.net.sockets.socket.poll.aspx

вы также можете проверить результат записи. это дает вам число фактически записанных байт.

само связанное свойство отражает только состояние в последней операции. В его документации говорится: "значение подключенного свойства отражает состояние соединения на момент последней операции. Если вам нужно определить течение состояние соединения, сделать неблокирующий, нулевой байт отправить вызов. Если вызов возвращается успешно или выдает код ошибки WAEWOULDBLOCK (10035), то сокет все еще подключен; в противном случае сокет больше не подключен."

http://msdn.microsoft.com/en-us/library/system.net.sockets.socket.connected.aspx


networkStream.DataAvailable

это свойство не уведомляет вас, если сокет закрыт на стороне сервера.

в вашей ситуации вы можете реализовать функцию "keepalive", где вы опрашиваете соединение каждые t минут/секунд, или добавить много try/catches всякий раз, когда вы пытаетесь читать или писать из сокета. Когда я использую TCP, я просто включаю метод Reconnect (), который входит в каждый оператор catch.