SETL provides support for UDP (the User Datagram Protocol). Although this is an ``unreliable'' protocol in that it does not include software mechanisms for retrying on transmission failures or data corruption (unlike TCP), and has restrictions on message length (a little under 65536 bytes), it is needed for applications that use broadcasting or multicasting, and it underlies such important applications as NFS (the Network File System), DNS (the Domain Name System), SNMP (the Simple Network Management Protocol), and various others noted by Stevens . It is also likely to continue to figure prominently in some modern performance-intensive roles such as multimedia.
Strictly speaking, UDP is a ``connectionless'' protocol--a program can use a single UDP socket to communicate with more than one host and port number--but it is convenient for most UDP client programs to maintain the fiction that there is a connection, by keeping a local record of each server host and port number.
This is modeled in SETL by distinguishing between client and server UDP sockets, both in the way the first argument to open is specified and in the operations that are subsequently available on the resulting file descriptor.
To be a UDP client, a program makes a call very similar to what it uses when it asks to be a TCP client: *
fd := open (host_and_port, `udp-client-socket');The host name and port number in the string form of the first argument here are separated by a colon, just as for TCP. UDP port numbers are entirely independent of TCP port numbers, though the IANA tries to register the same port number for both UDP and TCP when a given service is offered through both protocols. An integer representing an already open UDP client file descriptor is permitted as an alternative to the host_and_port argument, as usual. A successful UDP open makes available the operations *
send (fd, datagram);where datagram is simply a string, restricted in length as noted above, and *
datagram := recv fd;which receives a string into datagram. Send and recv are named after the Posix send and recv functions.
The following example program is the UDP analogue of the TCP client with which we began Section 3.1.1 [A Client]: *
fd := open (`galt.cs.nyu.edu:13', `UDP-client-socket');In the TCP case, opening the connection sufficed to prompt the server to return the desired information (the day of the week and so on), but when a UDP ``connection'' is opened, nothing is actually sent to the server. Even a null string will be wrapped in a UDP packet and sent by send, however, and that prods the server into action in this instance.
send (fd, `'); -- send an empty datagram
print (recv fd); -- receive and print a datagram
In fact, one of the most important practical differences between TCP and UDP is that there are no message boundaries in a TCP stream, whereas in UDP, every packet (datagram) is effectively a self-contained message, complete with a length that is implicit in its SETL string representation. For applications where reliability is not a concern and where all messages are known to fit within the limited size of datagrams, this can occasionally make UDP more convenient to use than TCP, though it is rarely the case that reliability can be so far ignored that it is acceptable for a program to sleep indefinitely waiting for a reply that never arrives, or to go into a confused state due to a message that has arrived twice. Both of these situations are perfectly possible with UDP, and the apparent convenience of UDP is but an evanescent illusion if the programmer has to write code to deal with them.
A UDP server socket is created in much the same way as a TCP server socket: *
fd := open (port_number, `udp-server-socket');Again, the port number specified to open is a string of decimal digits. The operations available on this kind of socket are: *
sendto (fd, host_and_port, datagram);and *
[host_and_port, datagram] := recvfrom fd;These are also named after the corresponding Posix functions.
Notice that the host name and port number must be specified afresh on every sendto call, and may be returned differently on every recvfrom call--the UDP server socket has no memory of any particular client after passing each datagram.
Following is the UDP analogue of the sequential TCP server of Section 3.1.2 [A Server]. Remarkably, it is even simpler: *
fd := open(`50013', `UDP-server-socket');
[whom] := recvfrom fd; -- ignore input datagram
sendto (fd, whom, date); -- send "date" datagram
Because a UDP server socket can send and receive data, unlike a TCP server socket (which can only produce new connection sockets using accept), it can actually be used in a client-like role. The following program is functionally equivalent to the very first UDP client example above: *
fd := open (`0', `udp-server-socket');This rather subverts the notion of a client, but is interesting as an illustration of how the only fundamental difference between a UDP client and server in SETL is in the lack of memory that a UDP server has. This client in disguise obtains an arbitrary ephemeral port on the local machine and then uses the associated file descriptor to send an empty datagram to a UDP server which as usual replies with an information-bearing datagram. It is worth emphasizing that SETL maintains the distinction between UDP clients and servers only in that it restricts file descriptors opened as client sockets (according to the mode argument to open) to the use of send and recv, and restricts those opened as server sockets to the use of sendto and recvfrom.
sendto (fd, `galt.cs.nyu.edu:13', `');
[-, datagram] := recvfrom fd;