I'm using Delphi XE7. I've never used Indy components before.
I found a very good tutorial from Embarcadero called Developing TCP/IP-based Server Applications using Indy Components. It shows a very good example using TidCmdTCPServer
and Command Handlers.
My problem is I did not understand how to construct a command. I couldn't find it in help files neither Indy homepage.
See the code below:
procedure TMyServer.InitializeCommandHandlers;
var
NewCmd: TIdCommandHandler;
begin
NewCmd := FCommandHandlers.Add;
NewCmd.Command := 'HEARTBEAT'; { Do not Localize }
NewCmd.OnCommand := CommandHEARTBEAT;
NewCmd.ExceptionReply.NumericCode := 550;
NewCmd.Description.Text := 'Syntax: HEARTBEAT'; { do not localize }
NewCmd.Disconnect := False;
NewCmd := FCommandHandlers.Add;
NewCmd.Command := 'COLOR'; { Do not Localize }
NewCmd.CmdDelimiter := #$20;
NewCmd.ParamDelimiter := '|';
NewCmd.OnCommand := CommandCOLOR;
NewCmd.ExceptionReply.NumericCode := 550;
NewCmd.Description.Text := 'Syntax: COLOR <sp> "GET | [SET" | color-"blue | red | yellow]"'; { do not localize }
NewCmd.Disconnect := False;
end;
The first command HEARTBEAT
is fairly easy, but the COLOR
command is not.
I didn't understand the line:
NewCmd.Description.Text := 'Syntax: COLOR <sp> "GET | [SET" | color-"blue | red | yellow]"'; { do not localize }
Can anyone explain how it is constructed? Or show me a document where I can learn this?
What "do not localize" means?
The syntax should have been written using Augmented Backus-Naur Form (BNF), similar to that defined in RFC 822, Section 2 - NOTATIONAL CONVENTIONS.
COLOR
is the command name, obviously.
<sp>
is a space character.
Quotes mean literal strings that appear as-is.
In this case, |
is being used as a delimiter between parameters in the transmitted data, and as such should have been in quotes.
[]
in this case is being used to group logical items together. However, the brackets appear inside of quotes, which is wrong because they do not actually appear in the transmission.
color-
is this case is not actually transmitted, either, and should not have been in the syntax either.
So, based on the actual code provided in the article, the server will accept the following commands (case insensitive):
HEARTBEAT
COLOR GET
COLOR SET|BLUE
COLOR SET|RED
COLOR SET|YELLOW
Which you can clearly see in the article's client code:
IdTCPClient1.Socket.WriteLn('HEARTBEAT');
IdTCPClient1.IOHandler.WriteLn('COLOR GET');
case RadioGroup1.ItemIndex of
0: IdTCPClient1.IOHandler.WriteLn('COLOR SET|BLUE');
1: IdTCPClient1.IOHandler.WriteLn('COLOR SET|RED');
2: IdTCPClient1.IOHandler.WriteLn('COLOR SET|YELLOW');
end;
The correct syntax should thus be defined as follows:
NewCmd.Description.Text := 'Syntax: COLOR <sp> ("GET" / ("SET" "|" ("BLUE" / "RED" / "YELLOW")))'; { do not localize }
The { do not localize }
is a hint to automated internationalization tools and translators that the line contains string literals that need to be left as-is and not localized to any particular language.
That being said, the article also mentions this:
Since we have chosen to use TidCmdTCPServer we cannot simply put one on a form. We must instead descend our own class base on TidCmdTCPServer and add all of our custom behavior to it. This was even the prefered way to go even when TidTCPServer supported command handlers.
This is simply not true at all. The TIdCmdTCPServer.CommandHandlers
collection (TIdTCPServer.CommandHandlers
in Indy 9) is available at design-time and is fully editable using the Object Inspector and its built-in collections editor, including assigning per-command event handlers. THAT is the preferred way to use it, and always has been. The only reason to derive a custom component is if you need to modularize the command handling code to make it reusable (Indy has a few TIdCmdTCPServer
derived components of its own). Otherwise, deriving and installing a new component is overkill.