Indy 在客户端和服务器端都使用阻塞套接字。它没有什么异步的。对于TIdTCPServer,它在单独的工作线程中运行每个客户端套接字,就像您尝试在客户端中执行的操作一样。 TIdTCPClient1 不是多线程的,所以你必须运行自己的线程。
1:如果你升级到 Indy 10,它有一个多线程的 TIdCmdTCPClient 客户端,为你运行自己的线程,为从服务器接收到的数据包触发 TIdCommandHandler.OnCommand 事件.
ReadLn() 运行一个循环,直到在InputBuffer 中找到指定的ATerminator,或者直到发生超时。在找到ATerminator 之前,ReadLn() 从套接字读取更多数据到InputBuffer 并再次扫描。缓冲区大小检查只是为了确保它不会重新扫描已经扫描过的数据。
“唤醒”阻塞 ReadLn() 调用(或任何阻塞套接字调用,就此而言)的唯一方法是从另一个线程关闭套接字。否则,你只需要正常等待调用超时即可。
另请注意,ReadLn() 在超时时不会引发 EIdReadTimeout 异常。它将ReadLnTimedout 属性设置为True,然后返回一个空白字符串,例如:
ConnectionToServer.ReadTimeout := MyTimeOut;
while not Terminated do
begin
try
Command := ConnectionToServer.ReadLn;
except
on E: Exception do
begin
if E is EIdConnClosedGracefully then
AnotarMensaje(odDepurar, 'Conexión cerrada')
else
AnotarMensaje(odDepurar, 'Error en lectura: ' + E.Message );
Exit;
end;
end;
if ConnectionToServer.ReadLnTimedout then begin
//AnotarMensaje(odDepurar, 'Timeout');
Continue;
end;
// treat the command
ExecuteRemoteCommand( Command );
end;
如果您不喜欢此模型,则不必使用 Indy。一个更有效和响应更快的模型是直接使用 WinSock。您可以将 Overlapped I/O 与WSARecv() 一起使用,并通过CreateEvent() 或TEvent 创建一个可等待事件来表示线程终止,然后您的线程可以使用WaitForMultipleObjects() 同时在套接字和终止上等待无事可做的时候睡觉,例如:
hSocket = socket(...);
connect(hSocket, ...);
hTermEvent := CreateEvent(nil, True, False, nil);
...
var
buffer: array[0..1023] of AnsiChar;
wb: WSABUF;
nRecv, nFlags: DWORD;
ov: WSAOVERLAPPED;
h: array[0..1] of THandle;
Command: string;
Data, Chunk: AnsiString;
I, J: Integer;
begin
ZeroMemory(@ov, sizeof(ov));
ov.hEvent := CreateEvent(nil, True, False, nil);
try
h[0] := ov.hEvent;
h[1] := hTermEvent;
try
while not Terminated do
begin
wb.len := sizeof(buffer);
wb.buf := buffer;
nFlags := 0;
if WSARecv(hSocket, @wb, 1, @nRecv, @nFlags, @ov, nil) = SOCKET_ERROR then
begin
if WSAGetLastError() <> WSA_IO_PENDING then
RaiseLastOSError;
end;
case WaitForMultipleObjects(2, PWOHandleArray(@h), False, INFINITE) of
WAIT_OBJECT_0: begin
if not WSAGetOverlappedResult(hSocket, @ov, @nRecv, True, @nFlags) then
RaiseLastOSError;
if nRecv = 0 then
begin
AnotarMensaje(odDepurar, 'Conexión cerrada');
Exit;
end;
I := Length(Data);
SetLength(Data, I + nRecv);
Move(buffer, Data[I], nRecv);
I := Pos(Data, #10);
while I <> 0 do
begin
J := I;
if (J > 1) and (Data[J-1] = #13) then
Dec(J);
Command := Copy(Data, 1, J-1);
Delete(Data, 1, I);
ExecuteRemoteCommand( Command );
end;
end;
WAIT_OBJECT_0+1: begin
Exit;
end;
WAIT_FAILED: begin
RaiseLastOSError;
end;
end;
end;
except
on E: Exception do
begin
AnotarMensaje(odDepurar, 'Error en lectura ' + E.Message );
end;
end;
finally
CloseHandle(ov.hEvent);
end;
end;
如果您使用的是 Delphi XE2 或更高版本,TThread 有一个虚拟的 TerminatedSet() 方法,您可以在调用 TThread.Terminate() 时覆盖以发出信号 hTermEvent。否则,只需在调用Terminate() 后调用SetEvent()。