2012-07-18 74 views
1

我目前正在嘗試使用Indy在TCP之上編寫一個自定義更高級別的協議。本質上,我想跨越(使用流)發送一條記錄,但是這條記錄可以表明要跟隨的x個字節將是一個圖像文件。因此,當記錄在服務器上被接收時,如果它的內容指示它後面是圖像,則它需要分離記錄和圖像數據。Delphi:(Indy)發送一個頭文件後跟一個文件

var 
Segment: TDPPSegment; 
Segment2: TDPPSegment; 
Buffer: TIdBytes; 
Buffer2: TIdBytes; 
Mem: TMemoryStream; 
begin 
if (Client.Connected) then begin 

Segment.NameStr := 'Adrian'; 
Segment2.NameStr := 'Jon'; 

Mem := TMemoryStream.Create; 
Mem.Write(Segment, SizeOf(Segment)); 
Mem.Write(Segment2, SizeOf(Segment2)); 

//The Size of the stream is 8 bytes here! 
Client.IOHandler.Write(Mem, 0, False); 

end;

要模擬'圖像文件'我只想連續發送兩個記錄;這就是目標。注意這裏我一次發送整個內存流(!)而不是通過記錄進行記錄。但有趣的是,服務器運行兩次OnExecute事件!

var 
Buffer: TIdBytes; 
Segment: TDPPSegment; 
Mem: TMemoryStream; 
begin 
Mem := TMemoryStream.Create; 
AContext.Connection.IOHandler.ReadStream(Mem, SizeOf(TDPPSegment), False); 

//Incoming stream size is FOUR bytes but TWICE! 
Mem.Position := 0; 
Mem.Read(Segment, SizeOf(TDPPSegment)); 
Showmessage(Segment.NameStr); 

我希望它執行一次,所以在未來我可以先讀取文件頭記錄(已知大小),然後看看有什麼/如果有什麼是要注意和採取相應的行動..

幫助會非常感謝!

感謝您的閱讀, 阿德里安

+0

或者,在記錄(或類似的東西?)中放置一個Byte數組來存儲圖像是一種更好的選擇?問題是標題需要不斷地是相同的大小,並且作爲圖像不同,我需要一些方式來允許它.. – Adrian 2012-07-18 13:05:32

+0

我會去序列化記錄和數據到XML,壓縮和發送,有許多高性能XML組件在那裏,我的首選是omnixml http://code.google.com/p/omnixml/ – ComputerSaysNo 2012-07-18 13:10:35

+0

我會問你在兩個OnExecute事件中收到了哪些數據,因爲這會給出關於發生什麼事的線索開 - 你不應該得到兩個 - 但是因爲你錯誤地發送了字符串數據,所以很難相信你在接收端看到的內容。只能通過發送包含記錄來發送字符串。 (考慮一下:你至少有* 9個字節的字符數據,但是你注意到流的大小是8.)對於你的初始測試,例如,忘記字符串並使用只保存整數的記錄類型。把兩個這樣的記錄放在流上,發送它,看看你收到了什麼。 – 2012-07-18 13:49:38

回答

0

你的記錄包含在其中非ShortString短字符串,由一個事實,即由字符數據佔用的字節數大於字節數較大的被寫入爲明顯流 - 意味着指針值(2 x SizeOf(指針))正在寫入流而不是實際字符。因此,您將不得不手動序列化您的字符串,例如通過發送字符串長度後跟實際字符。如果您計劃支持Delphi 2009+,則必須考慮Unicode,因此您應在傳輸字符串之前對其進行編碼,然後在接收端對其進行解碼。

記錄對於在內存中組織數據非常有用,但通常不太適合通過網絡傳輸數據,除非記錄僅包含POD類型(並且字符串不符合該條件)。

試試這個:

procedure WriteStrToIO(IO: TIdIOHandler; const S: String); 
var 
    Buf: TIdBytes; 
    Len: Integer; 
begin 
    Buf := ToBytes(S, IndyUTF8Encoding); 
    Len := Length(Buf); 
    IO.Write(Len); 
    if Len > 0 then IO.Write(Buf); 
end; 

var 
    Len: Integer; 
    Buf: TIdBytes; // or whatever you want to use... 
begin 
    if Client.Connected then 
    begin 
    WriteStrToIO(Client.IOHandler, 'Adrian'); 

    Buf := ...; // secondary data 
    Len := Length(Buf); 
    Client.IOHandler.Write(Len); 
    if Len > 0 then 
     Client.IOHandler.Write(Buf); 

    ... 
    end; 
end; 

var 
    NameStr: String; 
    Buf: TIdBytes; 
begin 
    with AContext.Connection.IOHandler do 
    begin 
    NameStr := ReadString(ReadInteger, IndyUTF8Encoding); 

    // read secondary data ... 
    ReadBytes(Buf, ReadInteger); 
    end; 

    // ShowMessage() is not thread-safe! 
    Windows.MessageBox(0, PChar(NameStr), 'NameStr', MB_OK); 
end; 

至於OnExecute事件被多次觸發,這是正常行爲。該事件根本不依賴於數據傳輸。它在連接的整個生命週期中被連續循環調用。一旦您退出事件處理程序,它將在客戶端仍連接時立即再次觸發。這對於像你的基於消息的協議非常有用,事件被觸發,讀取單個消息,等待數據到達,退出,再次觸發以讀取下一條等待數據到達的消息,等等。

相關問題