N. McA.
N. McA.

Reputation: 4946

Indy TCP communications and line breaks

I have an error that is confusing the hell out of me. The following code sends a line from a TidTCPClient to a TidTCPServer. The first time it executes, it works perfectly. The second time it executes, and every time after that, it adds a line break to the start of every string. What am I missing? (I know it does it in an odd way, but the list of clients is necessary in the full code)

   procedure TClientForm.ButtonSendStringClick(Sender: TObject);
    var
      I: integer;
      List: TList;
    begin
      List := ClientList.LockList;
      try
       for I := 0 to (List.Count- 1) do
        begin
          TidTCPClient(List[I]).IOHandler.WriteLn('Hello'+'|x|');
        end;
      finally
       ClientList.UnlockList;
      end;
      Edit1.Text := '';
    end;

    procedure TClientForm.IdTCPServer1Execute(AContext: TIdContext);
    var
    LLine: string;
    begin
    LLine := Acontext.Connection.IOHandler.ReadLn('|x|');
    OutputDebugString(PChar(LLine));
    end;

Upvotes: 2

Views: 2551

Answers (2)

Remy Lebeau
Remy Lebeau

Reputation: 597036

WriteLn() appends a CRLF to the end of the string you pass to it, but ReadLn() stops reading when it encounters the terminator string that you specify. So you are sending 'Hello|x|#13#10' but you are only reading 'Hello|x|', leaving #13#10 in the socket buffer for the next read to grab.

To solve your problem, you have two choices:

1) If you want to keep using a custom terminator in ReadLn(), change WriteLn() to Write() so the implicit CRLF is not sent anymore. No change is needed in your ReadLn() call.

Write('Hello|x|');
LLine := ReadLn('|x|');

2) Stop using a custom terminator altogether. Pass just your main string by itself to WriteLn() and let it append a CRLF, then do not pass any terminator to ReadLn() as its default terminator is LF (which includes handling for CRLF).

WriteLn('Hello');
LLine := ReadLn();

Upvotes: 3

whosrdaddy
whosrdaddy

Reputation: 11859

I see a potential problem in your client code, ClientList.unlocklist should be executed outside your loop:

procedure TClientForm.ButtonSendStringClick(Sender: TObject);
var
  I: integer;
  List      : TList; 
begin     
  List := ClientList.LockList;
  try
   for I := 0 to (List.Count- 1) do
    begin
      ShowMessage('Text to encrypt is ' + Edit1.Text);
      TidTCPClient(List[I]).IOHandler.WriteLn((Encrypt(Edit1.Text,'Pass')+'|x|'));      
    end;
  finally
   ClientList.UnlockList;
  end;
  Edit1.Text := '';
end;

Do not use showmessage in the execute event of your server, since this is not the main GUI thread you must use synchronize or use another logging method. An other potential problem is the fact that your encrypted string may contain carriage return characters or even null characters (#0) in those cases the Readln method of your server will be unable to read the entire encrypted string and so fail to decrypt. One way to resolve this is to use base64 encoding.

EDIT

The problem lies in the fact that you are using the '|x|' terminator in combination with ReadLn. Since you are using writeln from the client side you can do this

procedure TClientForm.IdTCPServer1Execute(AContext: TIdContext);
var
 LLine: string;
begin
 LLine := Acontext.Connection.IOHandler.ReadLn;
 OutputDebugString(PChar(LLine));
end;

If you want to use a terminator don't use writeln and readln

Upvotes: 2

Related Questions