我有一個偵聽 tcp 客戶端連接的應用程式。當客戶端連接時,客戶端發送一大塊資料,在一個例子中是 100 k。Indy TCPServer 接收它,處理/重新格式化資料,將其發送到云中的 http 服務器,接收回應,根據回應創建確認,然后將其發送回客戶端,斷開連接。
procedure TMyApp.TCPConnect(AContext: TIdContext);
begin
try
AContext.Connection.Socket.ReadTimeout := 10000;
except
on E:Exception do
AContext.Connection.Disconnect;
end;
end;
procedure TMyApp.TCPDisconnect(AContext: TIdContext);
begin
SetLength(FMDMStr,0);
end;
function TMyApp.TransferTCPBytesToString(AnIOHandler: TIdIOHandler): String;
begin
if AnIOHandler.InputBufferIsEmpty then
Exit;
Result := AnIOHandler.WaitFor(#28#13,True,True);
if Pos(#28#13,Result) = 0 then
Result := Result #28#13;
end;
procedure TMyApp.WriteTCPResponse(AnIOHandler: TIdIOHandler);
var
bytes: TIdBytes;
begin
BuildACK(FMDMStr,bytes);
AnIOHandler.WriteDirect(bytes, Length(bytes));
end;
procedure TMyApp.TCPExecute(AContext: TIdContext);
begin
try
FMDMStr := TransferTCPBytesToString(AContext.Connection.IOHandler);
// test string for HL/7 terminating characters
if Pos(#28#13, FMDMStr) > 0 then
begin
FormatAndSendHTTPMsg(FMDMStr);
// send the response back to tcp client
WriteTCPResponse(AContext.Connection.IOHandler);
end;
except
on E:Exception do
AContext.Connection.Disconnect;
end;
end;
我已經在不同的場景中使用了這個應用程式超過 2 年,它運行良好。我已經在多個 Windows 10 和 Windows Server 2016 SE 服務器上對其進行了測驗,并且運行良好。
最近的部署根本不起作用;同一個客戶端發送超過 100k 的資料,連接到 TIdTcpServer;execute 方法觸發,但有一個 -0- 大小的輸入緩沖區。為了安全起見,我已將接收緩沖區設定為 256k。這是我正在記錄的內容:
AContext.Connection.IOHandler.InputBufferAsString
AContext.Connection.IOHandler.RecvBufferSize
AContext.Connection.IOHandler.InputBufferIsEmpty
ReceiveBuffer 記錄為 262144。 InputBufferIsEmpty 總是回傳 true!當然還有 InputBufferAsString := ''。
所以我想知道這是否可能是應用程式運行所在的服務器/網路/域上的安全“功能”?我已經在域中的兩臺不同服務器上對其進行了測驗,結果完全相同。客戶端連接,記錄它正在發送,IdTcpServer 記錄連接,但什么也沒收到。
任何想法或建議將不勝感激!TIA
uj5u.com熱心網友回復:
該try..except中TCPConnect()是無用的。設定ReadTimeout不會引發例外。并且在任何情況下,如果未捕獲的例外逃脫了OnConnectorOnExecute事件,服務器將自動斷開與客戶端的連接。
FMDMStr沒有以執行緒安全的方式使用。每個都TIdContext在自己的執行緒中運行。不要在沒有適當同步的情況下跨執行緒邊界共享變數。在這種情況下,如果只有 1 個客戶端連接(并且您通過設定來強制執行TIdTCPServer.MaxConnections=1),那么就這樣吧。否則,FMDMStr根本不應該是類成員變數,而應該是 的區域變數TCPExecute()(或者,存盤在 中TIdContext),以便每個連接的客戶端都對自己唯一的 進行操作string。
在InputBufferIsEmpty辦理入住手續TransferTCPBytesToString()需要被移除。Indy 不會填充InputBuffer直到它被告知執行讀取操作,其中需要從底層套接字中提取新位元組。因此,在InputBuffer嘗試從連接中讀取內容之前,它將保持為空。如果您總是先檢查空值InputBuffer,則可能會失去讀取資料的機會。讓我們WaitFor()正常阻塞直到資料到達或超時。
此外,由于您正在設定AInclusive=True, 的回傳值WaitFor()將始終包含#28#13在末尾,因此Pos()檢查無用,應洗掉。此外,由于TransferTCPBytesToString()總是#28#13在末尾回傳一個字串,因此Pos()簽入TCPExecute()也是無用的。
在 中WriteTCPResponse(),您TIdIOHandler.WriteDirect()根本不應該使用。改用適當的TIdIOHandler.Write()多載。發送過載TIdBytes。
話雖如此,請嘗試更像這樣的事情:
procedure TMyApp.TCPConnect(AContext: TIdContext);
begin
AContext.Connection.IOHandler.ReadTimeout := 10000;
end;
function TMyApp.TransferTCPBytesToString(AnIOHandler: TIdIOHandler): String;
begin
// will throw an exception on timeout...
Result := AnIOHandler.WaitFor(#28#13,True,True);
// alternatively:
// Result := AnIOHandler.WaitFor(#28#13,True,True,nil,10000);
// then you don't need to set the IOHandler.ReadTimeout...
end;
procedure TMyApp.WriteTCPResponse(AnIOHandler: TIdIOHandler; const AMsg: string);
var
bytes: TIdBytes;
begin
BuildACK(AMsg, bytes);
AnIOHandler.Write(bytes);
end;
procedure TMyApp.TCPExecute(AContext: TIdContext);
var
FMDMStr: string;
begin
FMDMStr := TransferTCPBytesToString(AContext.Connection.IOHandler);
// TODO: change FormatAndSendHTTPMsg() to return the response string
// directly, rather than save it in a class member variable...
FMDMStr := FormatAndSendHTTPMsg(FMDMStr);
// send the response back to tcp client
WriteTCPResponse(AContext.Connection.IOHandler, FMDMStr);
end;
僅供參考,附帶說明,您的代碼提到了 HL/7。Indy 有一個TIdHL7可以在服務器模式下運行的組件,運行一個內部TIdTCPServer來讀取和回應 HL/7 訊息,OnReceiveMessage為每條訊息觸發一個事件。在您的場景中,您可以設定TIdHL7.Port所需的偵聽埠、 set TIdHL7.CommunicationMode=cmSynchronous、 set TIdHL7.isListener=True、分配處理程式TIdHL7.OnReceiveMessage以發送您的 HTTP 訊息,然后TIdHL7.Start()在準備好時在運行時呼叫。
只是需要考慮的事情...
uj5u.com熱心網友回復:
首先,默認的 Windows Indy 套接字緩沖區大小為 32 KB。因此,當您想將所有內容全部發送時(我不喜歡這樣做),請使您發送的資料包更小。因此,您也可以更快地檢查您的連接是否存在超時或其他缺點。BTW 問題:您使用什么版本的 Indy - 版本 9 或 10?
其次,檢查您讀/寫“請求”和“回應”的服務器/客戶端代碼 - 當您處于文本模式時,讀取函式應以 \r\n 結尾。
第三,檢查您是否有非阻塞或阻塞的服務器代碼。這意味著 - 您應該為每個連接安裝一個 TThread 以正確處理所有連接(可能具有其他屬性)。因此,連接不會與現有的先前連接重疊。
第四(對于高級開發人員):您應該考慮,為每個人建立一個“第二個”連接 - 這可以幫助保持線路(跳躍......
第五:(對于高級開發人員):您應該考慮安全方面。這意味著,您應該為您的連接使用 SSL 證書(Indy 提供了簡單的單擊,并為此編輯組件。您的任務就是創建“自簽名證書”(PuttyGen(在 Windows 上)或在 Linux 上 - 請參閱谷歌了解詳細資訊) ) - 如果您擁有公共證書,則不需要此步驟 - 看看“Lets Encrypt”(公共免費 SSL 授權,但請注意:您必須每三個月更新一次開發者 SSL 證書(certbot 腳本)幫助你做所有這些事情)。
如果您有問題,請隨時提出,我會盡力幫助您。
圣誕快樂
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/386637.html
