You are not logged in.
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
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
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
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
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
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
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
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
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
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
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
It's a virtual Windows 2008 R2 server. Using Delphi 2007.
Offline