# Network sockets

This chapter is dedicated to the network socket objects which is a subpart of the standard network module.

## TCP client socket

The `TcpClient` class creates an TCP client object by address and port. The address can be either a string or an `Address` object. During the object construction, the connection is established with the server. Once the connection is established, the client can use the `read` and `write` method to communicate with the server. The `TcpClient` class is derived from the `Socket` class which is derived from the `InputStream` and `OutputStream` classes.

### Day time client

The simplest example is a client socket which communicates with the daytime server. The server is normally running on all machines and is located at port 13.

# get the network module interp:library "afnix-net"

# get the daytime server port const port (afnix:net:get-tcp-service "daytime")

# create a tcp client socket const s (afnix:net:TcpClient "localhost" port)

# read the data - the server close the connection while (s:valid-p) (println (s:readln))

Example 3201.als in the example directory prints the day time of the local host without argument or the day time of the argument. Feel free to use it with www.afnix.org. If the server you are trying to contact does not have a day time server, an exception will be raised and the program terminates.

zsh> axi 3201.als www.afnix.org

### HTTP request example

Another example which illustrates the use of the `TcpClient` object is a simple client which download a web page. At this stage we are not concern with the URL but rather the mechanics involved. The request is made by opening a TCP client socket on port 80 (the HTTP server port) and sending a request by writing some HTTP commands. When the commands have been sent, the data sent by the server are read and printed on the standard output. Note that this example is not concerned by error detection.

# fetch an html page by host and page # usage: axi 3203.als [host] [page]

# get the network module interp:library "afnix-net" interp:library "afnix-sys"

# connect to the http server and issue a request const send-http-request (host page) { # create a client sock on port 80 const s (afnix:net:TcpClient host 80) const saddr (s:get-socket-address)

# format the request s:writeln "GET " page " HTTP/1.1" s:writeln "Host: " (saddr:get-canonical-name) s:writeln "Connection: close" s:writeln "User-Agent: afnix tcp client example" s:newline

# write the result while (s:valid-p) (println (s:readln)) }

# get the argument if (!= (interp:argv:length) 2) (afnix:sys:exit 1) const host (interp:argv:get 0) const page (interp:argv:get 1)

# send request send-http-request host page

## UDP client socket

UDP client socket is similar to TCP client socket. However, due to the unreliable nature of UDP, UDP clients are somehow more difficult to manage. Since there is no flow control, it becomes more difficult to assess whether or not a datagram has reached its destination. The same apply for a server, where a reply datagram might be lost. The `UdpClient` class is the class which creates a UDP client object. Its usage is similar to the `TcpClient`.

### The time client

The UDP time server normally runs on port 37 is the best place to enable it. A UDP client is created with the `UdpClient` class. Once the object is created, the client sends an empty datagram to the server. The server send a reply datagram with 4 bytes, in network byte order, corresponding to the date as of January 1st 1900. Example 3204.als prints date information after contacting the local host time server or the host specified as the first argument.

# get the libraries interp:library "afnix-net" interp:library "afnix-sys"

# get the daytime server port const port (afnix:net:get-udp-service "time")

# create a client socket and read the data const print-time (host) { # create a udp client socket const s (afnix:net:UdpClient host port) # send an empty datagram s:write # read the 4 bytes data and adjust to epoch const buf (s:read 4) const val (- (buf:get-quad) 2208988800) # format the date const time (afnix:sys:Time val) println (time:format-date) ' ' (time:format-time) }

# check for one argument or use localhost const host (if (== (interp:argv:length) 0) "localhost" (interp:argv:get 0)) print-time host

This example calls for several comments. First the `write` method without argument sends an empty datagram. It is the datagram which trigger the server. The `read` method reads 4 bytes from the reply datagram and places them in a `Buffer` object. Since the bytes are in network byte order, the conversion into an integer value is done with the `get-quad` method. Finally, in order to use the `Time` class those epoch is January 1st 1970, the constant `2208988800` is subtracted from the result. Remember that the time server sends the date in reference to January 1st 1900. More information about the time server can be found in RFC738.

### More on reliability

