Home > Net >  TcpClient giving errors at thread execute at connect and write
TcpClient giving errors at thread execute at connect and write

Time:04-01

I'm having a problem on a laptop connection to TIdTCPServer. The thing is, it connects fine, sends a command, but when it tries to send it again, it gets socket error 10053 or 10004 or 10054.

The same code on other computers works fine, just on this one laptop this error happens.

I'm using a connection in a thread, here is the code:

type
  TThreadCon = class(TThread)
  private
    TCPClient : TIdTCPClient;
  protected
    procedure Execute; override;
  public
    constructor Create;
    destructor Destroy; override;
  end;

procedure DJWRZORLBS(millisecs: Integer);
var
  tick    : dword;
  AnEvent : THandle;
begin
  AnEvent := CreateEvent(nil, False, False, nil);
  try
    tick := GetTickCount   dword(millisecs);
    while (millisecs > 0) and (MsgWaitForMultipleObjects(1, AnEvent, False, millisecs, QS_ALLINPUT) <> WAIT_TIMEOUT) do begin
      Application.ProcessMessages;
      if Application.Terminated then Exit;
      millisecs := tick - GetTickcount;
    end;
  finally
    CloseHandle(AnEvent);
  end;
end;

constructor TThreadCon.Create;
begin
  inherited Create(True);
  TCPClient := TIdTCPClient.Create(Nil);
  TCPClient.ReadTimeout     := 3*60000;
  TCPClient.ConnectTimeout  := 3*60000;
  TCPClient.Port            := StrToInt(PortaPS);
  TCPClient.Host            := Host;
  TCPClient.IPVersion       := Id_IPv4;
  TCPClient.UseNagle        := True;
  TCPClient.ReuseSocket     := rsOSDependent;
end;

procedure TThreadCon.Execute;
begin
  while True do
  begin
    //Sleep(2500);

    try
      if not TCPClient.Connected then
      begin
        TCPClient.Connect;

        if TCPClient.Connected then
        begin
          Attempts:= 0;
          WriteLn(Format('[%s] Connected to server. [%d]', [TimeToStr(Now), Attempts]));

          TCPClient.IOHandler.WriteLn('connect');    
          if rt = nil then rt := TReadingThread.Create(TCPClient);
        end;
      end
      else
      begin
        LastPing:= GetTickCount;

        try
          TCPClient.IOHandler.WriteLn('Ping');
        except
          on E: Exception do
          begin
            WriteLn(Format('[%s] Error while trying send ping: %s', [TimeToStr(Now), E.Message]));
          end;
        end;

        WriteLn(Format('[%s] Ping send, Last Ping [%d]', [TimeToStr(Now), GetTickCount-LastPing]));
      end;
    except
      on E: Exception do
      begin
        Inc(Attempts);

        TCPClient.Disconnect(False);
        if TCPClient.IOHandler <> nil then TCPClient.IOHandler.InputBuffer.Clear;

        WriteLn(Format('[%s] Failed to connect, error: %s [%d]', [TimeToStr(Now), E.Message, Attempts]));
      end;
    end;

    DJWRZORLBS(5000);
  end;
end;

Below is the Console log where the problem happens. It connects to the server, then when the thread runs again, where it should send Ping starts the problems, and for some reason in some cases it always shows as connected at every thread run, like TCPClient.Connected isn't connected.

image

This is a normal log on a computer that works fine:

[21:44:59] Connected to server. [0]
[21:45:04] Ping send, Last Ping [0]
[21:45:09] Ping send, Last Ping [0]

if I close the server, wait some seconds and reopen, it shows as this:

[21:45:54] Failed to connect, error: Socket Error # 10054
Connection reset by peer. [1]
[21:46:01] Failed to connect, error: Socket Error # 10061
Connection refused. [2]
[21:46:08] Failed to connect, error: Socket Error # 10061
Connection refused. [3]
[21:46:14] Connected to server. [0]
[21:46:19] Ping send, Last Ping [0]

For me, it's how it should work correctly.

What can cause this? Some problem on the server? But if it is at the server, why do other machines work fine?

Some network setting? If yes, what can I do to solve it?

CodePudding user response:

Internally, Connected performs a reading operation, which is not a good thing in your case since you have another thread that reads from the same socket at the same time if Connect() is successful. The two threads will fight over access to the socket and putting data into its IOHandler.InputBuffer.

In any case, Connected returns True if there is any unread data in the InputBuffer, even if the underlying socket fails.

Your TThreadCon is not structured very well. I would suggest restructuring it to eliminate the need for using Connected at all (and DJWRZORLBS(), since TThreadCon does not have a message queue that needs to be pumped). A better design would be to have the thread connect in a loop until successful, then send pings in a loop, then disconnect, and repeat as needed.

Try something more like this:

type
  TThreadCon = class(TThread)
  private
    FTermEvent: TEvent;
  protected
    procedure Execute; override;
    procedure DoTerminate; override;
    procedure TerminatedSet; override;
  public
    constructor Create; reintroduce;
    destructor Destroy; override;
  end;

constructor TThreadCon.Create;
begin
  inherited Create(True);
  FTermEvent := TEvent.Create;
end;

destructor TThreadCon.Destroy;
begin
  FTermEvent.Free;
  inherited;
end;

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

procedure TThreadCon.Execute;
var
  TCPClient: TIdTCPClient;
  rt: TReadingThread;
  Attempts: Integer;
begin
  TCPClient := TIdTCPClient.Create(nil);
  try
    TCPClient.ReadTimeout     := 3*60000;
    TCPClient.ConnectTimeout  := 3*60000;
    TCPClient.Port            := StrToInt(PortaPS);
    TCPClient.Host            := Host;
    TCPClient.IPVersion       := Id_IPv4;
    TCPClient.UseNagle        := True;
    TCPClient.ReuseSocket     := rsOSDependent;

    Attempts := 0;

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

      try
        TCPClient.Connect;
        try
          TCPClient.IOHandler.WriteLn('connect');    
        except
          TCPClient.Disonnect(False);
          raise;
        end;
      except
        on E: Exception do
        begin
          Inc(Attempts);
          WriteLn(Format('[%s] Failed to connect, error: %s [%d]', [TimeToStr(Now), E.Message, Attempts]));
          if FTermEvent.WaitFor(2500) <> wrTimeout then Exit;
          Continue;
        end;
      end;

      Attempts := 0;
      WriteLn(Format('[%s] Connected to server.', [TimeToStr(Now)]));

      rt := TReadingThread.Create(TCPClient);
      try
        try
          while not Terminated do
          begin
            LastPing := GetTickCount;      
            TCPClient.IOHandler.WriteLn('Ping');
            WriteLn(Format('[%s] Ping send, Last Ping [%d]', [TimeToStr(Now), GetTickCount-LastPing]));
            if FTermEvent.WaitFor(5000) <> wrTimeout then Exit;
          end;
        except
          on E: Exception do
          begin
            WriteLn(Format('[%s] Error while trying to send ping: %s', [TimeToStr(Now), E.Message]));
          end;
        end;            
      finally
        rt.Terminate;
        try
          TCPClient.Disconnect(False);
        finally
          rt.WaitFor;
          rt.Free;
        end;
      end;
    end;
  finally
    TCPClient.Free;
  end;
end;

procedure TThreadCon.DoTerminate;
begin
  if FatalException <> nil then
    WriteLn(Format('[%s] Fatal Error: %s', [TimeToStr(Now), Exception(E).Message]));
  inherited;
end;
  • Related