Héc*_* C. 4 delphi asynchronous indy
我\xc2\xb4m是Delphi的新手,我\xc2\xb4m尝试进行一些网络操作。在本例中,我想连接到(让\xc2\xb4s 称之为)通知服务器,该服务器将在发生某些事件时发送字符串。
\n\n我的第一种方法是这样的:\n我在自己的线程上运行 TIdTCPClient 并设置 ReadTimeout,这样我\xc2\xb4m 就不会总是被阻塞。这样我可以检查线程的终止状态。
\n\nConnectionToServer.ReadTimeout := MyTimeOut;\nwhile( Continue ) do\nbegin\n //\n try\n Command := ConnectionToServer.ReadLn( );\n except\n on E: EIdReadTimeout do\n begin\n //AnotarMensaje(odDepurar, 'Timeout ' + E.Message );\n end;\n on E: EIdConnClosedGracefully do\n begin\n AnotarMensaje(odDepurar, 'Conexi\xc3\xb3n cerrada ' + E.Message );\n Continue := false;\n end;\n on E: Exception do\n begin\n AnotarMensaje(odDepurar, 'Error en lectura ' + E.Message );\n Continue := false;\n end;\n end;\n // treat the command\n ExecuteRemoteCommand( Command ); \n if( self.Terminated ) then\n begin\n Continue := false;\n end;\nend; // while continue\nRun Code Online (Sandbox Code Playgroud)\n\n阅读 ReadLn 代码时,我发现它在重复直到循环中执行一些主动等待,该循环始终检查某些缓冲区大小。
\n\n有没有办法以 TIdTCPServer 与 OnExecute 等方法一起工作的方式异步执行此操作?或者,至少有某种方法可以避免这种主动等待。
\nIndy 在客户端和服务器端都使用阻塞套接字。它没有什么异步的地方。在 的情况下TIdTCPServer,它在单独的工作线程中运行每个客户端套接字,就像您尝试在客户端中执行的操作一样。 TIdTCPClient1不是多线程,所以你必须运行自己的线程。
1:如果您升级到 Indy 10,它有一个TIdCmdTCPClient多线程客户端,为您运行自己的线程,触发TIdCommandHandler.OnCommand从服务器接收的数据包事件。
ReadLn()运行循环,直到ATerminator在 中找到指定的值InputBuffer,或者直到发生超时。直到ATerminator找到为止,ReadLn()从套接字读取更多数据到InputBuffer并再次扫描。缓冲区大小检查只是为了确保它不会重新扫描已经扫描过的数据。
“唤醒”阻塞ReadLn()调用(或任何阻塞套接字调用)的唯一方法是从另一个线程关闭套接字。否则,你只需等待调用正常超时即可。
另请注意,超时时ReadLn()不会引发异常。EIdReadTimeout它将ReadLnTimedout属性设置为 True,然后返回一个空字符串,例如:
ConnectionToServer.ReadTimeout := MyTimeOut;\n\nwhile not Terminated do\nbegin\n try\n Command := ConnectionToServer.ReadLn;\n except\n on E: Exception do\n begin\n if E is EIdConnClosedGracefully then\n AnotarMensaje(odDepurar, \'Conexi\xc3\xb3n cerrada\')\n else\n AnotarMensaje(odDepurar, \'Error en lectura: \' + E.Message );\n Exit;\n end;\n end;\n\n if ConnectionToServer.ReadLnTimedout then begin\n //AnotarMensaje(odDepurar, \'Timeout\');\n Continue;\n end;\n\n // treat the command\n ExecuteRemoteCommand( Command ); \nend;\nRun Code Online (Sandbox Code Playgroud)\n\n如果你不喜欢这个模型,你不必使用 Indy。更高效、响应更快的模型是直接使用 WinSock。您可以将 Overlapped I/O 与 一起使用WSARecv(),并通过CreateEvent()或创建一个可等待事件来发出TEvent线程终止信号,然后您的线程可以WaitForMultipleObjects()在无事可做时在睡眠时同时等待套接字和终止,例如:
hSocket = socket(...);\nconnect(hSocket, ...);\nhTermEvent := CreateEvent(nil, True, False, nil);\n\n...\n\nvar\n buffer: array[0..1023] of AnsiChar;\n wb: WSABUF;\n nRecv, nFlags: DWORD;\n ov: WSAOVERLAPPED;\n h: array[0..1] of THandle;\n Command: string;\n Data, Chunk: AnsiString;\n I, J: Integer;\nbegin\n ZeroMemory(@ov, sizeof(ov));\n ov.hEvent := CreateEvent(nil, True, False, nil);\n try\n h[0] := ov.hEvent;\n h[1] := hTermEvent;\n\n try\n while not Terminated do\n begin\n wb.len := sizeof(buffer);\n wb.buf := buffer;\n\n nFlags := 0;\n\n if WSARecv(hSocket, @wb, 1, @nRecv, @nFlags, @ov, nil) = SOCKET_ERROR then\n begin\n if WSAGetLastError() <> WSA_IO_PENDING then\n RaiseLastOSError;\n end;\n\n case WaitForMultipleObjects(2, PWOHandleArray(@h), False, INFINITE) of\n WAIT_OBJECT_0: begin\n if not WSAGetOverlappedResult(hSocket, @ov, @nRecv, True, @nFlags) then\n RaiseLastOSError;\n\n if nRecv = 0 then\n begin\n AnotarMensaje(odDepurar, \'Conexi\xc3\xb3n cerrada\');\n Exit;\n end;\n\n I := Length(Data);\n SetLength(Data, I + nRecv);\n Move(buffer, Data[I], nRecv);\n\n I := Pos(Data, #10);\n while I <> 0 do\n begin\n J := I;\n if (J > 1) and (Data[J-1] = #13) then\n Dec(J);\n\n Command := Copy(Data, 1, J-1);\n Delete(Data, 1, I);\n\n ExecuteRemoteCommand( Command );\n end;\n end;\n\n WAIT_OBJECT_0+1: begin\n Exit;\n end;\n\n WAIT_FAILED: begin\n RaiseLastOSError;\n end;\n end;\n end;\n except\n on E: Exception do\n begin\n AnotarMensaje(odDepurar, \'Error en lectura \' + E.Message );\n end;\n end;\n finally\n CloseHandle(ov.hEvent);\n end;\nend;\nRun Code Online (Sandbox Code Playgroud)\n\n如果您使用的是 Delphi XE2 或更高版本,TThread则有一个虚拟TerminatedSet()方法,您可以覆盖该方法以在调用hTermEvent时发出信号。TThread.Terminate()否则,就SetEvent()调用后调用Terminate()。
| 归档时间: |
|
| 查看次数: |
3674 次 |
| 最近记录: |