Recent

Author Topic: Q: System code for Indy Sockets, extra parameters/values from server to client.  (Read 1960 times)

pascalbythree

  • Sr. Member
  • ****
  • Posts: 255
Hey to Freepascal Forum Members,

TIdCmdTCPServer code:

Code: Pascal  [Select][+][-]
  1. //******************************************************************************
  2.     NewCmd                            := IdCmdTCPServer.CommandHandlers.Add;
  3.     NewCmd.Command                    := '[SYSTEM_ERASE]';
  4.     NewCmd.OnCommand                  := @WVW_HANDLER_SYSTEM_ERASE;
  5.     NewCmd.ExceptionReply.NumericCode := 550;
  6.     NewCmd.Description.Text           := 'Erase MCU';
  7.     NewCmd.Disconnect                 := False;
  8.         NewCmd.ParseParams                := True;
  9. //******************************************************************************
  10.  
  11. procedure TApplication.WVW_HANDLER_SYSTEM_ERASE(ASender: TIdCommand);
  12. begin
  13.   clrscr;
  14.   Wr('WVW_HANDLER_SYSTEM_ERASE...',green,black);
  15.   ASender.Response.Add('[SYSTEM_ERASE_DONE]');  
  16.   ASender.SendReply;
  17. end;

TidCmdTCPClient code:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.IdCmdTCPClient1CommandHandlers0Command(
  2.   ASender: TIdCommand);
  3. begin
  4. //SYSTEM_ERASE_DONE
  5. Memo1.Lines.Append('[SYSTEM_ERASE_DONE]');
  6. end;
  7.  

Question, does anybody know howto send strings with variables in it from this Server to Client?
Inside the indy class framwork, with these procedures ?

Greets, PascalByThree.

« Last Edit: March 25, 2023, 06:10:35 pm by pascalbythree »

Thaddy

  • Hero Member
  • *****
  • Posts: 14197
  • Probably until I exterminate Putin.
Reminds me of this:
http://bobby-tables.com/img/xkcd.png
( Note I have not really used Indy for over 15 years, but there are experts, including a Guru Indy specialist, forum member and Indy maintainer that can answer this)
« Last Edit: March 25, 2023, 06:16:46 pm by Thaddy »
Specialize a type, not a var.

pascalbythree

  • Sr. Member
  • ****
  • Posts: 255
More alike this:

https://stackoverflow.com/questions/33172408/tidcommandhandler-how-to-construct-commands

Code: Pascal  [Select][+][-]
  1. case RadioGroup1.ItemIndex of
  2.   0: IdTCPClient1.IOHandler.WriteLn('COLOR SET|BLUE');
  3.   1: IdTCPClient1.IOHandler.WriteLn('COLOR SET|RED');
  4.   2: IdTCPClient1.IOHandler.WriteLn('COLOR SET|YELLOW');
  5. end;


Except that there commands require to much CommandHandlers procdures.

Is there a way to make it parse variables ? Instead of  fixed commands ?

Greets, Wouter

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 1312
    • Lebeau Software
Code: Pascal  [Select][+][-]
  1. procedure TApplication.WVW_HANDLER_SYSTEM_ERASE(ASender: TIdCommand);
  2. begin
  3.   clrscr;
  4.   Wr('WVW_HANDLER_SYSTEM_ERASE...',green,black);
  5.   ASender.Response.Add('[SYSTEM_ERASE_DONE]');  
  6.   ASender.SendReply;
  7. end;

FYI, you don't need to manually call ASender.SendReply() in this example.  It will be called automatically after the OnCommand handler exits, if a reply has not been sent yet.

Other than that, why is your server replying to the client by sending it another command?  Why not send a numeric/text code ala ASender.Reply.SetReply() instead?  Why is your client using TIdCmdTCPClient instead of using a standard TIdTCPClient with its SendCmd() method?  Do you really need fully asynchronous handling on both sides of the connection?

does anybody know how to send strings with variables in it from this Server to Client?

I'm not really sure I understand what you are asking for.  Are you looking for something like this?

Code: Pascal  [Select][+][-]
  1. procedure TApplication.WVW_HANDLER_SYSTEM_ERASE(ASender: TIdCommand);
  2. begin
  3.   clrscr;
  4.   Wr('WVW_HANDLER_SYSTEM_ERASE...',green,black);
  5.   ASender.Response.Add('[SYSTEM_ERASE_DONE] String1 String2 String3');
  6. end;

Code: Pascal  [Select][+][-]
  1. procedure TForm1.IdCmdTCPClient1CommandHandlers0Command(
  2.   ASender: TIdCommand);
  3. begin
  4.   //SYSTEM_ERASE_DONE
  5.   Memo1.Lines.Append('[SYSTEM_ERASE_DONE]');
  6.   Memo1.Lines.AddStrings(ASender.Params);
  7. end;

