连接后如何测试TCPClient连接断开?

mj8*_*j82 10 c# tcp visual-studio-2010

我一直在争论一个问题整整3天我找不​​到任何解决方案,请帮助:)
我使用Visual Studio 2010和C#语言.

我有一台像服务器一样工作的设备,它在非常不规则的时间段内发送一些数据(不可能定义任何读取超时).
我写了一个TCP客户端来连接到该服务器并读取数据.它工作正常,但是当网络出现问题并且服务器变得不可用时(例如,当我从计算机中拔出网线时),应用程序需要大约10秒才能"注意到"没有连接到服务器并且丢弃一个例外.(我不知道为什么到了10秒?它在哪里定义?我可以改变它吗?)
我想要更快地做出反应 - 让我们说连接断开后一秒钟.
然而谷歌搜索答案并没有为我提供任何有效的解决方案.

测试代码如下,我尝试在2个线程上进行:一个是读取数据,第二个是查找连接状态,当它被破坏时应该报警.它既不适用于TCPClient也不适用于Socket类.我曾尝试用tcpClient.SendTimeout = 100;和读取/写入一些数据,stream.WriteTimeout = 100;但它似乎不起作用.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Threading;

namespace TCPClient
{
    class Program
    {
        static volatile bool _continue = true;
        static TcpClient tcpClient;
        static NetworkStream stream;

