Connection Closed Gracefully on IOHandler.ReadLn Solution at IdTcpClient

172 Views Asked by At

I have a small problem, when connect to server TIdTcpClient generate "Connection Closed Gracefully" error on FConn.IOHandler.ReadLn of procedure TReadingThread.Execute;, that don't happen all time, just some, and if try again and again it don't happen, also don't happen at another VPS where server run. but that's ok.

I did few changes who work arount the error and if error happens it basically disconnect, terminate threads and try again. my question is, what i did can cause some problem or works fine? on tests solved, it failed 8 times and at 9 attempt it connected.

What i did:

procedure TReadingThread.Execute;
var
  Recebeu   : String;
begin
  while not Terminated do
  begin
    try
      Recebeu := FConn.IOHandler.ReadLn;
    except
      on E: Exception do
      begin
        THD.Terminate;

        FConn.IOHandler.InputBuffer.Clear;
        FConn.Disconnect(False);

        if rt <> nil then rt.Terminate;

        THD := TThreadCon.Create;
        THD.Start;
      end;
    end;

    if not Recebeu.IsEmpty then
    begin

THD is THD : TThreadCon = nil

procedure TThreadCon.Execute;
var
  Attempt : Integer;
begin
  Attempt   := 0;

  try
    while not Terminated do
    begin
      if TCPClient.IOHandler <> nil then TCPClient.IOHandler.InputBuffer.Clear;

      try
        Inc(Attempt);

        TCPClient.Connect;

        try
          TCPClient.IOHandler.WriteLn('Connect');
        except
          TCPClient.Disconnect(False);
          raise;
        end;
      except
        on E: Exception do
        begin
          if (Attempt >= 3) then
          begin
            THD.Terminate;
          end;

          if FTermEvent.WaitFor(2500) <> wrTimeout then Exit;
          Continue;
        end;
      end;

      rt := TReadingThread.Create(TCPClient);
      try
        try
          while not Terminated do
          begin
            if FTermEvent.WaitFor(5000) <> wrTimeout then Exit;
          end;
        except
        end;
      finally
        rt.Terminate;
        try
          TCPClient.Disconnect(False);
        finally
          rt.WaitFor;
          rt.Free;
        end;
      end;
    end;
  finally
    TCPClient.Free;
  end;
end;

As i see, since i declare and Set Attempt at Execute, it never will trigger if (Attempt >= 3) thenon on E: Exception do but that's ok.

Connection Closed Gracefully error happens only if Server closes the connection?

1

There are 1 best solutions below

11
Remy Lebeau On

"Connection Closed Gracefully" means the connection was closed intentionally by a remote peer, typically the server yes, but it could also happen if a firewall/router sitting between the client and server closed the connection due to inactivity, etc.

The error is normal on the client side, and your solution is basically correct - close the connection and reconnect.

Although, I do question why you are calling Connect() and ReadLn() in separate threads. That is unusual. Why not call them in a single thread instead? You can have an outer loop that calls Connect() and WriteLn(), and then an inner loop that calls ReadLn(), eg:

procedure TThreadCon.Execute;
var
  Attempt, ConnectDelay : Integer;
  Recebeu : String;
begin
  Attempt := 0;
  ConnectDelay := 0;

  try
    while (not Terminated) and
          (FTermEvent.WaitFor(ConnectDelay) = wrTimeout) do
    begin
      if TCPClient.IOHandler <> nil then
        TCPClient.IOHandler.InputBuffer.Clear;

      try
        Inc(Attempt);

        TCPClient.Connect;

        try
          TCPClient.IOHandler.WriteLn('Connect');
        except
          TCPClient.Disconnect(False);
          raise;
        end;
      except
        if (Attempt >= 3) then Terminate;
        ConnectDelay := 2500;
        Continue;
      end;

      try
        ConnectDelay := 0;

        while (not Terminated) and
              (FTermEvent.WaitFor(0) = wrTimeout) do
        begin
          try
            Recebeu := TCPClient.IOHandler.ReadLn(LF, 1000);
            if (not TCPClient.IOHandler.ReadLnTimedOut) and (Recebeu <> '') then
            begin
              ...
            end;
            ...
          except
            ...
            Break;
          end;
        end;
      finally
        TCPClient.Disconnect(False);
        Attempt := 0;
      end;
    end;
  finally
    TCPClient.Free;
  end;
end;

procedure TThreadCon.TerminatedSet;
begin
  FTermEvent.SetEvent;
end;