Publications->Internet Server
Internet Server
Home
  What's New
  Downloads
Programs
  CSS Editor
  TCP/IP
  rInstaller
  R
  R2
  Java
Publications
  ODBC Browser
  Internet Server

Red Corona
Mail me




A General-Purpose Internet Server using Callbacks

by Richard Smith

Note: this page uses the fonts APL2741 or APLSans, both available from Causeway.

Abstract

During recent months, I have been involved in the writing of many Internet and Web servers, culminating of course in the multi-user client/server system written for Adapta DLS. It struck me that I was writing almost the same code every time I wrote a new server (socket event handlers such as Receive were the worst), so, following the principles of XP the answer was to put this functionality into a utility library (i.e. a namespace) and to call it from all of the servers.

The Criteria

There are two criteria which are extremely important in the design of such a namespace:

  • It should be easy to use
  • It should not restrict what you can do
These should be the platform upon which any design is based.

The Design

Responding to a Request

The server and client namespaces work using a callback system. By using APL style sockets, I could choose any message format I wanted, although you will see that the system I use could easily be converted to text sockets. A message consists of three parts:

  1. A message code. This is what tells the server what to run - it looks up this code in ‘callbacks and runs a suitable function.
  2. The content of the message. This can be any APL array.
  3. An end flag as specified in server.‘end_of_msg or client.‘end_of_msg.
The same system is used for both the client and the server once a connection has been established.

To respond to a message you need to register a callback. You can do this using the function Register, which takes a code on the left and an executable string on the right; for example:
1 Register '#.HelloWorld'
In the root namespace you should define a function along the lines of:

sock HelloWorld data
[1] © Acknowledge a message from the client
[2] sock server.Send 1,›('Received message ',•data)
... in a server workspace, or

HelloWorld;data
[1] © Acknowledge a message from the server
[2] 1 client.Send'Hello world'
[3] data„client.WaitForItem
[4] © do something with <<data>>

... in the client.

At this point it is best to take a step back and try to see what is going on here. Firstly, both these functions will reply to any message that starts with a code of 1 with another message of code 1 (yes, this means that if you set up your client and server like this they'll keep exchanging messages for ever). Also, it is a good idea to see the reasons behind the differences in these two functions:

  • There is no sock in the client. This is because the client can only be connected to one server, so client.Send knows which socket to use. This also explains the changed argument format for server.Send, as 3 arguments won't fit sensibly.
  • data is a parameter for the server, but you have to call WaitForItem in the client. This is because the server can't afford to wait for a slow request to arrive, as it might have other clients to talk to. However, the client can hang about and wait for the request to come through as that will be its only connection. (The client.WaitForItem function will time out eventually; you won't be left hanging for ever.)

Identifying Clients

This is all very well for replying to a message. However if you want to broadcast a message, or send a copy to the user logged in as administator, etc. then you need to know who's connected to your server. Whenever a new connection is received (specifically, in the Accept event of the socket), the ‘clients table is updated. This is a matrix of which the first two columns contain the socket number and IP address of a client; the rest of the table is application-specific data such as login name.

To send a message to a particular client, use something of the form:

('Sock',•‘clients[inx;1]) server.Send 1 'Hello world'

A broadcast is similar; use ('Sock',¨•¨‘clients[;1]) as the left argument to server.Send, or use a :for ... :in loop.

Error Handling

To handle errors, you need to register callbacks on the special codes ¯1 (socket closed) and ¯2 (error in socket). Obviously, do not try to write data into a connection that, for one reason or another, no longer exists!


This article is based on a presentation given at the VikAPL 2000 conference in Gilleleje, Denmark.


Page and site © Richard Smith 2001
Please send any comments to richard@redcorona.com - thank you.
 Top   Home