The previous example has some inherent problems due to the unreliability of UDP. If the first datagram is lost, the `read` method will block indefinitely. Another scenario which causes the `read` method to block is the loss of the server reply datagram. Both problem can generally be fixed by checking the socket with a timeout using the `valid-p` method. With one argument, the method timeout and return false. In this case, a new datagram can be send to the server. Example 3205.als illustrates this point. We print below the extract of code.

# create a client socket and read the data const print-time (host) { # create a udp client socket const s (afnix:net:UdpClient host port) # send an empty datagram until the socket is valid s:write # retransmit datagram each second while (not (s:valid-p 1000)) (s:write) # read the 4 bytes data and adjust to epoch const buf (s:read 4) const val (- (buf:get-quad) 2208988800) # format the date const time (afnix:sys:Time val) println (time:format-date) ' ' (time:format-time) }

Note that this solution is a naive one. In the case of multiple datagrams, a sequence number must be placed because there is no clue about the lost datagram. A simple rule of thumb is to use TCP as soon as reliability is a concern, but this choice might not so easy.

### Error detection

Since UDP is not reliable, there is no simple solution to detect when a datagram has been lost. Even worse, if the server is not running, it is not easy to detect that the client datagram has been lost. In such situation, the client might indefinitely send datagram without getting an answer. One solution to this problem is again to count the number of datagram re-transmit and eventually give up after a certain time.

## Socket class

The `Socket` class is the base class for both `TcpClient` and `UdpClient`. The class provides methods to query the socket port and address as well as the peer port and address. Note at this point that the UDP socket is a connected socket. Therefore, these methods will work fine. The `get-socket-address` and `get-socket-port` returns respectively the address and port of the connected socket. The `get-peer-address` and `get-peer-port` returns respectively the address and port of the connected socket's peer. Example 3206.als illustrates the use of these methods.

# create a client socket and read the data const print-socket-info (host) { # create a tcp client socket const s (afnix:net:TcpClient host port) # print socket address and port const saddr (s:get-socket-address) const sport (s:get-socket-port) println "socket ip address : " (saddr:get-address) println "socket canonical name : " (saddr:get-canonical-name) println "socket port : " sport # print peer address and port const paddr (s:get-peer-address) const pport (s:get-peer-port) println "peer ip address : " (paddr:get-address) println "peer canonical name : " (paddr:get-canonical-name) println "peer port : " pport }

### Socket predicates

The `Socket` class is associated with the `socket-p` predicate. The respective client objects have the `tcp-client-p` predicate and `udp-client-p` predicate.

## TCP server socket

The `TcpServer` class creates an TCP server object. There are several constructors for the TCP server. In its simplest form, without port, a TCP server is created on the `localhost` with an ephemeral port number (i.e port 0 during the call). With a port number, the TCP server is created on the `localhost`. For a multi-homed host, the address to use to run the server can be specified as the first argument. The address can be either a string or an `Address` object. In both cases, the port is specified as the second argument. Finally, a third argument called the `backlog` can be specified to set the number of acceptable incoming connection. That is the maximum number of pending connection while processing a connection. The following example shows various ways to create a TCP server.

trans s (afnix:net:TcpServer) trans s (afnix:net:TcpServer 8000) trans s (afnix:net:TcpServer 8000 5) trans s (afnix:net:TcpServer "localhost" 8000) trans s (afnix:net:TcpServer "localhost" 8000 5) trans s (afnix:net:TcpServer (Address "localhost") 8000) trans s (afnix:net:TcpServer (Address "localhost") 8000 5)

### Echo server example

A simple echo server can be built and tested with the standard `telnet` application. The application will echo all lines that are typed with the `telnet` client. The server is bound on the port 8000, since ports 0 to 1024 are privileged ports.

# get the network module interp:library "afnix-net"

# create a tcp server on port 8000 const srv (afnix:net:TcpServer 8000)

# wait for a connection const s (srv:accept)

# echo the line until the end while (s:valid-p) (s:writeln (s:readln))

The `telnet` session is then quite simple. The line hello world is echoed by the server.

zsh> telnet localhost 8000 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. hello world ^D

### The accept method

The previous example illustrates the mechanics of a server. When the server is created, the server is ready to accept connection. The `accept` method blocks until a client connect with the server. When the connection is established, the `accept` method returns a socket object which can be used to read and write data.