        static void Main(string[] args)
        {
            try
            {
                //client thread - listen from server
                Thread tcpListenThread = new Thread(TcpListenThread);
                tcpListenThread.Start();

                //connection checking thread
                Thread keepAliveThread = new Thread(KeepAliveThread);
                keepAliveThread.Start();

                while (_continue)
                {
                    if (Console.ReadLine() == "q")
                    {
                        _continue = false;
                    }
                }

                keepAliveThread.Join(2000);
                if (keepAliveThread.IsAlive)
                {
                    Console.WriteLine("Thread keepAlive has been aborted...");
                    keepAliveThread.Abort();
                }

                tcpListenThread.Join(2000);
                if (tcpListenThread.IsAlive)
                {
                    Console.WriteLine("Listen thread has been aborted...");
                    tcpListenThread.Abort();
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("\n" + ex.Message);
            }

            Console.WriteLine("\nHit any key to quit...");
            Console.Read();
        }

        private static void TcpListenThread()
        {
            string server = "172.20.30.40";
            int port = 3000;

            try
            {
                using (tcpClient = new TcpClient())
                {
                    tcpClient.Connect(server, port);

                    if (tcpClient.Connected)
                        Console.WriteLine("Successfully connected to server");

                    using (stream = tcpClient.GetStream())
                    {

                        while (_continue)
                        {
                            Byte[] data = new Byte[1024];
                            Int32 bytes = stream.Read(data, 0, data.Length);
                            string responseData = System.Text.Encoding.ASCII.GetString(data, 0, bytes);

                            Console.WriteLine("Received: {0}, responseData);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Listen thread exception! " + ex.Message);
            }
        }


        private static void KeepAliveThread()
        {
            while (_continue)
            {
                if (tcpClient != null)
                {
                    try
                    {
                        //...what to put here to check or throw exception when server is not available??...
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine("Disconnected...");
                    }
                }

                Thread.Sleep(1000);  //test for connection every 1000ms
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑:
@ carsten的答案:虽然它看起来很有前途,但这个解决方案不起作用......
我为此制作了最简单的测试应用程序:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Threading;

namespace TCPClientTest
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                string server = "172.20.30.40";
                int port = 3000;

                using (TcpClient tcpClient = new TcpClient())
                {
                    tcpClient.Connect(server, port);

                    int i = 0;
                    while (true)
                    {
                        // This is how you can determine whether a socket is still connected.
                        bool blockingState = tcpClient.Client.Blocking;
                        try
                        {
                            byte[] tmp = new byte[1];

                            tcpClient.Client.Blocking = false;
                            tcpClient.Client.Send(tmp, 0, 0);
                            Console.WriteLine("Connected!");
                        }
                        catch (SocketException e)
                        {
                            // 10035 == WSAEWOULDBLOCK
                            if (e.NativeErrorCode.Equals(10035))
                                Console.WriteLine("Still Connected, but the Send would block");
                            else
                            {
                                Console.WriteLine("Disconnected: error code {0}!", e.NativeErrorCode);
                            }
                        }
                        finally
                        {
                            tcpClient.Client.Blocking = blockingState;
                        }

                        Console.WriteLine("Connected: {0} ({1})", tcpClient.Client.Connected, i++);

                        Thread.Sleep(1000);
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Global exception: {0}", ex.Message);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

结果如下,显示:

连接的!
连接:真的

加上我的订单号每一秒.当我断开网络电缆时,开始打印需要8秒钟:

Disonnected:错误代码10054!
连接:错误

所以到8秒我不知道连接丢失了.看起来ping是最好的选择,但我会测试另一种解决方案.

Car*_*ten 17

我认为这是一个经常出现的问题.这可能就是为什么MSDN文档确实给出了一个很好的答案 - 请参阅Socket.Connected

从那里引用:

Connected属性获取最后一次I/O操作时Socket的连接状态.当它返回false时,Socket从未连接,或者不再连接.

Connected属性的值反映了最近操作时的连接状态.如果需要确定连接的当前状态,请进行非阻塞,零字节发送调用.如果调用成功返回或抛出WAEWOULDBLOCK错误代码(10035),则套接字仍然连接; 否则,套接字不再连接.

使用此示例代码:

// .Connect throws an exception if unsuccessful
client.Connect(anEndPoint);

// This is how you can determine whether a socket is still connected.
bool blockingState = client.Blocking;
try
{
    byte [] tmp = new byte[1];

    client.Blocking = false;
    client.Send(tmp, 0, 0);
    Console.WriteLine("Connected!");
}
catch (SocketException e) 
{
    // 10035 == WSAEWOULDBLOCK
    if (e.NativeErrorCode.Equals(10035))
        Console.WriteLine("Still Connected, but the Send would block");
    else
    {
        Console.WriteLine("Disconnected: error code {0}!", e.NativeErrorCode);
    }
}
finally
{
    client.Blocking = blockingState;
}

 Console.WriteLine("Connected: {0}", client.Connected);
Run Code Online (Sandbox Code Playgroud)

以及扩展方法的直接动机:

public static bool IsConnected(this Socket client)
{
   // This is how you can determine whether a socket is still connected.
   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;
   }
}
Run Code Online (Sandbox Code Playgroud)


Dav*_*vid 7

这是一个非常古老的主题,但它是我搜索这个问题时出现的第一个SO帖子,我在其他地方找到了一个更有用的解决方案,所以我想我会发布一个链接来帮助其他人:

https://social.msdn.microsoft.com/Forums/en-US/c857cad5-2eb6-4b6c-b0b5-7f4ce320c5cd/c-how-to-determine-if-a-tcpclient-has-been-disconnected?forum= netfxnetcom&教授=所需

ElFenix发布了这个对我有用的答案:

// Detect if client disconnected
if (tcp.Client.Poll(0, SelectMode.SelectRead))
{
  byte[] buff = new byte[1];
  if (tcp.Client.Receive(buff, SocketFlags.Peek) == 0)
  {
    // Client disconnected
    bClosed = true;
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 有趣的是,这仍然是我们 20 年前在 C 中所做的(或者对长辈来说更多) (2认同)

Alx*_*ndr 6

简单回答.你不能.Tcp的制作方式不允许这样做.但是,实现此目的的正常方法是以比消息更短的间隔发送ping.因此,比方说,每当您从服务器收到消息时,您启动一​​个倒计时1分钟的时钟,然后发送"ping"命令(如果您之间没有收到新消息).如果您在30秒内未收到对"ping"的响应,则表明连接已断开.

从理论上讲,你应该能够在包级别上执行此操作(tcp发送你应该能够检查的ACK),但是我不知道这是否可能在C#或任何编程语言中,或者如果你需要在固件/硬件中做到这一点.