Networking
We have described the use of Node.js for an echo server in the previous section. For this demonstration of real-time updating we use WebSockets for a minimal chat client-server system. It is set up for use on a local machine and works with the browsers Chrome, Firefox and Opera. It uses much of the code of the supplied WebSocket client and server examples. (We have retained our original 2015 page showing real time updating using socket.io).
Code of Server
This is a slimmed-down project file for the server.
<SMART> <Project version="2" subversion="2"> <Name>WebSocketServer</Name> <Options> <Compiler /> <Codegen> <Obfuscation>1</Obfuscation> <Devirtualize>1</Devirtualize> <MainBody>1</MainBody> <CodePacking>1</CodePacking> <SmartLinking>1</SmartLinking> <Verbosity>0</Verbosity> </Codegen> <ConditionalDefines /> <Linker> <EmbedJavaScript>0</EmbedJavaScript> </Linker> <Output> <OutputFilePath>output\</OutputFilePath> </Output> <Import /> <Execute> <Server>1</Server> <ExecuteType>3</ExecuteType> <ExecuteableName>node.exe</ExecuteableName> <ExecuteableParams>--no-warnings --stack-size=1200 %output%</ExecuteableParams> </Execute> </Options> <Files> <File type="main"> <Name>WebSocketServer</Name> <Source> <![CDATA[uses Unit1; uses SmartNJ.System; var Server := TMessageServer.Create; Server.Run;]]> </Source> </File> <File type="unit"> <Name>Unit1</Name> <Source> <![CDATA[
unit Unit1; // Note: Install the "ws" nodejs module in your output folder before running this demo. // 1. Change directory to the output folder (compile to create it first) // 2. Execute the following via shell: npm install ws interface uses System.Types, System.Types.Convert, System.Objects, Nodejs.websocket, SmartNJ.System, SmartNJ.Network, SmartNJ.Server.Http, SmartNJ.Server.WebSocket; type TMessageServer = class(TObject) private FServer: TNJWebSocketServer; procedure HandleTextMessage(Sender: TObject; Socket: TNJWebSocketSocket; Info: TNJWebsocketMessage); public procedure Run; constructor Create; virtual; destructor Destroy; override; end; implementation constructor TMessageServer.Create; begin inherited Create; FServer := TNJWebSocketServer.Create; end; destructor TMessageServer.Destroy; begin if FServer.Active then FServer.Active := false; FServer.free; inherited; end; procedure TMessageServer.Run; begin FServer.Port := 1881; // Setup an event handler for text messages. FServer.OnTextMessage := @HandleTextMessage; FServer.Active := true; WriteLn('Server started, listening to port 1881'); end; procedure TMessageServer.HandleTextMessage(Sender: TObject; Socket: TNJWebSocketSocket; Info: TNJWebsocketMessage); begin writeLnF("Got message: %s", [Info.wiText]); // Send the message to all clients. for var ClientNum := 0 to FServer.Count - 1 do FServer.Clients[ClientNum].Send(Info.wiText); end; end.
]]> </Source> </File> </Files> <Target>Node.js</Target> <Generator>Node.js Project</Generator> </Project> </SMART>
Code of Client
This is a slimmed-down project file for the client.
<SMART> <Project version="2" subversion="2"> <Name>WebSocketClient</Name> <Options> <Compiler /> <Codegen> <Obfuscation>1</Obfuscation> <Devirtualize>1</Devirtualize> <MainBody>1</MainBody> <CodePacking>1</CodePacking> <SmartLinking>1</SmartLinking> <Verbosity>1</Verbosity> </Codegen> <ConditionalDefines /> <Linker> <Theme>default.css</Theme> <EmbedJavaScript>1</EmbedJavaScript> </Linker> <Output> <HtmlFileName>WebSocketClient.html</HtmlFileName> <OutputFilePath>www\</OutputFilePath> </Output> <Import /> <Execute /> </Options> <Files> <File type="main"> <Name>WebSocketClient</Name> <Source> <![CDATA[uses SmartCL.System, Unit1; var Application := TApplication.Create; Application.RunApp; ]]> </Source> </File> <File type="unit"> <Name>Unit1</Name> <Source> <![CDATA[
unit Unit1; interface uses Pseudo.CreateForms, // auto-generated unit that creates forms during startup System.Types, SmartCL.System, SmartCL.Components, SmartCL.Forms, SmartCL.Application, Form1; type TApplication = class(TW3CustomApplication) end; implementation end.]]> </Source> </File> <File type="form"> <Name>Form1</Name> <Source> <![CDATA[unit Form1; interface uses System.Types, System.Types.Convert, System.Objects, System.Time, SmartCL.System, SmartCL.Graphics, SmartCL.Components, SmartCL.FileUtils, SmartCL.Forms, SmartCL.Fonts, SmartCL.Theme, SmartCL.Borders, SmartCL.Net.websocket, SmartCL.Application, SmartCL.Controls.EditBox, SmartCL.Controls.Button, SmartCL.Controls.Memo; type TForm1 = class(TW3Form) procedure W3Button1Click(Sender: TObject); private {$I 'Form1:intf'} FSocket: TW3WebSocket; protected procedure InitializeObject; override; end; implementation procedure TForm1.W3Button1Click(Sender: TObject); begin FSocket.Connect('ws://127.0.0.1:1881'); end; procedure TForm1.InitializeObject; begin inherited; {$I 'Form1:impl'} FSocket := TW3WebSocket.Create; FSocket.OnOpen := procedure (sender: TW3WebSocket) begin FSocket.Write(edtMsg.text); end; FSocket.OnMessage := procedure (Sender: TW3WebSocket; Message: TWebSocketMessageData) begin if Message.mdType = wsText then TextLog.Add(Message.mdText, true); end; end; initialization Forms.RegisterForm({$I %FILE%}, TForm1); end.
]]> </Source> <Design> <![CDATA[<?xml version="1.0" encoding="utf-16"?> <Form version="2" subversion="2"> <object type="TW3Form"> <Caption>W3Form</Caption> <Name>Form1</Name> <object type="TW3Button"> <Caption>Send</Caption> <Width>304</Width> <Top>400</Top> <Left>8</Left> <Height>56</Height> <Name>W3Button1</Name> <OnClick>W3Button1Click</OnClick> </object> <object type="TW3EditBox"> <Text>Hello</Text> <Width>400</Width> <Top>300</Top> <Left>8</Left> <Height>32</Height> <Name>edtMsg</Name> </object> <object type="TW3Memo"> <Width>280</Width> <Top>32</Top> <Left>8</Left> <Height>200</Height> <Name>TextLog</Name> </object> </object> </Form>]]> </Design> <AutoCreate> <IsAutoCreate>1</IsAutoCreate> <IsMainForm>1</IsMainForm> <Order>1</Order> </AutoCreate> </File> </Files> <Target>Browser</Target> <Generator>Visual Components Project</Generator> </Project> </SMART>