Home > Delphi, JavaScript, nodeJS, Object Pascal, OP4JS, Smart Mobile Studio > Writing a Delphi WebSocket server and Smart Mobile Client in 15 minutes

Writing a Delphi WebSocket server and Smart Mobile Client in 15 minutes

September 26, 2015 Leave a comment Go to comments

Websocket is all the rage these days. In essence its just an extra layer built on top of ordinary HTTP (available as a snap-in for IIS and “mod” for apache). But Delphi developers like to build their solutions from the ground up! So what could possibly be better than to roll your own?

Indy to the rescue

No I didnt see you playing with your sockets SIR!

No I didnt see you playing with your sockets SIR!

Think of client-server programming and Delphi and chances are “indy” will be the first word to pop into your mind. It’s been there for ages, it’s rock solid, it supports every known RFC known to mankind – and it’s tried and tested by time. It may not provide the same speed as Microsoft Internet Explorer or Apache, but there are hundreds (if not thousands) of products out there built with the Indy library, so it’s pretty damn awesome!

But what about websocket? As far as standards go it’s the new kid on the block – invented more or less purely for secure HTML5/JavaScript development. Does Indy have that yet? Well, no. I’m sure it will be included at one point in a future update, but thankfully Indy is easy to extend and mold due to it’s purely object oriented nature.

A while back mr. Andre Mucche took the time to implement just that, extending an ordinary Indy HTTP server with the required plumbing – turning a bog standard, multi-threaded, multi-context HTTP server into a websocket nerdvana.

Why is this important?

If all you do is write old-school stuff in Delphi then you probably don’t need it, but if you want to keep up with the way technology is moving – then WebSockets is bound (pun intended) to cross your path sooner or later. If you havent already been asked by your customers, it’s only a matter of time before you are approached with the question “Can we poll data from our Delphi solution and use that on our website from JavaScript?”.

Well, there are many ways to deal with getting data from a Delphi centric solution (read: server) onto your website. You can spend weeks and months writing the JavaScript yourself, you can publish a few DataSnap API’s — or go for RemObjects SDK which IMHO is a much better alternative to DataSnap.

But Smart Mobile Studio offers an alternative route. The benefits should be fairly obvious:

  • You write object pascal (Delphi / FreePascal)
  • You don’t have to learn much JavaScript
  • All the low-level stuff is already wrapped and ready
  • Smart Mobile supports both RemObjects, DataSnap and Websocket (and a few more)

So how hard is it to create a Delphi websocket server and a Smart Mobile Studio client?

The Delphi side

Right, first start by creating a folder for your project. In my example I just named it “WebSocket”. Then create a fresh Delphi project (VCL) and save that into the folder as “SocketServer.dpr”.

Next, download Andre’s WebSocket extension units, these can be found here: https://github.com/andremussche/DelphiWebsockets. It’s Github so just download the zip archive. Once downloaded, unzip the files into your project folder. Your folder should look something like this by now:

Quick and dirty

Quick and dirty

With the files in place, add all the units to your project inside Delphi (including the superobject files). You dont really have to do this, you can unzip the files wherever you like — but for this quick demonstration I just stuff it all into the same project to avoid setting a path (it’s late, what can I say). Your Delphi project should now look like this:

Easy as apple-pie

Easy as apple-pie

With that in place, let’s add a TMemo component, a couple of buttons to control the server (start and stop) and isolate that in TActions. If you havent used actions before then please read up on that before you continue. It’s super simple and one of Delphi’s biggest strength’s over other RAD platforms out there. My form looks like this (just slap-dash 2 second stuff):

Not much to look at, but bling comes last

Not much to look at, but bling comes last

Now let’s write some code!

unit mainform;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
  System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,

  IdComponent,
  IdContext,
  IdCustomHTTPServer,
  IdServerWebsocketContext,
  IdServerSocketIOHandling,
  IdWebsocketServer, Vcl.StdCtrls, System.Actions, Vcl.ActnList;

