delphiindytstream

Delphi idtcpserver idtcpclient tstringstream to string


I try to send a string from a tidtcpserver to a tidtcpclient in Delphi, but when i send a string the client receives nothing. I don't get any errors. I am using tstringstream because i want to send a base64 string(writeln/readln can't send/receive that much text) This is the send code in idtcpserver1:

procedure TForm1.sendtext(index:integer; txt:string);
var
  StrStream:tStream;
  List: TList;
  AContext: TIdContext;
begin
  txt := trim(txt);
  strstream := TMemoryStream.Create;
  strstream.Write(PAnsiChar(txt)^, Length(txt));
  strstream.Position := 0;
  List := idTcpServer1.Contexts.LockList;
  AContext := TIdContext(List[index]);
  AContext.Connection.IOHandler.write(StrStream);
  idTcpServer1.Contexts.UnLockList;
end;

This is the read code in idtcpclient1

procedure TIndyClient.Execute;
var
StrStream:tStringStream;
receivedtext:string;
begin
StrStream := tstringstream.Create;
form1.IdTCPClient1.IOHandler.readstream(StrStream);
receivedtext := strstream.DataString;
if receivedtext = '' = false then
begin
  showmessage(receivedtext);
end;
end;

Does anyone know what i am doing wrong?


Solution

  • You are making a very common newbie mistake of mismatching the IOHandler.Write(TStream) and IOHandler.ReadStream() calls. By default, ReadStream() expects the stream data to be preceded by the stream size, but Write(TStream) does not send the stream size by default.

    Try this instead:

    procedure TForm1.sendtext(index: integer; const txt: string);
    var
      StrStream: TMemoryStream;
      List: TList;
      AContext: TIdContext;
    begin
      strStream := TMemoryStream.Create;
      try
        WriteStringToStream(StrStream, Trim(txt), enUTF8);
        List := idTcpServer1.Contexts.LockList;
        try
          AContext := TIdContext(List[index]);
          AContext.Connection.IOHandler.Write(StrStream, 0, True);
        finally
          idTcpServer1.Contexts.UnlockList;
        end;
      finally
        StrStream.Free;
      end;
    end;
    

    procedure TIndyClient.Execute;
    var
      StrStream: TMemoryStream;
      receivedtext: string;
    begin
      StrStream := TMemoryStream.Create;
      try
        Form1.IdTCPClient1.IOHandler.ReadStream(StrStream, -1, False);
        StrStream.Position := 0;
        receivedtext := ReadStringFromStream(StrStream, enUTF8);
      finally
        StrStream.Free;
      end;
      if receivedtext <> '' then
      begin
        // ShowMessage() is not thread-safe...
        MessageBox(0, PChar(receivedtext), PChar(Application.Title), MB_OK);
      end;
    end;
    

    Alternatively, because the stream size is now being sent, the receiving code can use ReadString() instead of ReadStream():

    procedure TIndyClient.Execute;
    var
      receivedtext: string;
    begin
      with Form1.IdTCPClient1.IOHandler do
      begin
        // you can alternatively set the IOHandler.DefStringEncoding
        // instead of passing the encoding as a parameter...
        receivedtext := ReadString(ReadLongInt, enUTF8);
      end;
      if receivedtext <> '' then
      begin
        // ShowMessage() is not thread-safe...
        MessageBox(0, PChar(receivedtext), PChar(Application.Title), MB_OK);
      end;
    end;
    

    BTW, WriteLn()/ReadLn() do not have any length restrictions, they can handle big strings as long as there is available memory (and the text being sent does not have any line breaks in it). You are probably being confused by ReadLn() being subject to the IOHandler.MaxLineLength, which is set to 16K characters by default, but you can bypass that:

    procedure TForm1.sendtext(index: integer; const txt: string);
    va
      List: TList;
      AContext: TIdContext;
    begin
      List := idTcpServer1.Contexts.LockList;
      try
        AContext := TIdContext(List[index]);
        // you can alternatively set the IOHandler.DefStringEncoding
        // instead of passing the encoding as a parameter...
        AContext.Connection.IOHandler.WriteLn(Trim(txt), enUTF8);
      finally
        idTcpServer1.Contexts.UnlockList;
      end;
    end;
    

    procedure TIndyClient.Execute;
    var
      receivedtext: string;
    begin
      // you can alternatively set the IOHandler.DefStringEncoding
      // and IOHandler.MaxLineLength instead of passing them as parameters...
      receivedtext := Form1.IdTCPClient1.IOHandler.ReadLn(LF, -1, MaxInt, enUTF8);
      if receivedtext <> '' then
      begin
        // ShowMessage() is not thread-safe...
        MessageBox(0, PChar(receivedtext), PChar(Application.Title), MB_OK);
      end;
    end;