### Multiple connections

One problem with the previous example is that the server accepts only one connection. In order to accept multiple connection, the `accept` method must be placed in a loop, and the server operation in a thread (There are some situations where a new process might be more appropriate than a thread). Example 3302.als illustrates such point.

# get the network module interp:library "afnix-net"

# this function echo a line from the client const echo-server (s) { while (s:valid-p) (s:writeln (s:readln)) }

# create a tcp server on port 8000 const srv (afnix:net:TcpServer 8000)

# wait for a connection while true { trans s (srv:accept) launch (echo-server s) }

## UDP server socket

The `UdpServer` class is similar to the `TcpServer` object, except that there is no backlog parameters. In its simplest form, the UDP server is created on the `localhost` with an ephemeral port (i.e port 0). With a port number, the server is created on the `localhost`. For a multi-homed host, the address used to run the server can be specified as the first argument. The address can be either a string or an `Address` object. In both cases, the port is specified as the second argument.

trans s (afnix:net:UdpServer) trans s (afnix:net:UdpServer 8000) trans s (afnix:net:UdpServer "localhost" 8000) trans s (afnix:net:UdpServer (Address "localhost") 8000)

### Echo server example

The echo server can be revisited to work with udp datagram. The only difference is the use of the `accept` method. For a UDP server, the method return a `Datagram` object which can be used to read and write data.

# get the network module interp:library "afnix-net"

# create a udp server on port 8000 const srv (afnix:net:UdpServer 8000)

# wait for a connection while true { trans dg (srv:accept) dg:writeln (dg:readln) }

### Datagram object

With a UDP server, the `accept` method returns a `Datagram` object. Because a UDP is connection-less, the server has no idea from whom the datagram is coming until that one has been received. When a datagram arrives, the `Datagram` object is constructed with the peer address being the source address. Standard i/o methods can be used to read or write. When a write method is used, the data are sent back to the peer in a form of another datagram.

# wait for a datagram trans dg (s:accept)

# assert datagram type assert true (datagram-p dg)

# get contents length println "datagram buffer size : " (dg:get-buffer-length)

# read a line from this datagram trans line (dg:readln)

# send it back to the sender s:writeln line

### Input data buffer

For a datagram, and generally speaking, for a UDP socket, all input operations are buffered. This means that when a datagram is received, the `accept` method places all data in an input buffer. This means that a read operation does not necessarily flush the whole buffer but rather consumes only the requested character. For example, if one datagram contains the string hello world. A call to `readln` will return the entire string. A call to read will return only the character 'h'. Subsequent call will return the next available characters. A call like `read 5` will return a buffer with 5 characters. Subsequent calls will return the remaining string. In any case, the `get-buffer-length` will return the number of available characters in the buffer. A call to `valid-p` will return true if there are some characters in the buffer or if a new datagram has arrived.

Care should be taken with the `read` method. For example if there is only 4 characters in the input buffer and a call to read for 10 characters is made, the method will block until a new datagram is received which can fill the remaining 6 characters. Such situation can be avoided by using the `get-buffer-length` and the `valid-p` methods. Note also that a timeout can be specified with the `valid-p` method.

## Low level socket methods

Some folks always prefer to do everything by themselves. Most of the time for good reasons. If this is your case, you might have to use the low level socket methods. Instead of using a client or server class, the implementation let's you create a `TcpSocket` or `UdpSocket`. Once this done, the `bind`, `connect` and other methods can be used to create the desired connection.

### A socket client

A simple TCP socket client is created with the `TcpSocket` class. Then the `connect` method is called to establish the connection.

# create an address and a tcp socket const addr (afnix:net:Address "localhost") const sid (afnix:net:TcpSocket) # connect the socket sid:connect 13 addr

Once the socket is connected, normal read and write operations can be performed. After the socket is created, it is possible to set some options. A typical one is `NO-DELAY` which disable the Naggle algorithm.

# create an address and a tcp socket const addr (afnix:net:Address "localhost") const sid (afnix:net:TcpSocket) # disable the naggle algorithm sid:set-option sid:NO-DELAY true # connect the socket sid:connect 13 addr