delphi TcpClient在连接和写入时执行线程时出错

ui7jx7zq  于 2022-12-23  发布在  其他
关注(0)|答案(1)|浏览(267)

我的笔记本电脑连接到TIdTCPServer时遇到了一个问题。问题是,它连接正常,发送了一个命令,但当它试图再次发送时,它得到了套接字错误10053或10004或10054。
相同的代码在其他计算机上工作正常,只是在这台笔记本电脑上发生了这个错误。
我在一个线程中使用连接,下面是代码:

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;

下面是发生问题的控制台日志。它连接到服务器,然后当线程再次运行时,它应该发送Ping开始出现问题,出于某种原因,在某些情况下,它总是在每次线程运行时显示为连接,就像TCPClient.Connected没有连接一样。

这是工作正常的计算机上的正常日志:

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

如果我关闭服务器,等待几秒钟,然后重新打开,它显示如下:

[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]

对我来说,这是它应该如何正确地工作。
是什么原因导致的?服务器上的一些问题?但如果是在服务器上,为什么其他机器工作正常?
一些网络设置?如果是,我可以做些什么来解决它?

t0ybt7op

t0ybt7op1#

在内部,Connected执行一个阅读操作,这在您的情况下不是一件好事,因为如果Connect()成功,您会有另一个线程同时从同一个套接字读取,这两个线程将争夺对套接字的访问权并将数据放入它的IOHandler.InputBuffer
在任何情况下,如果InputBuffer中有任何未读数据,Connected都将返回True,即使底层套接字失败也是如此。
您的TThreadCon结构不是很好。我建议重新构建它,以完全消除使用Connected(和DJWRZORLBS(),因为TThreadCon没有需要泵送的消息队列)的需要。更好的设计是让线程在循环中连接,直到成功,然后在循环中发送ping,然后断开连接,并根据需要重复。
试试这样的方法:

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.Disconnect(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;

相关问题