1

現在、Indy を使用して、TCP の上にカスタムの高レベル プロトコルを作成しようとしています。基本的に、(ストリームを使用して) レコードを送信したいのですが、このレコードは、続く x バイトが画像ファイルになることを示している可能性があります。

そのため、レコードがサーバーで受信されたときに、その内容が画像が続くことを示している場合は、レコードと画像データを分離する必要があります。

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);

終わり;

「画像ファイル」をシミュレートするには、2 つのレコードを連続して送信するだけです。それが狙いです。ここで、メモリ ストリーム全体を一度に (!) 送信しており、レコードごとに送信していないことに注意してください。しかし興味深いことに、サーバーは OnExecute イベントを 2 回実行します。

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);

一度実行したいので、将来は最初にヘッダーレコード(既知のサイズ)を読み取ってから、何が続くか/何かが続くかを確認し、それに応じて行動することができます..

助けていただければ幸いです。

読んでくれてありがとう、エイドリアン

4

1 に答える 1

0

文字データが占めるバイト数がストリームに書き込まれるバイト数よりも大きいという事実から明らかなように、レコードには ShortString 以外の文字列が含まれています。これは、ポインター値 (2 x SizeOf(Pointer)) を意味します。実際の文字の代わりにストリームに書き込まれています。そのため、文字列の長さに続いて実際の文字を送信するなど、文字列を手動でシリアル化する必要があります。また、Delphi 2009 以降のサポートを計画している場合は、Unicode を考慮する必要があるため、文字列を送信する前にエンコードし、受信側でデコードする必要があります。

レコードは、メモリ内のデータを整理するのに役立ちますが、レコードに POD タイプのみが含まれている場合 (および文字列が 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イベントが複数回トリガーされるのは、正常な動作です。そのイベントは、データ送信とはまったく関係ありません。接続の存続期間中、連続ループで呼び出されます。イベント ハンドラーを終了すると、クライアントがまだ接続されている場合は、すぐに再びトリガーされます。これは、イベントがトリガーされ、データが到着するのを待っている単一のメッセージを読み取り、終了し、データが到着するのを待っている次のメッセージを読み取るために再度トリガーされる、などのメッセージベースのプロトコルに役立ちます。

于 2012-07-18T17:16:31.440 に答える