[next] [previous] [contents] [full-page]5.1 - Multi-Client WebSocket Applications
5.2 - WebSocket Application
5.3 - WebSocket Library
5.4 - WebSocket Application Examples
5.4.1 - Chat
5.4.2 - Echo
5.4.3 - Mouse
5.5 - WebSocket Configuration
5.5.1 - WebSocket Throttle
5.5.2 - WebSocket Command-Line
5.5.3 - WebSocket Version
5.6 - WebSocket Throughput
5.7 - WebSocket References
WebSocket is a capability introduced with HTML5, providing an asynchronous, bidirectional, full-duplex connection over which messages can be sent between agents, commonly a browser client and a server application. Compatible browsers provide a JavaScript interface that allows connections to be set up, maintained, messages exchanged, and connections closed down, using a callable and event-based interface.
WASD provides a WebSocket compatible scripting environment, one that is activated in the same fashion as an equivalent CGI/CGIplus/RTE and has an identical CGI environment (variables, streams, etc.) but which uses a unique HTTP response and communicates with its client using the WebSocket protocol.
Client supplied data is available to the script via the WEBSOCKET_INPUT mailbox and data from the script supplied via the WEBSOCKET_OUTPUT mailbox (indicated via CGI variables). Communication using a WebSocket requires the use of a framing protocol while WEBSOCKET_INPUT and WEBSOCKET_OUTPUT are opaque octet-streams providing communication to and from the WebSocket application. CGI variables WEBSOCKET_INPUT_MRS and WEBSOCKET_OUTPUT_MRS indicate the respective mailbox capacity.
The WASD server largely acts as a conduit for the WebSocket octet-stream. It provides the upgrade from HTTP to WebSocket protocol handshake and then connects the bidirectional data stream to the WebSocket application activated in WASD's scripting environment which then is required to perform all of the protocol requirements, etc. The baseline WASD implementation is via the wsLIB library (see below). The complexity and potential extensibility of the WebSocket protocol means this decoupling of server infrastructure and protocol implementation offers a number of advantages, including more straight-forward updates and bug fixing (just the library), and alternate, concurrent implementations.
Long-lived WebSocket scripts by default have timeouts and other limits set to infinite. If control is required it must be exercised using the appropriate mapping SETings or DCL callouts.
A single WASD WebSocket server application (script) can support multiple clients by using some form of multi-threading such as AST-based I/O, POSIX Threads, multi-thread interpreter environment, etc. The WASD wsLIB library (5.3 - WebSocket Library) supports native AST concurrency.
A WebSocket connection to a script is maintained by the WEBSOCKET_INPUT and WEBSOCKET_OUTPUT channels remaining connected to the script. If the script closes them (or the image or process exits, etc.) the WebSocket connection is closed. WebSocket requests are maintained as long as the script maintains them, for a CGIplus script, until it exits. If a CGIplus script requires to disconnect from a WebSocket client without exiting it must do so explicitly (by using the wsLIB close function (and associated WebSocket protocol close handshake), closing C streams, deassigning channels, etc.)
Of course this is the underlying mechanism allowing a single CGIplus script to maintain connections with multiple WebSocket clients. Provided the script remains connected to the WebSocket IPC mailboxes and processes that I/O asynchronously a single script can concurrently handle multiple clients. The script just processes each request it is given, adding the new client to the existing group (and removing them as the IPC indicates they disconnect).
Obviously the script must remain resident via CGIplus or RTE.
BYTLM
WebSocket scripting environments have the potential to consume significantly more BYTLM than those for HTTP scripting. The potentially large number of mailboxes associated with each scripting process (two per WebSocket connection) means that server and scripting account(s) BYTLM and associated quotas will need to be increased appropriately.
The server will continue to provide requests to the script for as long as it appears idle (i.e. the CGIplus sentinal EOF is returned even though concurrent processing may continue). Obviously a single scripting process cannot accept an unlimited number of concurrent WebSockets. When a script decides it can process no more it should not return the sentinal EOF from the most recent request until it is in a position to process more, when it then provides the EOF and the server again will supply another request.
The original request is access logged at request run-down (when the WebSocket is finally closed either because the client disconnected or the script closed its connection to the WEBSOCKET_.. mailboxes). The access log status is 101 (Switching Protocols) and the bytes rx and tx reflect the total for the duration.
WebSocket server applications are essentially CGIplus scripts and so have similar programming considerations (see 3 - CGIplus).
A WebSocket application however is typically long-lived and involves significant interaction between the participants. Either party can initiate independent communication with the other according to the required business logic.
A WASD WebSocket application relies on asynchronous I/O and other events to provide the communication granularity required for application interaction. The following pseudo-code shows the structure of one such hypothetical application. It accepts multiple, concurrent requests in it's main loop, creates the required WebSocket protocol supporting data structure, and then services application requirements in two event loops.
The first reads from the remote client and processes according to the business logic of client-initiated processing, asynchronously and/or synchronously writing data to the client. The second loop pushes data asynchronously to the client based on the application business logic providing those events. The close event occurs when the client or application close the WebSocket, or are otherwise disconnected, and finalises the request.
begin { initialise loop { wait for next client request open the WebSocket streams begin asynchronous read from client } } read event from client { business logic asynchronous write to client next asynchronous read from client } push event to client { business logic asynchronous write to client } close event { business logic close the WebSocket streams }
This basic structure is seen in all the WebSocket example applications.
wsLIB is a C code module that provides the basic infrastructure for building WebSocket applications to run under WASD scripting.
It abstracts much of the required functionality into a few callable functions using optional string descriptors so as to minimise dependency on the C language and on knowing the internals of the library data structure. The list of functions and associated parameters would unnecessarily clutter this document and so WebSocket application designers and programmers are referred to the descriptive prologue in the library code module itself (see below). While wsLIB usage is relatively straight-forward, the detail of any multi-threaded, asynchronous application can be daunting and so the example WebSocket applications (scripts) should be used as a wsLIB reference and tutorial.
The library contains WATCH points. Network [x]Data and [x]Script provide a useful combination of traffic data. The library function WsLibWatchScript() allows WebSocket applications (scripts) to provide additional WATCHable information via the [x]Script item.
WASD_ROOT:[SRC.WEBSOCKET]WSLIB.C
The WASD WebSocket implementation provides a number of scripting examples illustrating WebSocket programming basics and the use of the WASD wsLIB library. All of these illustrate multi-client support using asynchonouse I/O. Each has a server component (the C code) and a client component (the HTML file containing the JavaScript code).
The following examples concentrate on the server C code as this is WASD-specific. Any WebSocket reference can adequitely cover the essentials of the client JavaScript implementation.
Does this browser support WebSockets?
NO!
This almost has to be the classic example of asynchronous, bidirectional communications without HTTP kludges. Each connected client can enter a message and it is distributed to all connected clients.
WASD_ROOT:[SRC.WEBSOCKET]WS_CHAT.C
Each connected client can enter a message which is then returned to them.
WASD_ROOT:[SRC.WEBSOCKET]WS_ECHO.C
The HTML/JavaScript/WebSocket client end connects to the script. Each mouse movement is then reported to the script. These data are distributed to all connected clients. This provides an asynchronous update facility from all clients to all clients.
The script is implemented using VMS I/O-driven ASTs. The code is also interesting because it implements all required functionality explicitly; no WebSocket library functions are employed.
WASD_ROOT:[SRC.WEBSOCKET]WS_MOUSE.C
WebSocket server applications are essentially CGIplus scripts and so are mapped and activated in the same fashion as any other CGIplus script (3.3 - Other Considerations).
Throttle mapping rules may be applied to WebSocket requests. There is however, a fundamental difference between request throttling and WebSocket throttling though. HTTP request throttling applies control to the entire life of the response. WebSocket throttling applies only to establishing connection to the underlying server application. Once the script responds to accept the connection or reject it throttling is concluded.
Long-lived WebSocket connections are considered less suitable to full life-cycle throttling and should use internal mechanisms to control resource utilisation (i.e. using the delayed sentinal EOF mechanism described in 5.1 - Multi-Client WebSocket Applications). Essentially it is used to limit the impact concurrent requests have on the number of supporting script processes allowed to be instantiated to support the application.
For example, the rule
set /cgi-bin/ws_application throttle=1will only allow one new request at a time attempt to connect to and/or create a WebSocket application script. This will effectively limit the number of supporting processes to one however many clients wish to connect.
To support concurrent requests distributed across multiple application scripts specify the throttle value as the number of separate scripts
set /cgi-bin/ws_application throttle=5and if each script is to support a maximum number of individual connections then have it delay the EOF sentinal (described above) to block the server selecting it for the next request. Requests will be allocated until all processes have blocked after which they will be queued.
To return a "too busy" 503 to clients (almost) immediately upon all processes become full and blocking (maximum application concurrency has been reached) then set the "t/o-busy" value to 1 second.
set /cgi-bin/ws_application throttle=5,,,,,1
Unconditionally disconnects all WebSocket applications.
$ HTTPD /DO=WEBSOCKET=DISCONNECT
For VMS V8.2 and later, more selective disconnects are possible. Disconnects WebSocket applications with connect number, with matching script names, and with matching scripting account usernames, respectively.
$ HTTPD /DO=WEBSOCKET=DISCONNECT=number $ HTTPD /DO=WEBSOCKET=DISCONNECT=SCRIPT=pattern $ HTTPD /DO=WEBSOCKET=DISCONNECT=USER=pattern
CGI variable WEBSOCKET_VERSION provides the WebSocket protocol version number negotiated by the server at connection establishment.
At the time of writing the WebSocket protocol has just gone to IETF Draft RFC and has during development been very volatile and may continue to be so as it evolves. WASD supports the current base protocol number and any higher. At some time in the future it may be necessary to constrain that to a supported version number or set of numbers. Defining the logical name WASD_WEBSOCKET_VERSION to be one or more comma-separated numbers will limit the supported protocol versions. For example
$ DEFINE /TABLE=WASD_TABLE WASD_WEBSOCKET_VERSION "10, 9, 8"limits requests to protocol version 10 (current), 9 (earlier) and 8 (earliest). Logical name is only tested once for each server startup (the first WebSocket request received). This logical name only controls server handshake support and behaviour. The underlying WebSocket library used by the application (e.g. wsLIB.c) supports version idiosyncracies for other aspects.
This string is also used as the list of versions reported in a 426 (upgrade required) response when a client makes a request using an unsupported version.
The raw WebSocket throughput of a platform (hardware plus VMS plus TCP/IP stack plus WASD and optionally network infrastructure) can be measured using the WSB utility. Measures of raw message and byte throughput for a series of messages of various sizes can provide useful information on the underlying maximum messaging characteristics of that platform.
The following example shows usage on an Alpha XP1000:
$ WSB == "$WASD_EXE:WSB" $ WSB /DO=ECHO /THROUGHPUT /REPEAT=1000 /SIZE=0 %WSB-I-STATS, 1 total connections Duration: 0.303 seconds Tx: 1000 msgs at 3303/S, 0 bytes at 0 B/S Rx: 1000 msgs at 3303/S, 0 bytes at 0 B/S Total: 2000 msgs at 6607/S, 0 bytes at 0 B/S $ WSB /DO=ECHO /THROUGHPUT /REPEAT=1000 /SIZE=16 %WSB-I-STATS, 1 total connections Duration: 0.349 seconds Tx: 1000 msgs at 2869/S, 16.0 kbytes at 45.9 kB/S Rx: 1000 msgs at 2869/S, 16.0 kbytes at 45.9 kB/S Total: 2000 msgs at 5737/S, 32.0 kbytes at 91.8 kB/S $ WSB /DO=ECHO /THROUGHPUT /REPEAT=1000 /SIZE=64 %WSB-I-STATS, 1 total connections Duration: 0.359 seconds Tx: 1000 msgs at 2783/S, 64.0 kbytes at 178.1 kB/S Rx: 1000 msgs at 2783/S, 64.0 kbytes at 178.1 kB/S Total: 2000 msgs at 5566/S, 128.0 kbytes at 356.2 kB/S $ WSB /DO=ECHO /THROUGHPUT /REPEAT=1000 /SIZE=256 %WSB-I-STATS, 1 total connections Duration: 0.607 seconds Tx: 1000 msgs at 1646/S, 256.0 kbytes at 421.5 kB/S Rx: 1000 msgs at 1646/S, 256.0 kbytes at 421.5 kB/S Total: 2000 msgs at 3293/S, 512.0 kbytes at 843.0 kB/S $ WSB /DO=ECHO /THROUGHPUT /REPEAT=1000 /SIZE=1024 %WSB-I-STATS, 1 total connections Duration: 0.659 seconds Tx: 1000 msgs at 1517/S, 1.0 Mbytes at 1.6 MB/S Rx: 1000 msgs at 1517/S, 1.0 Mbytes at 1.6 MB/S Total: 2000 msgs at 3034/S, 2.0 Mbytes at 3.1 MB/S $ WSB /DO=ECHO /THROUGHPUT /REPEAT=1000 /SIZE=65k %WSB-I-STATS, 1 total connections Duration: 10.279 seconds Tx: 1000 msgs at 97/S, 65.0 Mbytes at 6.3 MB/S Rx: 1000 msgs at 97/S, 65.0 Mbytes at 6.3 MB/S Total: 2000 msgs at 195/S, 130.0 Mbytes at 12.6 MB/S
For more information see the description in the prologue of the program. (A zero-size message is legitimate with the WebSocket protocol.)