If you were to use TIdCommand.Reply.SetReply()/TIdTCPClient.SendCmd() instead, then it could look something more like this:

Code: Pascal  [Select][+][-]
  1. procedure TApplication.WVW_HANDLER_SYSTEM_ERASE(ASender: TIdCommand);
  2. begin
  3.   clrscr;
  4.   Wr('WVW_HANDLER_SYSTEM_ERASE...',green,black);
  5.   ASender.Reply.SetReply('OK', '');
  6.   ASender.Reply.Text.Add('String1');
  7.   ASender.Reply.Text.Add('String2');
  8.   ASender.Reply.Text.Add('String3');
  9. end;

Code: Pascal  [Select][+][-]
  1. ...
  2. IdTCPClient1.SendCmd('[SYSTEM_ERASE]', 'OK');
  3. Memo1.Lines.Append('[SYSTEM_ERASE_DONE]');
  4. Memo1.Lines.AddStrings(IdTCPClient1.LastCmdResult.Text);
  5. ...
« Last Edit: March 28, 2023, 02:33:55 am by Remy Lebeau »
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

pascalbythree

  • Sr. Member
  • ****
  • Posts: 255
Thank you alot Remy Lebeau, it got to work so far!

Check.... these are my 2 systems running on a Raspberry ZERO

Both have the Freepascal compiler installed in the LITE version, from ARM to AVR

Including screenshot form my Delphi 10 Application.

Problem: It does not re-connect, i must always restart the WIN32 Client and my Console application before it connect again.

Does anybody have source code how i can stable reconnect in the same instance ? For the IdCmdTCPServer ?

Greets, Wouter

 :-X

PS: My console app has a indy socket at the back.

Is this going to work out on a forum? Or do i need to reply with more clearance?

Rack mounted design on picture.
« Last Edit: April 06, 2023, 02:06:54 pm by pascalbythree »

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 1312
    • Lebeau Software
Problem: It does not re-connect, i must always restart the WIN32 Client and my Console application before it connect again.

I can't help you with that, as you did not provide your client's code.  Are you handling disconnects/errors from the server, and re-connecting your TCP client?

Does anybody have source code how i can stable reconnect in the same instance ?

Simply Disconnect() the client when you detect a disconnect/error from the server, and then re-Connect() it again.

For the IdCmdTCPServer ?

I'm not sure I understand what you are asking.

Is this going to work out on a forum? Or do i need to reply with more clearance?

