我有一個應用程式,它從另一個應用程式接收 TCP 套接字連接,處理通過套接字流式傳輸的內容,根據接收到的資料構建請求,使用 TIdHTTP 將請求發送到外部服務器,處理回應,然后發送一個確認回到 tcp 客戶端。
procedure TMyClass.TCPExecute(AContext: TIdContext);
var
s: AnsiString;
begin
s := TransferTCPBytesToString(AContext.Connection.IOHandler);
// test string for terminating characters
if Pos(#28#13, s) > 0 then
begin
//********PROBLEM: THIS WILL BE CALLED MULTIPLE TIMES
//********WITH THE SAME COMPLETE DATA STREAM CONTENT!
BuildAndSendExternalRequest(s);
AContext.Connection.IOHandler.InputBuffer.Clear;
// send the response back to tcp client
WriteTCPResponse(AContext.Connection.IOHandler);
end;
end;
function TMyClass.TransferTCPBytesToString(AnIOHandler: TIdIOHandler): AnsiString;
begin
if AnIOHandler.InputBufferIsEmpty then
Exit;
// read message from tcp client connection
AnIOHandler.ReadBytes(FTCPBytes, AnIOHandler.InputBuffer.Size, True);
SetLength(Result, Length(FTCPBytes));
// copy message bytes to string
Move(FTCPBytes[0], Result[1], Length(FTCPBytes));
end;
procedure TMyClass.WriteTCPResponse(AnIOHandler: TIdIOHandler);
var
bytes: TBytes;
begin
// FTCPBytes contains data from tcp client
// bytes will contain processed response from external server
BuildACK(FTCPBytes,bytes);
// FTCPBytes now contains ACK data
AnIOHandler.WriteDirect(FTCPBytes, Length(FTCPBytes));
end;
請求/回應周期成功完成;發送回 tcp 客戶端請求者的回應是正確的,行程按預期執行和完成。
但是,由于 OnExecute 事件使用相同的資料多次觸發,因此會接收、構建、發送、確認兩個請求。
在查看 tcp 客戶端應用程式上的日志時,日志僅顯示正在發送的一個請求。不知道這里發生了什么。但是我需要確保請求/回應周期是 1-1,而不是 1-很多!
謝謝。
uj5u.com熱心網友回復:
OnExecute 事件多次觸發...
它應該是,因為它是一個回圈事件。只要連接處于活動狀態,回圈就會運行。OnExecute事件處理程式中的代碼負責決定每次回圈迭代中發生的事情。
在理想的情況是在事件處理程式從客戶端接收1條訊息,發送回應1回傳到客戶端,然后退出,從而使下一個回圈迭代準備處理下一個請求/回應對。
根據您的描述,您正在嘗試這樣做,但顯示的代碼處理得不是很好。最值得注意的是,該代碼沒有考慮 TCP 的位元組流性質。它不區分一個傳入的訊息和另一個。它只是轉儲全部IOHandler.InputBuffer內容,是成AnsiString(為何AnsiString?),并且如果AnsiString碰巧包含分隔符#28#13,那么你正在處理的整個AnsiString,然后把它扔了,失去了分隔符后收到的(東西即未來的一部分資訊)。如果AnsiString不包含分隔符,那么您仍然會丟棄AnsiString,從而在分隔符到達之前丟失當前訊息到目前為止收到的所有資料。
如果您的所有訊息都以 分隔#28#13,那么我建議您使用IOHandler.ReadLn()或IOHandler.WaitFor()方法正確讀取每條訊息。
此外,您正在FTCPBytes為輸入和輸出重新使用相同的成員,這本身是錯誤的,但如果存在多個客戶端連接,您也不會為其提供任何并發訪問保護。
話雖如此,請嘗試更像這樣的事情:
procedure TMyClass.TCPConnect(AContext: TIdContext);
begin
AContext.Connection.IOHandler.DefStringEncoding := IndyTextEncoding_8Bit;
end;
procedure TMyClass.TCPExecute(AContext: TIdContext);
var
s: String;
response: TIdBytes:
begin
s := TransferTCPBytesToString(AContext.Connection.IOHandler);
BuildAndSendExternalRequest(s, response);
// send the response back to tcp client
WriteTCPResponse(AContext.Connection.IOHandler, response);
end;
function TMyClass.TransferTCPBytesToString(AnIOHandler: TIdIOHandler): String;
begin
// read message from tcp client connection
Result := AnIOHandler.ReadLn(#28#13);
or:
Result := AnIOHandler.WaitFor(#28#13);
end;
procedure TMyClass.BuildAndSendExternalRequest(const Msg: String; var Response: TIdBytes);
begin
// send Msg to external server as needed...
// store response bytes in Response...
end;
procedure TMyClass.WriteTCPResponse(AnIOHandler: TIdIOHandler; const Response: TIdBytes);
var
bytes: TIdBytes;
begin
// Response contains processed response from external server
BuildACK(bytes, Response);
// bytes now contains ACK data
AnIOHandler.Write(bytes);
end;
...具有相同的資料
That is not possible given the code you have shown. The call to IOHandler.ReadBytes() in TransferTCPBytesToString() is reading and discarding all bytes from the IOHandler.InputBuffer, so there is no way the same bytes can still be in that buffer when TransferTCPBytesToString() is called again at a later time. So, the only way you could be seeing the same data over and over is if the client is sending the same data over and over to begin with.
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/340215.html
