如何检查 TcpClient 连接是不是关闭?

Posted

技术标签:

【中文标题】如何检查 TcpClient 连接是不是关闭?【英文标题】:How to check if TcpClient Connection is closed?如何检查 TcpClient 连接是否关闭? 【发布时间】:2010-11-26 03:00:49 【问题描述】:

我正在玩弄 TcpClient,并试图弄清楚如何在连接断开时使 Connected 属性为 false。

我试过了

NetworkStream ns = client.GetStream();
ns.Write(new byte[1], 0, 0);

但如果 TcpClient 断开连接,它仍然不会显示。你会如何使用 TcpClient 来解决这个问题?

【问题讨论】:

【参考方案1】:

我不建议您尝试仅为测试套接字而编写。也不要在 .NET 的 Connected 属性上进行中继。

如果想知道远程端点是否还处于活动状态,可以使用TcpConnectionInformation:

TcpClient client = new TcpClient(host, port);

IPGlobalProperties ipProperties = IPGlobalProperties.GetIPGlobalProperties();
TcpConnectionInformation[] tcpConnections = ipProperties.GetActiveTcpConnections().Where(x => x.LocalEndPoint.Equals(client.Client.LocalEndPoint) && x.RemoteEndPoint.Equals(client.Client.RemoteEndPoint)).ToArray();

if (tcpConnections != null && tcpConnections.Length > 0)

    TcpState stateOfConnection = tcpConnections.First().State;
    if (stateOfConnection == TcpState.Established)
    
        // Connection is OK
    
    else 
    
        // No active tcp Connection to hostName:port
    


client.Close();

另请参阅:MSDN 上的TcpConnectionInformation MSDN 上的IPGlobalProperties TcpState 状态的描述 Wikipedia 上的网络统计


这里是作为 TcpClient 的扩展方法。

public static TcpState GetState(this TcpClient tcpClient)

  var foo = IPGlobalProperties.GetIPGlobalProperties()
    .GetActiveTcpConnections()
    .SingleOrDefault(x => x.LocalEndPoint.Equals(tcpClient.Client.LocalEndPoint));
  return foo != null ? foo.State : TcpState.Unknown;

【讨论】:

这是一个很棒的答案。您可以改进它的唯一方法是将测试呈现为 Socket 的扩展方法,该方法返回套接字状态。 不错的一个。我真的想知道是否有更快的方法来做到这一点 谢谢!你的回答是第一个对我有用的。我尝试了各种使用 client.Client.Poll 的答案,但它们都不起作用。 使用FirstOrDefault 而不是SingleOrDefault 换行,.SingleOrDefault(x => x.LocalEndPoint.Equals(tcpClient.Client.LocalEndPoint) && x.RemoteEndPoint.Equals(tcpClient.Client.RemoteEndPoint));【参考方案2】:

据我所知/记得,除了读取或写入之外,没有其他方法可以测试套接字是否已连接。

我根本没有使用过 TcpClient,但如果远程端已正常关闭,Socket 类将从对 Read 的调用中返回 0。 如果远程端没有正常关闭[我认为]你得到一个超时异常,不记得抱歉类型。

使用像 'if(socket.Connected) socket.Write(...) 这样的代码会产生竞争条件。最好只调用 socket.Write 并处理异常和/或断开连接。

【讨论】:

是的。套接字层应使用异常进行管理。抛出的 IOException 将内部异常设置为 SocketException,其中包含远程检测超时或关闭套接字所需的所有信息。【参考方案3】:

Peter Wone 和 uriel 的解决方案非常好。但是您还需要检查远程端点,因为您可以与本地端点建立多个打开的连接。

    public static TcpState GetState(this TcpClient tcpClient)
    
        var foo = IPGlobalProperties.GetIPGlobalProperties()
          .GetActiveTcpConnections()
          .SingleOrDefault(x => x.LocalEndPoint.Equals(tcpClient.Client.LocalEndPoint)
                             && x.RemoteEndPoint.Equals(tcpClient.Client.RemoteEndPoint)
          );

        return foo != null ? foo.State : TcpState.Unknown;
    

