#1 2011-03-09 07:42:20

edwinsn
Member
Registered: 2010-07-02
Posts: 1,218

How to call a method of a specified client from the server?

Hi,

Synopse SQLITE3 is so great! And I want to use the implemented named-pipes based client/server communication.

I know a client can send a message to the server using the TSQLRestClientURI.CallBackGet method and the similarities, but how do it the other way around? I mean, to send a message to a *specified* client of all the clients, from the server.

Thanks!


Delphi XE4 Pro on Windows 7 64bit.
Lazarus trunk built with fpcupdelux on Windows with cross-compile for Linux 64bit.

Offline

#2 2011-03-09 07:56:20

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,655
Website

Re: How to call a method of a specified client from the server?

Due to the RESTful architecture, it's not feasible directly: there is only one-way communication, from the client to the server.
This is how AJAX application work: they maintain the link with the server, and ask periodically for any pending message/notification.

So the clients have to ask the server (using a timer) for any pending message, via a RESTful service.

In practice, it's efficient and fast, since the link between the client and the server is kept alive, already open and ready.

Locally, the most efficient communication could be to use GDI messages. Those messages can be sent from the Client to the Server and in reverse order.
But remote connection, you'd have to use a timer and a dedicated Service.

Offline

#3 2011-03-09 08:25:17

edwinsn
Member
Registered: 2010-07-02
Posts: 1,218

Re: How to call a method of a specified client from the server?

ab wrote:

Locally, the most efficient communication could be to use GDI messages. Those messages can be sent from the Client to the Server and in reverse order.

Thanks for your fast response!  Really appreciated!

What do you mean by 'GDI messages' here? Do you mean sending the WM_CopyData windows message? 

BTW, Which 'rest client type' is most efficient if both the server and the client are located in the same machine? Is TSQLRestClientURIMessage? Thanks!


Delphi XE4 Pro on Windows 7 64bit.
Lazarus trunk built with fpcupdelux on Windows with cross-compile for Linux 64bit.

Offline

#4 2011-03-09 09:37:41

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,655
Website

Re: How to call a method of a specified client from the server?

For Client and Server on the same machine, the most efficient is GDI messages, then Named Pipes.

If you have big amount of data to transfer (more than 100KB) for each request, named pipes are faster than GDI messages.

Use TSQLRestServer.ExportServerMessage and TSQLRestClientURIMessage for fast GDI communication.
Those will create hidden GDI window: so it will work even with no GUI available. You can even write your server program as a service (if you create it with SERVICE_INTERACTIVE_PROCESS flag - which is case by default in our SQLite3Service unit).

For notification, you can use WM_USER messages between client and server. See TSQLRestServer.ExportServerMessage and TSQLRestClientURIMessage.WMCopyData methods for a way of handling messages from both client and server side.
But the timer+request trick is perhaps better, because if for some reason you'd use remote connection (e.g. with HTTP), it will work without code modification, whereas the GDI messages won't work over a network.

Offline

#5 2011-03-09 10:33:55

edwinsn
Member
Registered: 2010-07-02
Posts: 1,218

Re: How to call a method of a specified client from the server?

Wow, found another hidden gem in this lib following your clue - Class for controlling the windows services.

Thanks for the further info, ab.


Delphi XE4 Pro on Windows 7 64bit.
Lazarus trunk built with fpcupdelux on Windows with cross-compile for Linux 64bit.

Offline

#6 2011-04-06 15:30:46

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,655
Website

Re: How to call a method of a specified client from the server?

Here is some sample code of a background service program using this SQLite3Service unit:

/// implements a background Service
program Background_Service;

uses
  Windows,
  Classes,
  SysUtils,
  WinSvc,
  SQLite3Service;

// define this conditional if you want the GDI messages to be accessible
// from the background service 
{$define USEMESSAGES}

