C# 如何在连接TCPClient后测试其断开的连接?

C# 如何在连接TCPClient后测试其断开的连接?,c#,visual-studio-2010,tcp,C#,Visual Studio 2010,Tcp,我已经为一个问题奋斗了整整3天,我找不到任何解决方案,请帮助:) 我使用Visual Studio 2010和C语言 我有一个像服务器一样工作的设备,它在非常不规则的时间段内发送一些数据(不可能定义任何读取超时)。 我编写了一个TCP客户端来连接到该服务器并读取数据。它工作正常,但当网络出现问题且服务器不可用时(例如,当我从计算机上拔出网络电缆时),应用程序“注意”到服务器没有连接并引发异常大约需要10秒钟。(我不知道为什么精确到10秒?定义在哪里?我可以更改它吗? 我希望反应更快—比如说在连接

我已经为一个问题奋斗了整整3天,我找不到任何解决方案,请帮助:)
我使用Visual Studio 2010和C语言

我有一个像服务器一样工作的设备,它在非常不规则的时间段内发送一些数据(不可能定义任何读取超时)。
我编写了一个TCP客户端来连接到该服务器并读取数据。它工作正常,但当网络出现问题且服务器不可用时(例如,当我从计算机上拔出网络电缆时),应用程序“注意”到服务器没有连接并引发异常大约需要10秒钟。(我不知道为什么精确到10秒?定义在哪里?我可以更改它吗?
我希望反应更快—比如说在连接断开一秒钟后。
然而,谷歌搜索答案并不能为我提供任何有效的解决方案

测试代码如下,我试着在两个线程上运行:一个线程正在读取数据,第二个线程正在查找连接状态,当连接断开时应该会向我发出警报。它既不适用于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
            }
        }
    }
}
编辑:
@卡斯滕的回答:虽然看起来很有希望,但这个解决方案不起作用……
我为此制作了最简单的测试应用程序:

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);
            }
        }
    }
}
结果如下所示,它显示:

已连接
已连接:正确

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

断开连接:错误代码10054
已连接:错误


所以到了8秒钟,我还没有意识到连接断开了。看起来ping是这里最好的选择,但我将测试其他解决方案。

简单答案。你不能。Tcp是以一种不允许这种情况的方式生成的。然而,实现这一点的正常方法是以比消息更短的间隔发送ping。比如说,每当你从服务器收到一条消息时,你启动一个倒计时1分钟的时钟,然后你发送一个“ping”命令(如果你在这期间没有收到新消息)。如果在30秒内没有收到对“ping”的响应,则表明连接已断开


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

在oginal unix Implementation中有一个名为“SO_KEEPALIVE”的套接字选项,它让TCP协议偶尔发送探测,以确保管道的另一端仍在侦听。它可以与通过Socket.IOControl设置探头的频率一起设置。我认为这是一个经常出现的问题。这可能就是为什么MSDN文档确实给出了一个很好的答案-请参阅

引述:

Connected属性获取截至的套接字的连接状态 最后一次I/O操作。当它返回false时,套接字为 从未连接,或不再连接

关联财产的价值反映了资产的状态 从最近的操作开始的连接。如果你需要确定 连接的当前状态,生成一个非阻塞的零字节 打电话。如果调用成功返回或抛出WaeWoldBlock 错误代码(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);
以及扩展方法的直接主题化:

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

如果您键入以下内容,它将是正确且有效的:

byte[] tmp = new byte[] {0};
...             
client.Send(tmp, 1, 0);
...

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

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

以防其他人需要简单有效的东西

这是我想出的密码

            while (true)
            {

                string s = null;
                DateTime readLag = DateTime.Now;
                try
                {
                    s = streamIn.ReadLine();
                }
                catch (Exception ex)
                {
                    SocketException sockEx = ex.InnerException as SocketException;
                    if (sockEx != null)
                    {
                        OnDebug("(" + sockEx.NativeErrorCode + ") Exception = " + ex);
                    }
                    else
                    {
                        OnDebug("Not SockEx :" + ex);
                    }
                }



                if (enableLog) OnDebug(s);
                if (s == null || s == "")
                {
                    if (readLag.AddSeconds(.9) > DateTime.Now)
                    {
                        break;
                    }
                }
                else
                {
                    CommandParser(s);
                }
            }
每次读取失败时,我得到的是本机错误10035,但会被阻止

当连接被破坏时,我立即得到了一个没有数据的返回

将readLag.AddSeconds()设置为刚好低于读取超时,这会让我非常清楚,时间从未流逝,而且我没有得到任何数据。这是不应该发生的

这个标准一出现,我就退出循环,线程就结束了


希望这能对其他人有所帮助。

可能的重复:嗯,KEEPALIVEs充其量也只是一个片片。它们有时会造成比它们解决的问题更多的问题-我只是不记得为什么或者源代码是什么-但是要小心使用。如果我读对了,这是否意味着(基本上)通过执行
Send(新字节[0])来
Send方法等待底层的
ACK
作为对发送的包的响应到达?@Carsten König:谢谢您的回答。看起来很有希望,但恐怕不行。我已经编辑了我的帖子来解释这个解决方案的问题。谢谢你的回答。到目前为止,ping似乎是最好的选择,但是当我挖掘答案时,我发现一些页面指出它是一个糟糕的解决方案-不知道为什么。我认为你没有理解一些东西,或者我(这很可能是btw xD)。据我所知,你刚刚更新的帖子是ping,只是用了一种比自己更聪明的方式。另外,8秒钟直到你发现一个断开连接听起来一点也不糟糕。例如,IRC服务器在ping(!)之间最多使用5分钟。我更新了我的帖子,表明Carsten König提出的解决方案不起作用。它试图每秒执行
tcpClient.Client.Send(…)
,所以我一直希望在断开电缆连接后,它会抛出异常或其他异常