Delphi: Indy TIdTCPClient 读取数据

4

我正在使用Delphi 2007和Indy 10;我是一个Delphi新手,如果我错过了一些明显的东西,请原谅...

背景:我有一个简单的服务器应用程序,当您连接到其端口时,它会简单地发送单词“PING”。如果它收到单词“PONG”,它也会做出响应。这很好地工作着,我已经使用netcat / wireshark手动测试过了。

我正在尝试编写客户端以连接到该端口,并在接收到PING时自动做出响应。我创建了一个简单的表格,其中包含一个按钮以手动连接。

客户端已连接,但它没有响应PING。 我认为问题出在这里:

TLog.AddMsg(FConn.IOHandler.ReadLn);

我的调试日志仅报告到“DEBUG:TReadingThread.Execute - FConn.Connected”。

我的客户端代码:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, IdCustomTransparentProxy, IdSocks, IdBaseComponent,
  IdComponent, IdIOHandler, IdIOHandlerSocket, IdIOHandlerStack,
  IdTCPConnection, IdTCPClient, IdSync;

type

  TReadingThread = class(TThread)
  protected
    FConn: TIdTCPConnection;
    procedure Execute; override;
  public
    constructor Create(AConn: TIdTCPConnection); reintroduce;
  end;

  TLog = class(TIdSync)
  protected
    FMsg: String;
  procedure DoSynchronize; override;
  public
    constructor Create(const AMsg: String);
    class procedure AddMsg(const AMsg: String);
  end;


  TForm1 = class(TForm)
    Memo1: TMemo;
    Button1: TButton;
    IdIOHandlerStack1: TIdIOHandlerStack;
    client: TIdTCPClient;
    IdSocksInfo1: TIdSocksInfo;
    procedure Button1Click(Sender: TObject);
    procedure clientConnected(Sender: TObject);
    procedure clientDisconnected(Sender: TObject);
    procedure FormCreate(Sender: TObject);

  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  rt: TReadingThread = nil;

implementation

{$R *.dfm}

constructor TReadingThread.Create(AConn: TIdTCPConnection);
begin
  Form1.Memo1.Lines.Add('DEBUG: TReadingThread.Create'); // Debug

  FConn := AConn;
  inherited Create(False);
end;

procedure TReadingThread.Execute;
begin
  Form1.Memo1.Lines.Add('DEBUG: TReadingThread.Execute'); // Debug

  while not Terminated and FConn.Connected do
  begin
    Form1.Memo1.Lines.Add('DEBUG: TReadingThread.Execute - FConn.Connected'); // Debug

    TLog.AddMsg(FConn.IOHandler.ReadLn);
  end;
end;

constructor TLog.Create(const AMsg: String);
begin
  Form1.Memo1.Lines.Add('DEBUG: TLog.Create'); // Debug

  FMsg := AMsg;
  inherited Create;
end;

procedure TLog.DoSynchronize;
var
  cmd : string;
begin
  Form1.Memo1.Lines.Add('DEBUG: TLog.DoSynchronize'); // Debug

  cmd := copy(FMsg, 1, 1);

  if cmd='PING' then  begin
    Form1.client.Socket.WriteLn('PONG');
  end

end;

class procedure TLog.AddMsg(const AMsg: String);
begin
  Form1.Memo1.Lines.Add('DEBUG: TLog.AddMsg');  // Debug

  with Create(AMsg) do try
    Synchronize;
    finally
    Free;
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Memo1.Clear;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  Host : String;
  Port : Integer;
begin

  Host := '127.0.0.1';
  Port := StrToInt('1234');

  client.Host := Host;
  client.Port := Port;

  with client do
  begin
    try
      Connect;
      except
      on E: Exception do
      Memo1.Lines.Add('Error: ' + E.Message);
    end;
  end;

end;

procedure TForm1.clientConnected(Sender: TObject);
begin
  Form1.Memo1.Lines.Add('DEBUG: TForm1.clientConnected'); // Debug

  rt := TReadingThread.Create(client);
end;

procedure TForm1.clientDisconnected(Sender: TObject);
begin
  Form1.Memo1.Lines.Add('DEBUG: TForm1.clientDisconnected');  // Debug

  if rt <> nil then
    begin
      rt.Terminate;
      rt.WaitFor;
      FreeAndNil(rt);
    end;
end;

end.

