TcpClient 与服务器通信以保持 C# 中的活动连接?

Posted

技术标签:

【中文标题】TcpClient 与服务器通信以保持 C# 中的活动连接?【英文标题】:TcpClient communication with server to keep alive connection in c#? 【发布时间】:2010-06-25 21:34:44 【问题描述】:

我有这个工作正常的 TcpClient 代码。它连接到 linux 系统上的 perl 服务器并接收服务器发送给它的任何内容。效果很好。

 public static void Main() 
         foreach (ProtocolConnection tcpConnection in TcpConnectionsList) 
                ProtocolConnection connection = tcpConnection;
                ThreadPool.QueueUserWorkItem(_ => 
                                                 ThreadTcpClient(connection);
                                                 ManualResetEventTcp.Set();
                                             );
         
         ... Some code...      
 

 public static void TcpConnect(ProtocolConnection varConnection) 
        int retryCountSeconds = varConnection.RetryEverySeconds*Program.MilisecondsMultiplier;
        int count = 0;
        while (true) 

            try 
                using (var client = new TcpClient(varConnection.IpAddress.ToString(), varConnection.Port)  NoDelay = true )
                using (var stream = client.GetStream()) 
                    var data = new Byte[256];
                    while (!Program.PrepareExit) 
                        Int32 bytes = stream.Read(data, 0, data.Length);
                        string varReadData = Encoding.ASCII.GetString(data, 0, bytes).Trim();
                        if (varReadData != "" && varReadData != "PONG") 
                            VerificationQueue.EnqueueData(varReadData);
                            Logging.AddToLog("[TCP][" + varConnection.Name + "][DATA ARRIVED]" + varReadData);
                         else 
                            Logging.AddToLog("[TCP]" + varReadData);
                        
                    
                
             catch (Exception e) 
                if (e.ToString().Contains("No connection could be made because the target machine actively refused it")) 
                    Logging.AddToLog("[TCP][ERROR] Can't connect to server (" + varConnection.Name + ") " + varConnection.IpAddress + ":" + varConnection.Port );
                 else 
                    Logging.AddToLog(e.ToString());
                

            
            DateTime startTimeFunction = DateTime.Now;
            do 
                Thread.Sleep(1000);
             while (((DateTime.Now - startTimeFunction).TotalSeconds < retryCountSeconds));
        
    

但是在某些情况下我会遇到一些问题:

    我的工作连接经常在空闲时间后断开连接,所以我在服务器中实现了,所以当它收到 PING 时,它会用 PONG 响应。我可以使用 UDP 向服务器发送 PING,它会在 tcp 上使用 PONG 响应,但我更喜欢 tcp 客户端的内置方式,因此它每 60 秒左右发送一次 PING。即使 UDP 解决方案是可以接受的,我在 string varReadData = Encoding.ASCII.GetString(data, 0, bytes).Trim(); 上也没有超时,所以当 PONG 没有到达时,我的客户甚至都不会注意到它。它只是一直在等待......这让我...... 我的另一个问题是,在某些时候string varReadData = Encoding.ASCII.GetString(data, 0, bytes).Trim(); 这一直在等待数据。当服务器崩溃或断开我的客户端时,我什至没有注意到这一点。我希望服务器有某种timeout 或检查连接是否处于活动状态。如果它不活跃,它应该尝试重新连接。

修复此 TcpClient 的最简单方法是什么?我如何实现双向通信以确保如果服务器断开我的连接或我的网络断开连接,客户端会注意到它并重新建立连接?

【问题讨论】:

【参考方案1】:

永远阻塞的不是Encoding.ASCII.GetString(data, 0, bytes).Trim();,而是stream.Read() 如果您正在阅读,您将无法轻松区分服务器(或中间的任何 NAT 网关)断开您的连接,以及服务器根本没有任何东西可以发送给您的情况。至少在 TCP FIN/RST 数据包在失败的情况下无法到达您的客户端的情况下,或者 NAT 网关默默地断开您的连接。

你能做什么;

设置一个 Send/ReceiveTimeout ,并在发生超时时 ping 服务器,或通过 TCP 连接实现您自己的心跳消息。如果您在合理时间内未收到检测信号,请重新建立或采取其他措施。 设置TCP keepalive 选项,并依靠它来告诉您服务器是否已消失。见代码here。

最后一点会告诉你 tcp 连接是否失败,它不会告诉你服务器是否有些失败 - 例如。如果你 CTRL+Z 你的 perl 服务器,它只会在 tcp 窗口关闭时坐在那里不做任何事情,所以如果你需要的话,你可能需要实现自己的 heatbeat 消息来覆盖这种情况。

【讨论】:

嗯,这就是我想要的……通过 TCP 拥有我自己的心跳只是我不知道如何实现它。将测试 ReceiveTimeout 的工作原理,看看它是否像我认为的那样工作。如果它让 stream.Read() 在 50 秒后没有收到数据时放手,那么我可以稍后在代码中添加“发送消息 PING”,因为服务器已经在等待它。如果它收到 PING,它会发送 PONG,所以我会知道它是否在工作。 ReceiveTimeout 的唯一问题是它会抛出异常并断开连接,所以我必须不断地制作新的才能发送心跳。【参考方案2】:

您应该摆脱 UDP 心跳尝试并输入real TCP heartbeat。使用 UDP “Ping”服务器几乎没有任何意义。

您的协议也丢失了message framing。

仔细阅读这两篇链接的文章(尤其是消息框架)。您当前使用的协议确实需要认真修改。

【讨论】:

通过 ping 服务器意味着我在 perl 中有 TCP/UDP 服务器。它同时侦听 UDP 和 TCP。所以我可以将 PING 值发送到 UDP,并且 TCP 服务器在 UDP 上看到 PING 可以用 PONG 发回 TCP,这将保持连接运行。但是你是对的,它不是现在最好的实现。

以上是关于TcpClient 与服务器通信以保持 C# 中的活动连接?的主要内容,如果未能解决你的问题,请参考以下文章

使用 TcpClient 进行通信的更快方式?

Socket网络编程(C#)----TcpListener 与 TcpClient

C# TCP/IP中的客户端和服务器在局域网或外网怎么通信呀?谁有完整的例子呀.

将 TcpClient ReceiveTimeout 与同步程序一起使用

C# TcpClient,同时读写流

c# TcpClient简易聊天工具