Indy使用阻塞套接字,客户端和服务器端。关于它没有任何异步。在TIdTCPServer
的情况下,它将在单独的工作线程中运行每个客户端套接字,就像您在客户端尝试执行的一样。 TIdTCPClient
不是多线程的,所以你必须运行你自己的线程。
:如果您升级到印第安纳波利斯10,它有一个TIdCmdTCPClient
客户端是多线程的,运行其自己的线程你,触发TIdCommandHandler.OnCommand
事件从服务器接收的数据包。
ReadLn()
运行一个循环,直到在InputBuffer
中找到指定的ATerminator
,或者直到发生超时。在找到ATerminator
之前,ReadLn()
会将更多数据从套接字读取到InputBuffer
并再次扫描。缓冲区大小检查只是为了确保它不会重新扫描已经扫描的数据。
“唤醒”阻塞ReadLn()
调用(或任何阻塞的套接字调用,就此而言)的唯一方法是从另一个线程关闭套接字。否则,你只需等待通话超时。请注意0在超时时不会引发异常。它设置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;
如果你不喜欢这种模式,你不必用印。一个更高效和响应的模型应该是直接使用WinSock。您可以使用WSARecv()
的Overlapped I/O,并通过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()
方法可以重写到信号hTermEvent
时TThread.Terminate()
被调用。否则,致电Terminate()
后致电SetEvent()
。
感谢您的分解。我不担心ReadLn的阻塞性质,我担心检查缓冲区大小的繁忙等待循环。 我坚持使用老版本的Indy和Delphi 7(遗留代码),所以没有太大的改变空间。 –
@HéctorC。然后考虑彻底摆脱读取超时,让'ReadLn'在没有数据要读取时阻塞线程,然后在准备好终止线程时断开客户端连接。 –