【讨论】:

【参考方案4】:

我已经创建了这个函数并为我检查客户端是否仍然与服务器连接。

/// <summary>
/// THIS FUNCTION WILL CHECK IF CLIENT IS STILL CONNECTED WITH SERVER.
/// </summary>
/// <returns>FALSE IF NOT CONNECTED ELSE TRUE</returns>
public bool isClientConnected()

    IPGlobalProperties ipProperties = IPGlobalProperties.GetIPGlobalProperties();

    TcpConnectionInformation[] tcpConnections = ipProperties.GetActiveTcpConnections();

    foreach (TcpConnectionInformation c in tcpConnections)
    
        TcpState stateOfConnection = c.State;

        if (c.LocalEndPoint.Equals(ClientSocket.Client.LocalEndPoint) && c.RemoteEndPoint.Equals(ClientSocket.Client.RemoteEndPoint))
        
            if (stateOfConnection == TcpState.Established)
            
                return true;
            
            else
            
                return false;
            

        

    

    return false;



【讨论】:

【参考方案5】:

@uriel 的回答对我很有用,但我需要在 C++/CLI 中对其进行编码,这并不完全是微不足道的。这是(大致等效的)C++/CLI 代码,添加了一些稳健性检查以进行良好的衡量。

using namespace System::Net::Sockets;
using namespace System::Net::NetworkInformation;

TcpState GetTcpConnectionState(TcpClient ^ tcpClient)

    TcpState tcpState = TcpState::Unknown;

    if (tcpClient != nullptr)
    
        // Get all active TCP connections
        IPGlobalProperties ^ ipProperties = IPGlobalProperties::GetIPGlobalProperties();
        array<TcpConnectionInformation^> ^ tcpConnections = ipProperties->GetActiveTcpConnections();

        if ((tcpConnections != nullptr) && (tcpConnections->Length > 0))
        
            // Get the end points of the TCP connection in question
            EndPoint ^ localEndPoint = tcpClient->Client->LocalEndPoint;
            EndPoint ^ remoteEndPoint = tcpClient->Client->RemoteEndPoint;

            // Run through all active TCP connections to locate TCP connection in question
            for (int i = 0; i < tcpConnections->Length; i++)
            
                if ((tcpConnections[i]->LocalEndPoint->Equals(localEndPoint)) && (tcpConnections[i]->RemoteEndPoint->Equals(remoteEndPoint)))
                
                    // Found active TCP connection in question
                    tcpState = tcpConnections[i]->State;
                    break;
                
            
        
    

    return tcpState;


bool TcpConnected(TcpClient ^ tcpClient)

    bool bTcpConnected = false;

    if (tcpClient != nullptr)
    
        if (GetTcpConnectionState(tcpClient) == TcpState::Established)
        
            bTcpConnected = true;
        
    
    return bTcpConnected;

希望这会对某人有所帮助。

【讨论】:

【参考方案6】:

截至 2019 年,在跨平台和异步环境中,我使用以下代码持续检查 TCP 通道是否打开。此检查触发例如如果我的 Windows 机器上的以太网电缆被拔出,或者我的 android 设备上的 Wifi 被禁用。

private async Task TestConnectionLoop()

    byte[] buffer = new byte[1];
    ArraySegment<byte> arraySegment = new ArraySegment<byte>(buffer, 0, 0);
    SocketFlags flags = SocketFlags.None;

    while (!_cancellationSource.Token.IsCancellationRequested)
    
        try
        
            await _soc.SendAsync(arraySegment, flags);
            await Task.Delay(500);
        
        catch (Exception e)
        
            _cancellationSource.Cancel();

            // Others can listen to the Cancellation Token or you 
            // can do other actions here
        
    

【讨论】:

【参考方案7】:

请注意,我发现 System.Net.Sockets.TcpClientGSF.Communication 包装器很有帮助,因为它有一个 CurrentState 属性来指示套接字是打开/连接还是关闭/断开连接。您可以在此处找到有关 NuGet 包的详细信息:

https://github.com/GridProtectionAlliance/gsf