type
  /// class implementing the background Service
  TMyService = class(TService)
  public
    /// the background Server processing all requests
    // - TThread should be replaced by your own process
    Server: TThread;

    /// event trigerred to start the service
    // - e.g. create the Server instance
    procedure DoStart(Sender: TService);
    /// event trigerred to stop the service
    // - e.g. destroy the Server instance
    procedure DoStop(Sender: TService);

    /// initialize the background Service
    constructor Create; reintroduce;
    /// release memory
    destructor Destroy; override;
  end;


const
  SERVICENAME = 'MyService';
  SERVICEDISPLAYNAME = 'My service';


{ TMyService }

constructor TMyService.Create;
begin
  inherited Create(SERVICENAME,SERVICEDISPLAYNAME);
  OnStart := DoStart;
  OnStop := DoStop;
  OnResume := DoStart; // trivial Pause/Resume actions
  OnPause := DoStop;
end;

destructor TMyService.Destroy;
begin
  FreeAndNil(Server);
  inherited;
end;

procedure TMyService.DoStart(Sender: TService);
begin
  if Server<>nil then
    DoStop(nil); // should never happen
  Server := TThread.Create(false); 
end;

procedure TMyService.DoStop(Sender: TService);
begin
  FreeAndNil(Server);
end;

procedure CheckParameters;
var i: integer;
    param: string;
begin
  with TServiceController.CreateOpenService('','',SERVICENAME) do
  // allow to control the service
  try
    if State<>ssErrorRetrievingState then
      for i := 1 to ParamCount do begin
        param := paramstr(i);
        if param='/install' then
          TServiceController.CreateNewService('','',SERVICENAME,
              SERVICEDISPLAYNAME, paramstr(0),'','','','',
              SERVICE_ALL_ACCESS,
              SERVICE_WIN32_OWN_PROCESS
                {$ifdef USEMESSAGES}or SERVICE_INTERACTIVE_PROCESS{$endif},
              SERVICE_AUTO_START).  // auto start at every boot
            Free else
        if param='/remove' then begin
           Stop;
           Delete;
        end else
        if param='/stop' then
          Stop else
        if param='/start' then
          Start([]);
      end;
  finally
    Free;
  end;
end;

var Service: TMyService;
begin
  if ParamCount<>0 then
    CheckParameters else begin
    Service := TMyService.Create;
    try
      // launches the registered Services execution = do all the magic
      ServicesRun;
    finally
      Service.Free;
    end;
  end;
end.

I've written this code for a question of http://stackoverflow.com/questions/5561 … 06#5568906

Offline

#7 2011-04-09 14:45:16

esmondb
Member
From: London
Registered: 2010-07-20
Posts: 299

Re: How to call a method of a specified client from the server?

Thanks for the example. How would you go about converting the HTTP server in Sample 4 to a service? Is it along the lines of replacing Server: TThread; with Server: TSQLite3HttpServer; or does a TThread sub-class need to be created?

Offline

#8 2011-04-10 17:50:12

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,655
Website

Re: How to call a method of a specified client from the server?

The TSQLite3HttpServer class will create any necessary threads.
And fast http.sys kernel mode server if available.

So you can use TSQLite3HttpServer directly in your TService child.

Offline

#9 2011-04-11 06:11:19

esmondb
Member
From: London
Registered: 2010-07-20
Posts: 299

Re: How to call a method of a specified client from the server?

I'm having trouble getting this working.
When I call TServiceController.CreateNewService like in the param='/install' part of the sample above, no errors are reported. But I can't see my new service in the Windows services control panel.
If I then call TServiceController.Start I get the error: 'The service did not respond to the start or control request in a timely fashion'.
Can you suggest where it could be going wrong?

Thanks

Offline

#10 2011-04-11 07:35:18

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,655
Website

Re: How to call a method of a specified client from the server?

Did you try step by step debugging within TServiceController.CreateNewService() code?
Is there no error returned by Windows?

You'll have to change the service name and display name, of course.

Offline

#11 2011-04-11 08:47:12

esmondb
Member
From: London
Registered: 2010-07-20
Posts: 299

Re: How to call a method of a specified client from the server?

I've found the install is working (I was looking for the service name instead of display name) but I get a windows error 1053 - 'The service did not respond to the start or control request in a timely fashion.' when I try to start the service.

