繁体   English   中英

TCP 连接卡在 CLOSE_WAIT 状态

[英]TCP connections stuck with CLOSE_WAIT state

我需要你们的帮助来解决非关闭 TCP 连接的问题。

基本上它工作正常,但几分钟后它卡在 CLOSE_WAIT 状态连接。

在此处输入图片说明

代码逻辑:

代码接受第一个数据包并解析它,然后将 CRC 发送回客户端。 如果 CRC 有效,则客户端将主数据包发送到服务器并重复它。 一旦没有数据包,则客户端关闭连接,但有时不会。 在这种情况下,服务器(下面的代码)在通信 1 分钟后关闭连接。

我认为它也应该对应于https://www.googlecloudcommunity.com/gc/Cloud-Product-Articles/TCP-states-explained/ta-p/78462

这是 C# 代码

void TCPListenerServer(object obj) {
  CancellationToken token = (CancellationToken) obj;
  if (token.IsCancellationRequested) {
    isDisposed = true;
    if (listener != null) {
      listener.Stop();
    }

    return;
  } else {
    try {
      isDisposed = false;

      try {
        var validIP = IPAddress.Parse(Properties.Settings.Default.ServerIP);
        listener = new TcpListener(validIP, Properties.Settings.Default.ServerPort);
        listener.Start();

        while (isDisposed == false || token.IsCancellationRequested == false) {
          if (token.IsCancellationRequested || isDisposed) {
            break;
          } else {

            if (!listener.Pending()) {
              Thread.Sleep(50);
              continue;
            }

            listener.Server.ReceiveTimeout = 10000;
            listener.Server.LingerState = new LingerOption(true, 0);

            var client = listener.AcceptTcpClient();

            var arrThread = new ThreadParams() {
              Client = client, Token = m_Cts.Token
            };
            var t = new Thread(ProcessClientRequests) {
              IsBackground = true,
                Name = "ClientConnectionThread",
            };
            clientThreads.Add(t);

            t.Start((object) arrThread);
          }
        }
      } catch (SocketException ex) {
        if (ex.SocketErrorCode == SocketError.Interrupted) {}
      } catch (Exception ex) {} finally {
        if (listener != null) {
          listener.Server.Close();
          listener.Stop();
        }
      }
    } catch (Exception ex) {

    }
  }
}


    private void ProcessClientRequests(object argument) {
      
      TcpClient client = ((ThreadParams) argument).Client;
      CancellationToken token = ((ThreadParams) argument).Token;
      client.SendTimeout = 10000;
      client.ReceiveTimeout = 10000;
      client.LingerState = new LingerOption(true, 0);
    
      var bufferSize = 1024;
      byte[] buffer = new byte[bufferSize];
      var isFirstPacket = true;
      var startTime = DateTime.Now;
      DateTime endTime = DateTime.Now;
    
      try { 
    
        using(NetworkStream stream = client.GetStream()) {
          do {
            Thread.Sleep(20);
          } while (!stream.DataAvailable);
    
          while ((client != null && client.Connected) && stream != null && stream.CanRead && (endTime - startTime).TotalMinutes < 1) {
            if (client == null) {
              break;
            }
    
            do {
              if (token.IsCancellationRequested) {
                return;
              }
    
              if (client == null) {
                break;
              }
    
              endTime = DateTime.Now;
    
              int streamReadBytes = 0;
              streamReadBytes = stream.Read(buffer, 0, buffer.Length);
    
              if (streamReadBytes == 0) {
                if (client != null) {
                  client.Close();
                }
                break;
              }
    
              if (buffer[0] == (byte) GalileoskyPacketHeaderEnums.FirstPacket || buffer[0] == (byte) GalileoskyPacketHeaderEnums.MainPacket) {
    
                var parserGalileosky = new Galileosky();
    
                var packetResult = parserGalileosky.ParsePacket(buffer, isFirstPacket);
                if (packetResult == null) {
                  if (client != null) {
                    client.Close();
                    client = null;
                  }
    
                  break;
                }
    
                if (packetResult.Errors.Any()) {
                  if (client != null) {
                    client.Close();
                    client = null;
                  }
                } else {
                  var imei = packetResult.Packet.IMEI;
    
                  if (isFirstPacket) {
                    isFirstPacket = false;
    
                    if (stream.CanWrite == true && packetResult.Packet.IsCrc) {
                      var answerPacket = packetResult.Packet.GetConfirmPacket();
                      stream.Write(answerPacket.Ready);
                    } else {
                      if (client != null) {
                        client.Close();
                        client = null;
                      }
                    }
                  } else // The Main Packet processing
                  {
                    // ... Some code to send the main packet to the app queue

                    if (stream.CanWrite == true && !packetResult.Errors.Any() && packetResult.Packet.IsCrc) {
                      var answerPacket = packetResult.Packet.GetConfirmPacket();
                      stream.Write(answerPacket.Ready);
                    }
    
                    if (packetResult.Packet.IsExtraData == false) {
                      if (client != null) {
                        client.Close();
                        client = null;
                        break;
                      }
                    }
    
                  }
                }
              } else {
                if (client != null) {
                  client.Close();
                  client = null;
                }
              }
              if ((endTime - startTime).TotalMinutes > 1) {
                if (client != null) {
                  client.Close();
                  client = null;
                  break;
                }
              }
    
            }
            while ((client != null && client.Connected) && stream != null && stream.CanRead && stream.DataAvailable && (endTime - startTime).TotalMinutes < 1);
          }
        }
    
        if (client != null) {
          client.Close();
          client = null;
        }
      } catch (Exception ex) {} finally {
        if (client != null) {
          client.Close();
          client = null;
        }
      }
    }

即使您的代码是正确的,您也可能会遇到“2 位将军”问题。 没有完美的算法可以让两方在可能出现数据包丢失的情况下同意关闭连接。

优雅地关闭 TCP 流需要双方关闭发送,读取数据直到 EOF,然后关闭。 我相信你会在 CLOSE_WAIT 中看到一个套接字,如果一方关闭了连接,但另一方没有。 看起来您已将套接字设置为linger 在这种情况下,操作系统将为您接管套接字的生命周期。

如果您的套接字未设置为延迟,那么提前关闭套接字将导致您认为已发送的数据丢失。

我还应该指出,您的代码过于复杂,错误处理无处不在。 看起来它可能是在 C# 引入 async/await 之前编写的。

您似乎还假设一次读取操作等同于一个数据包。 但这是一个 TCP 流。 操作系统可以将从一端写入的数据分段并重新组合成在另一端读取的任意数量的数据。

我建议您在互联网上搜索使用.AcceptTcpClientAsync.ReadAsync.WriteAsync等的.WriteAsync

最后,我找不到解决问题的方法来关闭我的代码中的连接。

但是我添加了一个关闭连接的timer ,它的工作原理很棒!

private void ProcessClientRequests(object argument) 

 // ...  The same code of my quetion

 Timer timerCloseConn = new(new TimerCallback((e) =>
                            {

                                if (stream != null)
                                {
                                    stream.Close();
                                    stream.Dispose();
                                }
                                if (client != null)
                                {

                                    if (client.Client.Connected)
                                    {
                                        client.Client.Shutdown(SocketShutdown.Both);
                                    }

                                    client.Close();
                                    client.Dispose();
                                    client = null;
                                }

                                Logger.Info("The connection has been closed by 
 timer's rule!");
                            }), null, 60000, Timeout.Infinite);

  // ... The same code of my quetion
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM