c#检测tcp断开连接

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15067014/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 13:57:22  来源:igfitidea点击:

c# detecting tcp disconnect

c#timeouttcpclientdisconnect

提问by nl-x

I have two simple applications:

我有两个简单的应用程序:

  • A Server application that waits on a specific tcp port for a client to connect. Then listens to what he says, send back some feedback and DISCONNECT that client.

  • A Form application that connects to the server application, then says something, then wait for the feedback and disconnect from the server, then show the feedback in the form.

  • 在特定的 tcp 端口上等待客户端连接的服务器应用程序。然后听他说的话,发回一些反馈并断开该客户端的连接。

  • 一个连接到服务器应用程序的表单应用程序,然后说些什么,然后等待反馈并与服务器断开连接,然后在表单中显示反馈。

Though the server application seems to behave correctly (I have tested it with Telnet and I see the feedback and I see the disconnect occurring directly after the feedback), the form application however doesn't seem to notice the disconnect from the server. ( TcpClient.Connected seems to stay true even after the server has disconnected )

尽管服务器应用程序似乎行为正确(我已经使用 Telnet 对其进行了测试,并且我看到了反馈,并且看到了反馈后直接发生的断开连接),但是表单应用程序似乎没有注意到与服务器的断开连接。(即使服务器断开连接,TcpClient.Connected 似乎仍然如此)

My question is: why is TcpClient.Connected staying true and how can I know if/when the server has disconnected?

我的问题是:为什么 TcpClient.Connected 保持为真,我如何知道服务器是否/何时断开连接?

Here is my full code:

这是我的完整代码:

Form application:

表格申请:

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 occured\n");
                networkStream.Close();
                tcpClient.Close();
            }
        }

    }
}

Server application:

服务器应用:

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();
        }
    }

}

采纳答案by Hermit Dave

TcpClient / NetworkStream does not get notified when the connection is closed. The only option available to you is to catch exceptions when writing to the stream.

当连接关闭时,TcpClient / NetworkStream 不会得到通知。唯一可用的选项是在写入流时捕获异常。

A few years back we moved to using sockets instead of tcp client. socket is more usable as compared to tcpclient.

几年前,我们开始使用套接字而不是 tcp 客户端。与 tcpclient 相比,socket 更有用。

there are a couple of methods that you can use

有几种方法可以使用

Poll is one of them

民意调查就是其中之一

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

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

You can also do a check on outcome of Write itself. it gives you the number of bytes actually written.

您还可以检查 Write 本身的结果。它为您提供实际写入的字节数。

The Connected property itself only reflects the state at the last operation. Its documentation states "The value of the Connected property reflects the state of the connection as of the most recent operation. If you need to determine the current state of the connection, make a non-blocking, zero-byte Send call. If the call returns successfully or throws a WAEWOULDBLOCK error code (10035), then the socket is still connected; otherwise, the socket is no longer connected."

Connected 属性本身仅反映上次操作的状态。其文档说明“Connected 属性的值反映了截至最近一次操作的连接状态。如果您需要确定连接的当前状态,请进行非阻塞、零字节的 Send 调用。如果调用成功返回或抛出 WAEWOULDBLOCK 错误代码 (10035),则套接字仍处于连接状态;否则,套接字不再连接。”

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

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

回答by chrisevett

networkStream.DataAvailable

This property doesn't notify you if the socket is closed on the server side.

如果套接字在服务器端关闭,此属性不会通知您。

In your situation, you can either implement a "keepalive" function where you poll the connection every t minutes/seconds, or add lots of try/catches whenever you try to read or write from the socket. When I use TCP, i just include a Reconnect() method that goes into every catch statement.

在您的情况下,您可以实现一个“keepalive”功能,您可以每 t 分钟/秒轮询一次连接,或者在您尝试从套接字读取或写入时添加大量尝试/捕获。当我使用 TCP 时,我只在每个 catch 语句中包含一个 Reconnect() 方法。