Service Discovery sample (Delphi)

Overview

This sample illustrates the usage of the broadcast UDP based service discovery with TRODiscoveryClient and TRODiscoveryServer components.

Note: This technology is deprecated, consider using ROZeroConf instead.

Each instance of the application acts as a server and a client at the same time.

For the server, you can modify the list of services supported on the right-hand side, while for the client you can enter a service name and get a list of the servers available on your LAN to support it.

Getting started

  • The resulting application combines the client and the server, so in order to test this sample, you need to deploy the compiled .exe on two or more computers.

Note: If you are running a trial version of the Remoting SDK, you need to have the Delphi IDE running on each machine due to trial version limitations.

  • Click Discover Servers on the client side and see the list of found servers with specified interfaces.
  • Change the Services list on the server side and try to discover servers with added or removed services.
  • Uncheck the Return info checkbox for one of the servers and discover the servers again; see that no info is provided by the server.
  • Test the Find a Server with a Load below and Simulated Server Load options, and see that clients won't discover servers with loads other than specified by the first one.
  • Uncheck/check the List registered RO Servers as supported checkbox and see that clients won't/will discover this server.

Examine the code

  • Look how discovery query and result objects are defined to expand the TRODiscoveryOptions class:
TMyDiscoveryQueryOptions = class(TRODiscoveryOptions)
private
  fMaximumLoad: Integer;
published
  property MaximumLoad: Integer read fMaximumLoad write fMaximumLoad;
end;

TMyDiscoveryResultOptions = class(TRODiscoveryOptions)
private
  fServerTime: TDateTime;
  fMoreInfo: UnicodeString;
  fLoad: Integer;
published
  property ServerTime: TDateTime read fServerTime write fServerTime;
  property MoreInfo: UnicodeString read fMoreInfo write fMoreInfo;
  property Load: Integer read fLoad write fLoad;
end;
  • In order to provide a custom list of service names that are available, the server uses the ServiceList string collection:
procedure TServiceDiscoveryMainForm.btn_UpdateServiceListClick(Sender: TObject);
begin
  RODiscoveryServer.ServiceList.Assign(ed_Services.Lines);
end;
  • See how the client looks for servers with the specified interface:
procedure TServiceDiscoveryMainForm.btn_LookupServiceClick(Sender: TObject);
var
  lOptions: TMyDiscoveryQueryOptions;
begin
  lb_Servers.Items.Clear();

  RODiscoveryClient.ServiceName := ed_ServiceName.Text;

  lOptions := TMyDiscoveryQueryOptions.Create();
  try
    lOptions.MaximumLoad := StrToIntDef(ed_MaxLoad.Text,100);
    RODiscoveryClient.RefreshServerList(lOptions);
  finally
    lOptions.Free();
  end;
end;
  • If the server supports queried interfaces, the OnServiceFound event is fired allowing the server to evaluate the data and return the answer or refuse to return:
procedure TServiceDiscoveryMainForm.RODiscoveryServerServiceFound(
  aSender: TObject; aName: string; var ioDiscoveryOptions: TRODiscoveryOptions;
  var ioHandled: Boolean);
begin
  if Assigned(ioDiscoveryOptions) and (ioDiscoveryOptions is TMyDiscoveryQueryOptions) then begin
    { Check if we fullfill the load requirement. If our load is too high, abort
      without sending a response to the client }
    if TMyDiscoveryQueryOptions(ioDiscoveryOptions).MaximumLoad <= StrToIntDef(ed_ServerLoad.Text,100) then begin
      ROSendNoResponse();
    end;
  end;

  if cb_ReturnInfo.Checked then begin
    ioDiscoveryOptions := TMyDiscoveryResultOptions.Create;
    with TMyDiscoveryResultOptions(ioDiscoveryOptions) do begin
      Load := StrToIntDef(ed_ServerLoad.Text,-1);
      MoreInfo := 'Some info';
      ServerTime := Now;
    end;    { with }
  end
  else begin
    ioDiscoveryOptions := nil;
  end;

  { Don't free the original ioDiscoveryOptions (if it was assigned), the RO
    framework takes care of that, just as it will free the one we pass back }
end;

  • When a new server responds to the broadcast query, the OnNewServiceFound event is fired on the client and the server is added to the server list.
procedure TServiceDiscoveryMainForm.RODiscoveryClientNewServiceFound(
  aSender: TObject; aName: String; aDiscoveryOptions: TRODiscoveryOptions);
var
  lOptions: TMyDiscoveryResultOptions;
begin
  if Assigned(aDiscoveryOptions) and (aDiscoveryOptions is TMyDiscoveryResultOptions) then begin
    lOptions := TMyDiscoveryResultOptions(aDiscoveryOptions);
    lb_Servers.Items.Add(Format('%s (Load: %d; Info: %s)',[aName, lOptions.Load, lOptions.MoreInfo]));
  end
  else begin
    lb_Servers.Items.Add(aName+' (no info)');
  end;
end;