【发布时间】:2017-08-30 01:50:35
【问题描述】:
如何使用 TIdTcpClient 接收具有以下条件的 100 字节字符串?:
- 如果什么都没有进来,Read 调用将被阻塞,线程将永远等待
- 如果收到 100 个字节,则 Read 调用应返回字节字符串
- 如果收到的字节数多于 0,但少于 100,则 Read 调用应在超时(例如 1 秒)后返回,以便在合理的时间内至少返回某些内容,而不会产生超时异常,因为 Delphi 中的异常处理IDE 的调试模式不方便。
我目前不是最优的代码如下:
unit Unit2;
interface
uses
System.Classes, IdTCPClient;
type
TTcpReceiver = class(TThread)
private
_tcpc: TIdTCPClient;
_onReceive: TGetStrProc;
_buffer: AnsiString;
procedure _receiveLoop();
procedure _postBuffer;
protected
procedure Execute(); override;
public
constructor Create(); reintroduce;
destructor Destroy(); override;
property OnReceive: TGetStrProc read _onReceive write _onReceive;
end;
implementation
uses
System.SysUtils, Vcl.Dialogs, IdGlobal, IdExceptionCore;
constructor TTcpReceiver.Create();
begin
inherited Create(True);
_buffer := '';
_tcpc := TIdTCPClient.Create(nil);
//_tcpc.Host := '192.168.52.175';
_tcpc.Host := '127.0.0.1';
_tcpc.Port := 1;
_tcpc.ReadTimeout := 1000;
_tcpc.Connect();
Suspended := False;
end;
destructor TTcpReceiver.Destroy();
begin
_tcpc.Disconnect();
FreeAndNil(_tcpc);
inherited;
end;
procedure TTcpReceiver.Execute;
begin
_receiveLoop();
end;
procedure TTcpReceiver._postBuffer();
var buf: string;
begin
if _buffer = '' then Exit;
buf := _buffer;
_buffer := '';
if Assigned(_onReceive) then begin
Synchronize(
procedure()
begin
_onReceive(buf);
end
);
end;
end;
procedure TTcpReceiver._receiveLoop();
var
c: AnsiChar;
begin
while not Terminated do begin
try
c := AnsiChar(_tcpc.IOHandler.ReadByte());
_buffer := _buffer + c;
if Length(_buffer) > 100 then
_postBuffer();
except
//Here I have to ignore EIdReadTimeout in Delphi IDE everywhere, but I want just to ignore them here
on ex: EIdReadTimeout do _postBuffer();
end;
end;
end;
end.
【问题讨论】:
-
为什么不直接使用
IOHandler.ReadBytes()而不是自己尝试做这项艰苦的工作? -
@Paul 我很确定 Indy 对缓冲区的处理比我们大多数人希望管理的要好。如果你只想要 50 个字节,为什么要 100 个?在某些时候,您有一个有意义的最小数据包大小。等待那个数据包——如果它来了,就处理它,如果它没有,那么你无事可做。如果您不在乎丢失数据,也许 UDP 是比 TCP 更好的协议。
-
@Paul 这是您设计的协议,还是您遵循某种协议?这种方法似乎没有多大意义。
-
TCP 是面向流的,而不是面向消息的。在没有任何结构的情况下读取任意字节是不好的设计,并且当您过早停止读取时很容易破坏您的通信,然后您想要读取的字节在您停止读取后到达。在读取它们之前,它们不会从套接字中删除。如果您期望 100 个字节,则读取 100 个字节。如果发送方只发送 50 个字节,它需要告诉接收方,以便在收到 50 个字节后停止读取。如果您的发件人没有这样做,那么这是一个设计得很糟糕的 TCP 协议。
-
而您关于“Delphi IDE 的调试模式中的异常处理不方便”的说法是荒谬的。 Indy 的
IOHandler具有用于控制异常行为的属性和方法参数,如果您不喜欢调试器处理异常的方式,则将其配置为根本不处理它们。您可以将调试器配置为忽略特定异常,也可以使用断点禁止调试器处理特定代码块中的异常。