Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
TIdCmdTCPServer customised Reply
#1
I try to implement a very simple set-up. An IdCmdTCPServer and an IdTCPClient. From the Client I send a Cmd and in the Reply I want to get a value from the server.
On the client side I have (properly built it Try..Except, etc, what I do not include here):

Code:
Log(IntToStr(IdTCPClient1.SendCmd('AAA 1',[200])));
Log(IdTCPClient1.LastCmdResult.DisplayName);

where Log is a procedure to display a string.

On the server side I have (again properly built around):

Code:
procedure TMyIdCmdTCPServer.OnCommandAAA(ASender: TIdCommand);
begin
  Log('Raw Line ' + ASender.RawLine);
  ASender.Reply.Text.Text := 'DT= ' + DateTimeToStr(Now);
end;

After connecting I send the comand from the client. What I get is "200 Welcome", what is obviously the default welcome message. If I send the Cmd again, from the same connect-disconnect session, then I get the proper DT= reply, with the Time of the PREVIOUS call. I can repeat it many times. So it is clear that the Reply.Text is set, but only after the Reply is sent to the client. At the second Cmd it sends the Reply set at the first Cmd, etc.

If I disconnect and connect again, then it forgets the set Reply and goes back to the Welcome message (this part I feel normal).

Similarly if I send an unknown command multiple times, the first Reply is still 200 Welcome and only the second gives the unknown command error.

What do I do wrong? How can I customise the Reply AFTER the Cmd is received (so i know what the question is) but BEFORE it is sent back to the client?
Reply
#2
(01-24-2019, 01:07 PM)jollytall Wrote: On the client side I have (properly built it Try..Except, etc, what I do not include here):

Code:
Log(IntToStr(IdTCPClient1.SendCmd('AAA 1',[200])));
Log(IdTCPClient1.LastCmdResult.DisplayName);

where Log is a procedure to display a string.

Note, in the above code, if SendCmd() receives any reply code other than 200, it will raise an exception, skipping your logging. If you don't want that to happen (or if you at least want to log the reply code before raising the exception) then don't specify any reply codes to SendCmd() and it will return the actual reply code received. You can use TIdTCPConnection.CheckResponse() afterwards (amongst others) to trigger the exception manually if needed:

Code:
IdTCPClient1.SendCmd('AAA 1');
Log(IdTCPClient1.LastCmdResult.DisplayName);
IdTCPClient1.CheckResponse(IdTCPClient1.LastCmdResult.NumericCode, [200]);

Otherwise, consider using the TIdTCPClient.Intercept property instead to log your command/response traffic. Indy has several TIdLog... components for intercepting traffic, such as TIdLogEvent, for instance:

Code:
// these can all be set at design-time, too...
IdTCPClient1.Intercept := IdLogEvent1;
IdLogEvent1.OnReceived := IdLogEvent1Received;
IdLogEvent1.Active := True;
...
IdTCPClient1.SendCmd('AAA 1', [200]);
...
procedure TMyForm.IdLogEvent1Received(ASender: TComponent; const AText, AData: string);
begin
  Log(AData);
end;

(01-24-2019, 01:07 PM)jollytall Wrote: On the server side I have (again properly built around):

Code:
procedure TMyIdCmdTCPServer.OnCommandAAA(ASender: TIdCommand);
begin
 Log('Raw Line ' + ASender.RawLine);
 ASender.Reply.Text.Text := 'DT= ' + DateTimeToStr(Now);
end;

You are not setting a reply code in that handler, so the NormalReply property of the TIdCommandHandler of the 'AAA' command will be used by default, if you have defined a reply code for it. Which is perfectly fine, I'm just pointing it out.

(01-24-2019, 01:07 PM)jollytall Wrote: After connecting I send the comand from the client. What I get is "200 Welcome", what is obviously the default welcome message.

Yes, that is the default greeting for TIdCmdTCPServer. Many Internet protocols have server greetings, so a client can verify that it is connected to the proper type of server, amongst other things, before continuing.

If you do not want your server to send a greeting, simply clear the contents of the TIdCmdTCPServer.Greeting property (or, at least set the Greeting.Code property to a blank string) so there is nothing for the server to send. You will have to do this at run-time, though, eg:

Code:
procedure TMyForm.FormCreate(Sender: TObject);
begin
  IdCmdTCPServer1.Greeting.SetReply('', '');
end;

Or:

Code:
procedure TMyForm.FormCreate(Sender: TObject);
begin
  IdCmdTCPServer1.Greeting.Code := '';
end;

If you try to clear the Greeting at design-time, it won't preserve correctly when the DFM is streamed, so the default '200 Welcome' will re-appear (I'll have to look into that).

Otherwise, the client will need to read the greeting before sending any commands. You can use TIdTCPClient.GetResponse() for that:

Code:
IdTCPClient1.Connect;
IdTCPClient1.GetResponse(200); // read greeting
IdTCPClient1.SendCmd('AAA', [200]);

(01-24-2019, 01:07 PM)jollytall Wrote: If I send the Cmd again, from the same connect-disconnect session, then I get the proper DT= reply, with the Time of the PREVIOUS call.

Yes, because that earlier reply ends up sitting unread in the socket's inbound buffer until the next SendCmd() reads it (leaving THAT reply in the buffer for the next SendCmd() to read).

(01-24-2019, 01:07 PM)jollytall Wrote: So it is clear that the Reply.Text is set, but only after the Reply is sent to the client.

Not true. The reply is sent to your client as soon as your OnCommand handler exits. You are simply not taking into account that the server's initial greeting is being sent first and your client is reading the server's responses in the wrong order because of it.

(01-24-2019, 01:07 PM)jollytall Wrote: What do I do wrong? How can I customise the Reply AFTER the Cmd is received (so i know what the question is) but BEFORE it is sent back to the client?

You are not doing anything wrong on the server side. It is your client side that is at fault, by not reading the server's greeting properly. So, either read the greeting, or disable it. Then your code will behave properly as expected.

Reply
#3
Thanks a lot. Indeed that was the problem. Now it works as expected.

Thanks for your other comments too.
Re your point of setting the Reply code: Actually I intentionally keep 200 (being "OK"), and only change the text after it to what I want to get. Ideally it shall be "OK<CR><LF>actual response". In the example I have sent I just simplified.
Re your point of Logging vs. Except. Yes, I am aware that Log() is not called if an exception is raised. I have a different (higher severity) Log in the except part too (if OK, I only log in a file, but if an excdeption is raised then I show it on the console too). Again, the example was simplified.
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)