type
  TForm1 = class(TForm)
    Memo1: TMemo;
    Button1: TButton;
    Button2: TButton;
    ActionList1: TActionList;
    acStart: TAction;
    acStop: TAction;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure acStartExecute(Sender: TObject);
    procedure acStopExecute(Sender: TObject);
    procedure acStartUpdate(Sender: TObject);
    procedure acStopUpdate(Sender: TObject);
  private
    { Private declarations }
    FServer:    TIdWebsocketServer;

    procedure   HandleServerStatus(ASender: TObject;
                const AStatus: TIdStatus;
                const AStatusText: string);

    procedure   HandleTextMessage(const AContext: TIdServerWSContext;
                const aText: string);

    procedure   HandleCommandGet(AContext: TIdContext;
                ARequestInfo: TIdHTTPRequestInfo;
                AResponseInfo: TIdHTTPResponseInfo);

  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.acStartExecute(Sender: TObject);
begin
  FServer.Active:=True;
end;

procedure TForm1.acStartUpdate(Sender: TObject);
begin
  TAction(sender).Enabled := not FServer.Active;
end;

procedure TForm1.acStopExecute(Sender: TObject);
begin
  FServer.Active := false;
end;

procedure TForm1.acStopUpdate(Sender: TObject);
begin
  TAction(sender).Enabled := FServer.Active;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  FServer := TIdWebsocketServer.Create(NIL);
  FServer.OnStatus := HandleServerStatus;
  FServer.OnMessageText := HandleTextMessage;
  FServer.OnCommandGet := HandleCommandGet;
  FServer.KeepAlive := True;
  FServer.DefaultPort := 8080;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  FServer.free;
end;

procedure TForm1.HandleCommandGet(AContext: TIdContext;
          ARequestInfo: TIdHTTPRequestInfo;
          AResponseInfo: TIdHTTPResponseInfo);
begin
  aResponseInfo.ContentText:='Hello world';
end;

procedure TForm1.HandleTextMessage(const AContext: TIdServerWSContext;
          const aText: string);
begin
  memo1.Lines.Add(aText);
end;

procedure TForm1.HandleServerStatus(ASender: TObject;
          const AStatus: TIdStatus;
          const AStatusText: string);
begin
  memo1.Lines.Add(aStatusText);
end;

end.

That’s basically it! The most bare-bone WebSocket server you will ever see. It just accepts a connection and dumps whatever text a client writes to the memo control on the form.
Right, now let’s look at the Smart Mobile Studio side of things.

The HTML5 Client

Fire up Smart Mobile Studio (im using the latest beta here) and create a new project. Remember to save the project before you start coding.
We will be adding a single button for connecting to the websocket server, and then a textbox for message input — and finally a “send” button to ship the next to the server.

This is what my slap-dash client looks like

This is what my slap-dash client looks like

With some components in place we move on to the WebSocket client code, which under the Smart Mobile RTL is a piece of cake:

unit Form1;

interface

uses 
  SmartCL.inet,
  SmartCL.System, SmartCL.Graphics, SmartCL.Components, SmartCL.Forms, 
  SmartCL.Fonts, SmartCL.Borders, SmartCL.Application, SmartCL.Controls.Button,
  SmartCL.Controls.Memo, SmartCL.Controls.EditBox;

type
  TForm1 = class(TW3Form)
    procedure W3Button2Click(Sender: TObject);
    procedure W3Button1Click(Sender: TObject);
  private
    {$I 'Form1:intf'}
    FSocket:  TW3WebSocket;
  protected
    procedure InitializeForm; override;
    procedure InitializeObject; override;
    procedure Resize; override;
  end;

implementation

{ TForm1 }

procedure TForm1.W3Button1Click(Sender: TObject);
begin
  try
    FSocket.Connect('ws://192.168.10.106:8080',[]);
  except
    on e: exception do
    showmessage(e.message);
  end;
end;

procedure TForm1.W3Button2Click(Sender: TObject);
var
  mText:String;
begin
  mText :=trim(w3Editbox1.text);

  if mtext.length > 0 then
    FSocket.Write(mText);

  w3Editbox1.text := '';
end;