Obviously, you need to provide more details about your code setup (don't care about the hardware).
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

pascalbythree

  • Sr. Member
  • ****
  • Posts: 255
IdCmdTcpServer:

Code: Pascal  [Select][+][-]
  1.  
  2. constructor TAppProgBench.Create;
  3. begin
  4.       LOG('Creating TCP Server...',white);
  5. //************************************************************************
  6.       IdCmdTCPServer                  := TIdCmdTCPServer.Create;
  7.       IdCmdTCPServer.DefaultPort      := WVW_SERVER_IP_PORT;
  8.       IdCmdTCPServer.ReuseSocket      := rsTrue;         
  9.       IdCmdTCPServer.MaxConnections   := 10;     
  10.       IdCmdTCPServer.TerminateWaitTime := 5000;
  11.       IdCmdTCPServer.ListenQueue       := 15;
  12.          
  13.  
  14.       IdCmdTCPServer.ExceptionReply.Text.Clear;
  15.       IdCmdTCPServer.ExceptionReply.Text.Append('WVW Unkown internall error.');
  16.          
  17.       IdCmdTCPServer.MaxConnectionReply.Text.Clear;
  18.       IdCmdTCPServer.MaxConnectionReply.Text.Append('WVW Too many connections. Try again later.');
  19.          
  20.       IdCmdTCPServer.ReplyUnknownCommand.Text.Clear;
  21.       IdCmdTCPServer.ReplyUnknownCommand.Text.Append('WVW Unknown command !');
  22.          
  23.       IdCmdTCPServer.Greeting.Text.Clear;        
  24.       IdCmdTCPServer.Greeting.Text.Append('WVW Welcome test PROG ZERO !');       
  25.          
  26.       IdCmdTCPServer.OnExecute        := @IdCmdTCPServerExecute;
  27.       IdCmdTCPServer.OnDisConnect     := @IdCmdTCPServerDisconnect;
  28.       IdCmdTCPServer.OnConnect        := @IdCmdTCPServerConnect;
  29.       IdCmdTCPServer.OnStatus         := @IdCmdTCPServerStatus;
  30.       IdCmdTCPServer.OnContextCreated := @IdCmdTCPServerContextCreated;  
  31.       IdCmdTCPServer.Active           := true;
  32.   //************************************************************************
  33. end;
  34.  


Code: Pascal  [Select][+][-]
  1. destructor TAppProgBench.Destroy;
  2. begin    
  3.   IdCmdTCPServer.Free;
  4. end;  

Code: Pascal  [Select][+][-]
  1. procedure TAppProgBench.IdCmdTCPServerDisconnect(AContext: TIdContext);
  2. begin
  3.    log('Server event Disconnected...',White);
  4. end;

Code: Pascal  [Select][+][-]
  1. procedure TAppProgBench.IdCmdTCPServerConnect(AContext: TIdContext);
  2. begin
  3.    log('Server event Connected...',White);
  4.    log('Connection from '+AContext.Binding.PeerIP+':'+IntToStr(AContext.Binding.Port),white);
  5.    log('Number of clients connected = '+IntToStr( IdCmdTCPServer.Contexts.LockList.Count), White);
  6. end;
  7.  

Code: Pascal  [Select][+][-]
  1. procedure TAppProgBench.WVW_ADD_COMMANDHANDLERS;
  2. var
  3.     NewCmd: TIdCommandHandler;
  4.        
  5. procedure WVW_ADD(S:String);
  6. begin
  7.     NewCmd                            := IdCmdTCPServer.CommandHandlers.Add;
  8.     NewCmd.Command                    := S;
  9.     NewCmd.ExceptionReply.NumericCode := 550;
  10.     NewCmd.Description.Text           := 'Description '+S; { do not localize }
  11.     NewCmd.Disconnect                 := False;
  12.     NewCmd.ParseParams                := True;
  13.     NewCmd.CmdDelimiter               := #32;
  14.     NewCmd.ParamDelimiter             := '|';  
  15. end;
  16.        
  17. begin
  18. //******************************************************************************
  19.     WVW_ADD('SYSTEMERASECH1');
  20.     NewCmd.OnCommand                  := @WVW_HANDLER_SYSTEM_ERASE_CH1;
  21.         WVW_ADD('SYSTEMERASECH2');
  22.     NewCmd.OnCommand                  := @WVW_HANDLER_SYSTEM_ERASE_CH2;
  23. //******************************************************************************
  24.     WVW_ADD('SYSTEMCOMPILECH1');
  25.     NewCmd.OnCommand                  := @WVW_HANDLER_SYSTEM_COMPILE_CH1;
  26.     WVW_ADD('SYSTEMCOMPILECH2');
  27.     NewCmd.OnCommand                  := @WVW_HANDLER_SYSTEM_COMPILE_CH2;      
  28. //******************************************************************************
  29.     WVW_ADD('SYSTEMPROGRAMCH1');
  30.     NewCmd.OnCommand                  := @WVW_HANDLER_SYSTEM_PROGRAM_CH1;
  31.     WVW_ADD('SYSTEMPROGRAMCH2');
  32.     NewCmd.OnCommand                  := @WVW_HANDLER_SYSTEM_PROGRAM_CH2;
  33. //******************************************************************************
  34.     WVW_ADD('SYSTEMREADSETTINGSCH1');
  35.     NewCmd.OnCommand                  := @WVW_HANDLER_SYSTEM_READ_SETTINGS_CH1;
  36.     WVW_ADD('SYSTEMREADSETTINGSCH2');
  37.     NewCmd.OnCommand                  := @WVW_HANDLER_SYSTEM_READ_SETTINGS_CH2;
  38. //******************************************************************************
  39.     WVW_ADD('SYSTEMWRITESETTINGSCH1');
  40.     NewCmd.OnCommand                  := @WVW_HANDLER_SYSTEM_WRITE_SETTINGS_CH1;
  41.     WVW_ADD('SYSTEMWRITESETTINGSCH2');
  42.     NewCmd.OnCommand                  := @WVW_HANDLER_SYSTEM_WRITE_SETTINGS_CH2;       
  43. //******************************************************************************
  44.     WVW_ADD('SYSTEMAVRDUDELIST');
  45.     NewCmd.OnCommand                  := @WVW_HANDLER_SYSTEM_AVRDUDE_LIST;
  46. //******************************************************************************
  47. end;  

IdCmdTcpClient:

Code: Pascal  [Select][+][-]
  1. procedure TFormMain.btnConnectBenchClick(Sender: TObject);
  2. begin
  3.   IdCmdTCPClientBench.Host := edIPbench.text;
  4.   IdCmdTCPClientBench.Port := sePortBench.Value;
  5.   LogBenchMAIN('Host IP adress: '+edIPBench.TEXT+':'+IntToStr(IdCmdTCPClientBench.Port), clWhite);
  6.   LogBenchMAIN('Connecting...', clWhite );
  7.  
  8.   Try
  9.     IdCmdTCPClientBench.Connect;
  10.  
  11.     LogBenchMAIN('Successfully Connected...', clWhite);
  12.   Except
  13.     LogBenchMAIN('ERROR Connecting...', clWhite);
  14.     exit;
  15.   end;
  16. end;  

Code: Pascal  [Select][+][-]
  1. procedure TFormMain.btnDisconnectBenchClick(Sender: TObject);
  2. begin
  3.  LogBenchMain('Disconnecting ...', clWhite);
  4.  IdCmdTCPClientBENCH.Disconnect;
  5. end;

Q: How can i re-connect this server? Connection works. Disconnection Works. Aftherwards i can not connect again without restarting the server executable
Anybody help?



« Last Edit: April 07, 2023, 03:49:11 pm by pascalbythree »

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 1312
    • Lebeau Software
Code: Pascal  [Select][+][-]
  1. procedure TAppProgBench.IdCmdTCPServerConnect(AContext: TIdContext);
  2. begin
  3.    ...
  4.    log('Number of clients connected = '+IntToStr( IdCmdTCPServer.Contexts.LockList.Count), White);
  5. end;
  6.  

You are locking the Contexts list, but you are not unlocking it after you are done accessing it.  As such, once the 1st client connects to the server, subsequent client threads will not be able to access the list correctly, which will deadlock the server.

Try this instead:

Code: Pascal  [Select][+][-]
  1. procedure TAppProgBench.IdCmdTCPServerConnect(AContext: TIdContext);
  2. var
  3.    list: TIdContextList;
  4. begin
  5.    ...
  6.    list := IdCmdTCPServer.Contexts.LockList;
  7.    try
  8.      log('Number of clients connected = '+IntToStr(list.Count), White);
  9.    finally
  10.      IdCmdTCPServer.Contexts.UnlockList; // <-- ADD THIS!!!!!
  11.    end;
  12. end;
  13.  

Alternatively, the TIdThreadSafeList class (which the Contexts list derives from) has its own Count() method that internally locks and unlocks the list for you:

Code: Pascal  [Select][+][-]
  1. procedure TAppProgBench.IdCmdTCPServerConnect(AContext: TIdContext);
  2. begin
  3.    ...
  4.    log('Number of clients connected = '+IntToStr(IdCmdTCPServer.Contexts.Count), White);
  5. end;
  6.  

Code: Pascal  [Select][+][-]
  1. procedure TAppProgBench.WVW_ADD_COMMANDHANDLERS;

You are activating your server in your class's constructor before you have added any command handlers to it.  As such, you are opening up a window of opportunity where clients can connect and send commands before the server is ready to handle them. Do ALL of your server setup BEFORE you activate the server. 
« Last Edit: April 08, 2023, 10:43:25 pm by Remy Lebeau »
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

pascalbythree

  • Sr. Member
  • ****
  • Posts: 255
Yay Yay Yay! it got to work, thank you

PS: Maybe another question about this will follow in a few days.

pascalbythree

  • Sr. Member
  • ****
  • Posts: 255
Finally back again...

Code: Pascal  [Select][+][-]
  1. destructor TApplication.Destroy;
  2. begin
  3.   Writeln('Indy socket active False...');
  4.   IdCmdTCPServer.Active := False;
  5.   Writeln('Free Indy socket...');
  6.   IdCmdTCPServer.Free;
  7.  
  8.   inherited;
  9. end;

Ofthen the terminal app refuses to exit at the end.

Do you got, after long waiting, any advisory to successful exit at the end?

Greets, Wouter van Wegen
« Last Edit: September 05, 2023, 11:17:15 am by pascalbythree »

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 1312
    • Lebeau Software
Code: Pascal  [Select][+][-]
  1. destructor TApplication.Destroy;
  2. begin
  3.   Writeln('Indy socket active False...');
  4.   IdCmdTCPServer.Active := False;
  5.   Writeln('Free Indy socket...');
  6.   IdCmdTCPServer.Free;
  7.  
  8.   inherited;
  9. end;

Ofthen the terminal app refuses to exit at the end.

Do you got, after long waiting, any advisory to successful exit at the end?

Are there clients still connected when you are deactivating the server?  Are you doing any synchronizing with the main UI thread while deactivating the server?  Do you have the same problem if you deactivate the server when your TForm is closed, rather than when your TApplication is destroyed?
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

pascalbythree

  • Sr. Member
  • ****
  • Posts: 255
Above got to work, it seems to exit;

But when i connect my putty client to the IdCmdTCPServer, the strings are only send back after requesting them for the second time.

Anybody know how to make it respond by once?

Greets, Wouter

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 1312
    • Lebeau Software
But when i connect my putty client to the IdCmdTCPServer, the strings are only send back after requesting them for the second time.

Is the client sending a line break after each command?
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

 

TinyPortal © 2005-2018