I'm experimenting with this modification of your code above:

/// implements a background Service
program SQLite3HttpServerService;

uses
  Windows,
  Classes,
  SysUtils,
  WinSvc,
  SQLite3Service,
  SQLite3Commons,
  SQLite3,
  SQLite3HTTPServer,
  SampleData in '..\01 - In Memory ORM\SampleData.pas';

// define this conditional if you want the GDI messages to be accessible
// from the background service
{.$define USEMESSAGES}

type
  /// class implementing the background Service
  TSQLite3HttpService = class(TService)
  public
    /// the background Server processing all requests
    // - TThread should be replaced by your own process
    //Server: TThread;
    Model: TSQLModel;
    DB: TSQLRestServerDB;
    Server: TSQLite3HttpServer;

    /// event trigerred to start the service
    // - e.g. create the Server instance
    procedure DoStart(Sender: TService);
    /// event trigerred to stop the service
    // - e.g. destroy the Server instance
    procedure DoStop(Sender: TService);

    /// initialize the background Service
    constructor Create; reintroduce;
    /// release memory
    destructor Destroy; override;
  end;


const
  SERVICENAME = 'SQLite3HttpServerService';
  SERVICEDISPLAYNAME = 'SQLite3 Http Server Service';

{ TSQLite3HttpService }

constructor TSQLite3HttpService.Create;
begin
  inherited Create(SERVICENAME,SERVICEDISPLAYNAME);
  OnStart := DoStart;
  OnStop := DoStop;
  OnResume := DoStart; // trivial Pause/Resume actions
  OnPause := DoStop;
  Model := nil;
end;

destructor TSQLite3HttpService.Destroy;
begin
  //FreeAndNil(Server);
  inherited;
end;

procedure TSQLite3HttpService.DoStart(Sender: TService);
begin
  if Model<>nil then
    DoStop(nil); // should never happen
  //Server := TThread.Create(false);
  Model := CreateSampleModel;
  DB := TSQLRestServerDB.Create(Model,ChangeFileExt(paramstr(0),'.db3'));
  DB.CreateMissingTables(0);
  Server := TSQLite3HttpServer.Create('8080',[DB]);
end;

procedure TSQLite3HttpService.DoStop(Sender: TService);
begin
  FreeAndNil(Server);
  FreeAndNil(DB);
  FreeAndNil(Model);
end;

procedure CheckParameters;
var i: integer;
    param: string;
begin
  with TServiceController.CreateOpenService('','',SERVICENAME) do
  // allow to control the service
  try
    if State<>ssErrorRetrievingState then
      for i := 1 to ParamCount do begin
        param := paramstr(i);
        if param='/install' then
          TServiceController.CreateNewService('','',SERVICENAME,
              SERVICEDISPLAYNAME, paramstr(0),'','','','',
              SERVICE_ALL_ACCESS,
              SERVICE_WIN32_OWN_PROCESS
                {$ifdef USEMESSAGES}or SERVICE_INTERACTIVE_PROCESS{$endif},
              SERVICE_AUTO_START).  // auto start at every boot
            Free else
        if param='/remove' then begin
           Stop;
           Delete;
        end else
        if param='/stop' then
          Stop else
        if param='/start' then
          Start([]);
      end;
  finally
    Free;
  end;
end;

var Service: TSQLite3HttpService;
begin
  if ParamCount<>0 then
    CheckParameters else begin
    Service := TSQLite3HttpService.Create;
    try
      // launches the registered Services execution = do all the magic
      ServicesRun;
    finally
      Service.Free;
    end;
  end;
end.

Offline

#12 2011-04-12 05:02:36

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,655
Website

Re: How to call a method of a specified client from the server?

On which version of Windows are you working?

Offline

#13 2011-04-12 05:37:29

esmondb
Member
From: London
Registered: 2010-07-20
Posts: 299

Re: How to call a method of a specified client from the server?

It's a virtual Windows 2008 R2 server. Using Delphi 2007.

Offline

Board footer

Powered by FluxBB