下面是如何设置一个简单的 TCP 套接字并测试它是否已连接:

    GSF.Communication.TcpClient tcpClient;

    void TestTcpConnectivity() 
    
        tcpClient = new GSF.Communication.TcpClient();
        string myTCPServer = "localhost";
        string myTCPport = "8080";
        tcpClient.MaxConnectionAttempts = 5;
        tcpClient.ConnectionAttempt += s_client_ConnectionAttempt;
        tcpClient.ReceiveDataComplete += s_client_ReceiveDataComplete;
        tcpClient.ConnectionException += s_client_ConnectionException;
        tcpClient.ConnectionEstablished += s_client_ConnectionEstablished;
        tcpClient.ConnectionTerminated += s_client_ConnectionTerminated;
        
        tcpClient.ConnectionString = "Server=" + myTCPServer + ":" + myTCPport;
        tcpClient.Initialize();
        tcpClient.Connect();        

        Thread.Sleep(250);
        
        if (tcpClient.CurrentState == ClientState.Connected)
        
            Debug.WriteLine("Socket is connected");
            // Do more stuff 
         
        else if (tcpClient.CurrentState == ClientState.Disconnected)
        
            Debug.WriteLine(@"Socket didn't connect");
            // Do other stuff or try again to connect 
        
    
    
    void s_client_ConnectionAttempt(object sender, EventArgs e)
    
        Debug.WriteLine("Client is connecting to server.");
    

    void s_client_ConnectionException(object sender, EventArgs e)
    
        Debug.WriteLine("Client exception - 0.", e.Argument.Message);
    

    void s_client_ConnectionEstablished(object sender, EventArgs e)
    
        Debug.WriteLine("Client connected to server.");
    

    void s_client_ConnectionTerminated(object sender, EventArgs e)
    
        Debug.WriteLine("Client disconnected from server.");
    

    void s_client_ReceiveDataComplete(object sender, GSF.EventArgs<byte[], int> e)
    
        Debug.WriteLine(string.Format("Received data - 0.", tcpClient.TextEncoding.GetString(e.Argument1, 0, e.Argument2)));
           

【讨论】:

【参考方案8】:

试试这个,它对我有用

private void timer1_Tick(object sender, EventArgs e)
    
        if (client.Client.Poll(0, SelectMode.SelectRead))
            
                if (!client.Connected) sConnected = false;
                else
                
                    byte[] b = new byte[1];
                    try
                    
                        if (client.Client.Receive(b, SocketFlags.Peek) == 0)
                        
                            // Client disconnected
                            sConnected = false;
                        
                    
                    catch  sConnected = false; 
                
            
        if (!sConnected)
        
          //--Basically what you want to do afterwards
            timer1.Stop();
            client.Close();
            ReConnect();
        

    

我使用 Timer 是因为我想定期检查连接状态 而不是在监听代码的循环中[我觉得它减慢了发送-接收过程]

【讨论】:

偶然发现了这个答案,只是好奇为什么它被否决了?【参考方案9】:

就我而言,我正在向服务器发送一些命令(在同一台计算机上的虚拟机中运行)并等待响应。但是,如果服务器在等待时意外停止,我没有收到任何通知。我尝试了其他海报提出的可能性,但都没有奏效(它总是说服务器仍然连接)。对我来说,唯一有效的是将 0 字节写入流:

var client = new TcpClient();
//... open the client

var stream = client.GetStream();

//... send something to the client

byte[] empty =  0 ;
//wait for response from server
while (client.Available == 0)

    //throws a SocketException if the connection is closed by the server
    stream.Write(empty, 0, 0);
    Thread.Sleep(10);

【讨论】:

以上是关于如何检查 TcpClient 连接是不是关闭?的主要内容,如果未能解决你的问题,请参考以下文章

超时连接后如何关闭TCP连接?

TcpClient 是单个连接使用吗?如何发送第二条消息?

关闭 TcpListener 和 TcpClient 连接的正确顺序(哪一侧应该是主动关闭)

TcpClient 发送/关闭问题

如何设置 TcpClient 的超时时间?

C# TcpClient 不会注册断开连接