In an increasingly connected world, websites or IoT applications must be updated in real-time.
A way to deliver information instantly to your sites is to use the Websocket protocol that provides a full-duplex communication channel between a server and a client. From the v20, 4D provides commands to create a Websocket server.
Commands to Create a Websocket Server
To manage your websocket server, 4D provides 2 new classes:
- The first manages the server itself, 4D.WebSocketServer
- The second manages the websocket connections: 4D.WebSocketConnection
To demonstrate this feature, let’s create a simple chat server!
Before using the websocket server class, your 4D webserver must be started!
This class allows you to manage different events that occur on your server. To do so, you need to create a class with the below functions:
- onConnection(), called when a new connection is requested.
- onOpen(), called when the server starts
- onTerminate(), called when the server stops
- onError(), called when an error occurred.
In our example, we need first to manage the connection request. With the onConnection() function, you can cancel a connection by returning Null or an object that will be used to manage the message sent or received. In the example below, we return the WSConnectionHandler class (described in the next section):
Function onConnection($wss : Object; $param : Object) : Object // call the VerifyAddress method to verify if the remote address is allow to connect If (VerifyAddress($param.request.remoteAddress)) // The connection is allowed // The WSConnectionHandler object returned is used // by 4D to instantiate the 4D.WebSocketConnection object // related to this connection return cs.WSConnectionHandler.new() Else // The connection is canceled return Null End if
Now, we want to log the start, close, and errors, so we create a WSServerHandler class:
Class constructor Function onOpen($wss : Object; $param : Object) LogFile("*** Server started") Function onTerminate($wss : Object; $param : Object) LogFile("*** Server closed") Function onError($wss : Object; $param : Object) LogFile("!!! Server error: "+$param.statusText)
This class allows you to manage different events during a given connection. To do so, you need to create a class with the below functions:
- onMessage(), called each time a message arrives through this connection.
- onOpen(), called when the 4D.WebSocketConnection object is created
- onTerminate(), called when the 4D.WebSocketConnection object is terminated
- onError(), called when an error occurred
To do our chat, this class will be the WSConnectionHandler class. It:
- warns other users that a new user is connected when the connection is opened
- warns other users that a user is disconnected when the connection is terminated
- broadcast the received messages to all the other chat clients connected when a message is received.
Function onMessage($ws : 4D.WebSocketConnection; $message : Object) // resend the message to all the chat clients This.broadcast($ws;$message.data) Function onOpen($ws : 4D.WebSocketConnection; $message : Object) // Send a message to the new connected user $ws.send("Welcome on the chat!") // Send message "New client connected" to all the other chat clients This.broadcast($ws;"New client connected") Function onTerminate($ws : 4D.WebSocketConnection; $message : Object) // Send message "Client disconnected" to all the other chat clients This.broadcast($ws;"Client disconnected") Function broadcast($ws : 4D.WebSocketConnection; $message : Text) var $client:4D.WebSocketConnection // resend the message to all the chat clients For each ($client; $ws.wss.connections) // Verify that the id is not the current connection If ($client.id#$ws.id) $client.send($message) End if End for each
All left is to start the websocket server using the classes created above. You must create a new process with the CALL WORKER command and instantiate the 4D.WebSocketServer in it:
var $handler:cs.WSSHandler $handler:=cs.WSServerHandler.new() CALL WORKER("WebSocketServer"; Formula(WSS:=4D.WebSocketServer.new($handler)))
As you can see above, we use a GLOBAL variable (WSS) to store the instance of our created handler. This global variable will exists forever (because a worker will not stop to exits as a process would do) and as long the variable exists, our Web Socket Server will exists. If a local variable would be used or a normal process would be used, the server would be killed as soon the variable is not existing anymore.
This code is designed to work with a local webserver at HTTP port 80 (line 18: const urlwss=”ws://127.0.0.1:80/”;).
Check out this feature with the HDI above and the documentation for more details!