procedure TForm1.InitializeForm;
begin
  inherited;
  // this is a good place to initialize components
  FSocket := TW3WebSocket.Create;
  FSocket.OnOpen := procedure (sender:TW3WebSocket)
    begin
      w3memo1.text := w3memo1.text + "WebSocket open" + #13;
    end;
end;

procedure TForm1.InitializeObject;
begin
  inherited;
  {$I 'Form1:impl'}
end;
 
procedure TForm1.Resize;
begin
  inherited;
end;
 
initialization
  Forms.RegisterForm({$I %FILE%}, TForm1);
end.

The final result

Now fire up your Delphi project, click the “start” button to initialize the server (note: the firewall may ask you to allow the server to use the port, remember to check “local networks” and just click “ok”). Your Delphi server should now run at port 8080 — use your favorite browser to check that it works. It should return “hello world” (see “HandleCommandGet” event handler in the Delphi code).

Next, fire up your Smart Mobile Studio project. Hit the “Connect” button, type something in the text-field and click “send” to ship it off to the server. Now watch the memo on the server and voila — you have just written your first websocket client/server system in less than 15 minutes!

Websocket has never been easier!

Websocket has never been easier!

Note: Remember to use your local IP. The IP listed in the SMS example above is just a local address on my local network. If you are running Delphi and SMS on the same machine, just use 127.0.0.1 and bob’s your uncle.

Enjoy!

Advertisements
  1. September 27, 2015 at 10:30 am

    One step further of using WebSockets is to include them as SOA asynchronous callbacks.
    This is what our Open Source mORMot framework offers, also with SmartMobileStudio support. See http://synopse.info/files/html/Synopse%20mORMot%20Framework%20SAD%201.18.html#TITL_149

  2. September 27, 2015 at 7:12 pm

    Hi, I’ve created a simple websocket application using mORMot and D7 that send/receive binary data (upload images to server, broadcasting videos to clients) to HTML5 clients.

    I think the current Smart Mobile Studio websocket implementation, you can not receive binary data for instance.

    On the client side, we expect to use the attribute binaryType, f.i.
    FSocket.binaryType = ‘arraybuffer’;

    Setting the binaryType attribute of WebSocket right after connection initialization it allows both text and binary data to be transfered. I think the current Smart implementation does not include this property.

    It also would be nice if we have some smart pascal similar like this onMessage event, to handle both blob and texts:

    fsocket.onmessage := lambda(event)
    begin
    if (event.data instanceof Blob)
    begin

    end;
    if (event.data instanceof ArrayBuffer)
    begin
    var myBlob := new Blob([event.data]);

    end else
    if (typeof event.data = “string”)
    begin

    end;
    end;

    • Jon Lennart Aasenden
      September 28, 2015 at 8:07 am

      Ops! You are right! Consider it fixed!

      Now implemented as such:

      TWebSocketMessageData = Record
      mdType: TWebSocketMessageType;
      mdBinary: TStream;
      mdText: String;
      end;

      TWebSocketMessageEvent = Procedure (Sender:TW3WebSocket;Message:TWebSocketMessageData);

    • Jon Lennart Aasenden
      September 28, 2015 at 10:20 am

      I have now updated the inet unit with full support for blob, arraybuffer and text messages. It defaults to arraybuffer and you get it as a TMemoryStream in the event-handler. It will be pushed to the beta repo later 🙂

  3. September 28, 2015 at 7:19 am

    By the way, websockets itself are very basic, but with socket.io you can easily create some cool event driven async stuff! https://github.com/andremussche/DelphiWebsockets/blob/master/README.md

  4. October 5, 2015 at 4:25 pm

    Hi, there’s an wrapper (smart pascal) around socket.io

    https://drive.google.com/folderview?id=0B5AcQp14Q5FaX2xiSGV5ejJHVHM

  5. October 9, 2015 at 10:11 am

    Hi. You can see realisation of the real HTML5 client with help of the pure Indy library here:

    http://www.makhaon.com/index.php?lng=en&p=products&id=viewer

    We just made some chenges in the library and it works fine for us.

  1. No trackbacks yet.

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: