Search code examples
c#tcptcpclienttcplistener

TCP connections stuck with CLOSE_WAIT state


I need your help guys in order to fix issue with non closing TCP connection.

Basically it works fine but after a few minutes it stucks with connection in CLOSE_WAIT state.

enter image description here

The logic of the code:

The code accepts first packet and parse it and after that it sends the CRC back to the client. If the CRC valid then client sends the main packet to the server and repeat it. Once if there are no packets then client close connection but sometimes it does not. In this case server (code below) close connection after 1 minute of the communications.

I assume it should correspond to https://www.googlecloudcommunity.com/gc/Cloud-Product-Articles/TCP-states-explained/ta-p/78462 as well

Here is C# code

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

Solution

  • Finally I could'n find a way to solve problem to close connection within my code.

    But I added a timer that close connection and it works just awesome!

    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
    }