Sockets provide the programmer with a facility to allow their applications to communicate over a network. This lesson shows how to use sockets to allow to LiveCode apps to talk to each other.
What are Sockets?
Sockets provide two networked machines with a bidirectional communication channel. One machine will be the server, listening in for connections and the other, the client, attempting to make a connection with the server. An example of this would be when you fetch a web page. Your web browser, or the client, attempts to make a connection to the web server. The web server, listening in for clients, will accept the connection and then proceed to handle the clients request. Here, the browser will ask the web server for the page, before the web server responds with the page data.
Servers are accessed via socket addresses, a combination of the server's IP address (or domain name) and a port number. The port can be thought as a connection point on the sever, like USB or Firewire ports, with each port serving a specific purpose. For example, web pages are served on port 80 (HTTP) , emails are sent via port 25 (SMTP).
Once two machines are connected, they can then communicate streams of bytes with each other. It's up to the client and server to format these byte streams into structured data chunks. A simple example would be an echo sever, which receives a stream of bytes from a client, assumes they form an ascii string and sends the string back to the client.
As noted previously, sockets use IP based networks. There are a range of transport layers that can be used on top of IP. In this lesson we will consider TCP sockets.
How to Use Sockets in LiveCode - Server Side
Using sockets in LiveCode is very simple. First of all let us consider a server: A server must listen in on a specific port for clients. To do this in LiveCode, we simply use the following command:
accept connections on port 1234 with message "clientConnected"
Here, our server will listen in on port 1234 for connecting clients. When a client is connected, the message "clientConnected" will be sent, detailing the newly opened socket. The server can now listen in on this socket for messages from the client:
on clientConnected pSocket
In the example above, the server is reading from the client in non-blocking mode. That means that the read request will exit instantly. When a data is received from the client, the "messageReceived" message will be sent, detailing the socket it came from and the data sent. The alternative to this is to omit the "with message". Here the read will function in blocking mode, not returning until data has been received from the client. When data is received, the read command will return and the data will be placed in it.
An additional parameter can be passed to the read command, detailing how much data we want to read. This can be a number of characters or lines (or any other chunk type - read from socket pSocket for 5 lines) or until a specific character is reached (read from socket pSocket until return).
Let us consider the read as detailed earlier, in non-blocking mode. When data is received from the client the "messageReceived" message will be sent. At this point, the server can choose to write a response to the client. Here, we will just echo the data back to the client using the write command.
on messageReceived pSocket, pMsg
Like the read command, write can be blocking or non-blocking. If we want a non blocking write, we simply specify the message to be sent when the write is complete. Note that in order to continue reading from the client, we must issue another read command.
When the server no longer wishes to communicate with the client, the socket can simply be closed with the command close socket tSocket. If the client closes the socket, the server will be sent a socketClosed message, detailing the socket that has been closed.
How to User Sockets in LiveCode - Client Side
Using sockets on the client side is equally as easy in LiveCode. To begin communicating with the server, we must first open a socket:
open socket to "host:port" with message "clientConnected"
Like the read and write commands, the callback message on the open command is optional, and defines if the command is blocking or non-blocking. If used in blocking mode, once connected the newly opened socket will be accessible via the it variable. In non-blocking mode, it will be passed as a parameter in the callback message.
If there has been an error connecting to the server the "socketError" message will be sent. (If the error is due to a problem finding the specified host, the error message is returned in the result, and no "socketError" message is sent.)
Let us consider the open command in non-blocking mode. Once the socket is opened, we can write a message to the server, read the response and then close the connection:
on clientConnected pSocket
The read and write commands function in exactly the same manner as defined previously for the server.
Creating a Simple Broadcast Server
Let us expand on these commands and create a simple broadcast server. Our broadcast server will listen in for connecting clients. Once a client is connected, the server will place the client on its list of connected clients before attempting to read a line of data from it. When a line of data is read from a client, that data will be broadcast to each connected client.
The list of connected clients will simply be a script variable holding the socket for each client separated by return. Our server will be started and stopped using simple start and stop commands. Two further handlers will be defined: One called when a client is connected, adding the client to the list before beginning reading. The other will be called when the read is complete, broadcasting the read message to all connected clients.
local sConnectedClients -- list of authorised clients
constant kPort = 8010
-- Start the server listening for connections. When a client attempts to
-- Stop the server listening for connections. Close all open communication
-- Sent when a client attempts to make a connection. Store the client
-- Sent when a line of text is read from a client. Broadcast that line of
-- Sent when a client disconnects. Remove the client from connected list
Creating a Broadcast Client
Now lets define our client. This is really easy. We just, as before, connected to the server. Once connected, we can begin sending messages by calling the "sendMessage" handler. This handler will simply write the message to the server using the socket we connected with.
The other task of the client is to listen for any messages broadcast by the server. Here, the client will just read from the server in non-blocking mode, logging the message read before continuing the read.
-- Connect the client to the given server. Will send a broadcastClientConnected
-- Disconnect the client from the broadcast server.
-- Write the given message to the communication channel the client
-- Sent once the client has connected to the broadcaset server.
-- Sent when a message has been received from the server. Output the
-- Sent when there is an error opening the socket. Log the error.
-- Sent when the connection to the server is closed by the server.
The image above shows our broadcast client communicating with the server and another client connected via telnet.
In the next edition we'll look at creating a communication protocol and a LiveCode Chat Server.