Delphi:Indy TIdTCPClient读取数据 [英] Delphi: Indy TIdTCPClient Reading Data
问题描述
我正在使用Delphi 2007&印第10号;如果我错过了一些明显的事情,我会有点儿Delphi傻了,很抱歉...
I am using Delphi 2007 & Indy 10; I am a bit of a Delphi noob so apologies if I have missed something obvious...
背景:我有一个简单的服务器应用程序,当您连接到其端口时,它会简单地发送单词"PING".如果收到单词"PONG",它也会响应.一切正常,我已经使用netcat/wireshark进行了手动测试.
Background: I have a simple server app which simply sends the word "PING" when you connect to its port. It will also respond if it receives the word "PONG". This is working fine, I have manually tested this using netcat/wireshark.
我试图对客户端进行编码以使其连接到端口,并在收到PING字样时自动对其进行响应.我创建了一个简单的表单,其中包含一个用于手动连接的按钮.
I am trying to code my client to connect to the port and automatically respond to the word PING whenever it receives it. I have created a simple form with a button to manually connect.
客户端连接,但是它不响应单词PING. 我认为问题在于:
The client connects, but it does not respond to the word PING. I think the problem lies with:
TLog.AddMsg(FConn.IOHandler.ReadLn);
我的调试日志仅报告"DEBUG:TReadingThread.Execute-FConn.Connected".
My debug log reports only as far as "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.
任何帮助/建议将不胜感激.
Any help/advice would be appreciated.
谢谢
推荐答案
读取线程正在直接访问Form1.Memo1
,这不是线程安全的,并且可能导致死锁,崩溃,内存损坏等.因此,读取线程甚至根本达不到ReadLn()
调用.您必须将对UI控件的 ALL 访问权限同步到主线程,无论访问的实际程度如何.只是不要冒险.
The reading thread is directly accessing Form1.Memo1
, which is not thread safe and can cause deadlocks, crashes, corrupted memory, etc. So it is possible that the reading thread is not even reaching the ReadLn()
call at all. You MUST synchronize ALL access to UI controls to the main thread, no matter how trivial the access actually is. Just don't risk it.
此外,您正在TLog
本身内部执行线程的ping/pong逻辑,而该逻辑不属于该逻辑.更不用说您在检查cmd
值之前将其截断为仅第一个字符,因此它将从不检测到PING
命令.您需要将逻辑移回它真正所属的线程中,并删除截断.
Also, you are doing your thread's ping/pong logic inside of TLog
itself, where it does not belong. Not to mention that you are truncating the cmd
to only its first character before checking its value, so it will NEVER detect a PING
command. You need to move the logic back into the thread, where it really belongs, and remove the truncation.
尝试一下:
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
字符串.默认).
If that still does not work, then make sure the server is actually delimiting the PING
string with a CRLF
sequence, or at least a LF
character (which is the minimum that ReadLn()
looks for by default).
这篇关于Delphi:Indy TIdTCPClient读取数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!