任何帮助和建议将不胜感激。
谢谢。

服务器是否响应“PONG”或“PONG#0D#0A”? - Martin James
PONG,服务器会剥离新行符等。 - Jamie Wright
1个回答

9
阅读线程直接访问 Form1.Memo1,这是不安全的线程,并可能导致死锁、崩溃、内存损坏等问题。因此,阅读线程甚至可能根本没有执行到 ReadLn() 调用。无论实际访问有多微不足道,您必须将对UI控件的所有访问与主线程同步。不要冒险。
此外,您正在 TLog 中执行您的线程 ping/pong 逻辑,这是不应该的。更何况,在检查其值之前,您正在将 cmd 截断为仅包含其第一个字符,因此它将永远不能检测到 PING 命令。您需要将逻辑移回线程中,这是其真正所属的地方,并删除截断。
请尝试以下内容:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, IdCustomTransparentProxy, IdSocks, IdBaseComponent,
  IdComponent, IdIOHandler, IdIOHandlerSocket, IdIOHandlerStack,
  IdTCPConnection, IdTCPClient, IdSync;

type

  TReadingThread = class(TThread)
  protected
    FConn: TIdTCPConnection;
    procedure Execute; override;
    procedure DoTerminate; override;
  public
    constructor Create(AConn: TIdTCPConnection); reintroduce;
  end;

  TLog = class(TIdSync)
  protected
    FMsg: String;
    procedure DoSynchronize; override;
  public
    constructor Create(const AMsg: String);
    class procedure AddMsg(const AMsg: String);
  end;

  TForm1 = class(TForm)
    Memo1: TMemo;
    Button1: TButton;
    IdIOHandlerStack1: TIdIOHandlerStack;
    client: TIdTCPClient;
    IdSocksInfo1: TIdSocksInfo;
    procedure Button1Click(Sender: TObject);
    procedure clientConnected(Sender: TObject);
    procedure clientDisconnected(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  rt: TReadingThread = nil;

implementation

{$R *.dfm}

constructor TReadingThread.Create(AConn: TIdTCPConnection);
begin
  TLog.AddMsg('DEBUG: TReadingThread.Create');
  FConn := AConn;
  inherited Create(False);
end;

procedure TReadingThread.Execute;
var
  cmd: string;
begin
  TLog.AddMsg('DEBUG: TReadingThread.Execute');

  while not Terminated do
  begin
    cmd := FConn.IOHandler.ReadLn;
    TLog.AddMsg('DEBUG: TReadingThread.Execute. Cmd: ' + cmd);
    if cmd = 'PING' then begin
      FConn.IOHandler.WriteLn('PONG');
    end
  end;
end;

procedure TReadingThread.DoTerminate;
begin
  TLog.AddMsg('DEBUG: TReadingThread.DoTerminate');
  inherited;
end;

constructor TLog.Create(const AMsg: String);
begin
  inherited Create;
  FMsg := AMsg;
end;

procedure TLog.DoSynchronize;
begin
  Form1.Memo1.Lines.Add(FMsg);
end;

class procedure TLog.AddMsg(const AMsg: String);
begin
  with Create(AMsg) do
  try
    Synchronize;
  finally
    Free;
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Memo1.Clear;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  Host : String;
  Port : Integer;
begin
  Host := '127.0.0.1';
  Port := StrToInt('1234');

  client.Host := Host;
  client.Port := Port;

  try
    client.Connect;
  except
    on E: Exception do
      TLog.AddMsg('Error: ' + E.Message);
    end;
  end;
end;

procedure TForm1.clientConnected(Sender: TObject);
begin
  TLog.AddMsg('DEBUG: TForm1.clientConnected');
  rt := TReadingThread.Create(client);
end;

procedure TForm1.clientDisconnected(Sender: TObject);
begin
  TLog.AddMsg('DEBUG: TForm1.clientDisconnected');
  if rt <> nil then
  begin
    rt.Terminate;
    rt.WaitFor;
    FreeAndNil(rt);
  end;
end;

end.

如果仍然无法解决问题,则确保服务器实际上使用CRLF序列或至少一个LF字符(这是ReadLn()默认查找的最小值)来分隔PING字符串。


经过两个小时的头疼,如果这能帮到某些人,请记住:确保服务器实际上使用CRLF序列或至少一个LF字符来分隔PING字符串。 - Martin Ocando Corleone

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接