5

我有这个工作正常的 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));
        }
    }

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

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

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

4

2 回答 2

7

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

你可以做什么;

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

最后一点会告诉你 tcp 连接是否失败,它不会告诉你服务器是否发生了一些故障 - 例如,如果你 CTRL+Z 你的 perl 服务器,它会在 tcp 窗口关闭时坐在那里不做任何事情,因此,如果需要,您可能还需要实现自己的 heatbeat 消息来涵盖这种情况。

于 2010-06-25T22:29:09.303 回答
3

您应该摆脱 UDP 心跳尝试并放入真正的 TCP 心跳。使用 UDP “ping”服务器几乎没有意义。

您的协议也缺少消息框架

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

于 2010-06-26T03:22:16.213 回答