Indyは、クライアント側とサーバー側の両方のブロッキングソケットを使用します。それについては何も非同期です。 TIdTCPServer
の場合、クライアントで実行しようとしているのと同じように、別のワーカースレッドで各クライアントソケットを実行します。 TIdTCPClient
はマルチスレッドではないため、独自のスレッドを実行する必要があります。
:あなたはインディ10にアップグレードする場合は、サーバーから受信したパケットのためのTIdCommandHandler.OnCommand
イベントをトリガーする、あなたのための独自のスレッドを実行し、マルチスレッドでTIdCmdTCPClient
クライアントを持っています。
ReadLn()
InputBuffer
に指定されたATerminator
が見つかるまで、またはタイムアウトが発生するまでループを実行します。 ATerminator
が見つかるまで、ReadLn()
はソケットからさらにInputBuffer
にデータを読み込み、再度スキャンします。バッファサイズのチェックは、すでにスキャンしたデータを再スキャンしないことを確認することです。
ブロッキングReadLn()
コール(またはブロッキングソケットコール)を「起床」する唯一の方法は、別のスレッドからソケットを閉じることです。それ以外の場合は、通常はタイムアウトになるまで待たなければなりません。
また、ReadLn()
は、タイムアウト時にEIdReadTimeout
例外を発生させません。それは例えば、TrueにReadLnTimedout
プロパティを設定し、空の文字列を返します。
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()
とオーバーラップしたI/Oを使用して、CreateEvent()
またはTEvent
を介して待機可能なイベントを作成して、スレッド終了を通知してから、スレッドは何もしないときにスリープ中にソケットと終了の両方を同時に待つことができます例えば:あなたは、Delphi XE2以降を使用している場合は
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;
、TThread
はあなたがTThread.Terminate()
が呼び出されたときにhTermEvent
を知らせるためにオーバーライドできる仮想TerminatedSet()
方法があります。そうでない場合は、Terminate()
を呼び出した後にSetEvent()
に電話してください。
ブレークダウンありがとう。私はReadLnの性質をブロックすることを心配していません、私はバッファサイズをチェックするビジーな待ちループが心配です。 私はインディとデルファイ7(レガシーコード)の古いバージョンを使用していますので、変更の余地はありません。 –
@HéctorC。読み込みタイムアウトを完全に取り除き、読み込むデータがないときに 'ReadLn'がスレッドをブロックし、スレッドを終了する準備ができたらクライアントを切断することを検討してください。 –