Articles Writing a Delphi WebSocket server and Smart Mobile Client in 15 minutes by Jon L. Aasenden

emailx45

Местный
Регистрация
5 Май 2008
Сообщения
3,571
Реакции
2,439
Credits
574
Writing a Delphi WebSocket server and Smart Mobile Client in 15 minutes
Jon L. Aasenden - September 26, 2015
[SHOWTOGROUPS=4,20]
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!

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: Для просмотра ссылки Войди или Зарегистрируйся. 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
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
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
Now let’s write some code!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
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.

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
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!

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.

[/SHOWTOGROUPS]