Working with UDP sockets

Unlike TCP sockets, UDP sockets do not set up an enduring connection between two end points. Instead individually addressed datagrams are sent to either a specific end point or in a broadcast to a range of possible end points. When a datagram is read from a UDP socket, the sender's address can be inspected. This makes UDP very useful for servers that need to service multiple clients on an ad hoc basis.

However UDP has some characteristics that make programming them more challenging than the more straight forward TCP variety. While UDP packets are checksummed, there is no guranteed delivery and no quaranteed sequencing of multiple packets. The application must be robust enough to cope with losing packets or receiving out of order datagrams. Datagrams are also limited in size with the exact size network dependent. However most systems today support at least 8192 bytes as this is the datagram size used in NFS.

To open a UDP socket on a port, specify just the port and add the UDP=Y qualifier e.g.

s = OPEN ":2001,UDP=Y", "@"

This is similar to the way TCP servers are created though such a UDP socket can be used for both sending and receiving. The port number or service name refers to the port for this endpoint. If the port is zero then the operating system will allocate the next available unused port. The port number bound to the socket can be discovered in the immediately after the OPEN in the .OPTIONS_HASH_IPport$ field of the $OPTIONS # structure for the socket.

To send a datagram the destination IP address and port must first be filled into the .OPTIONS_HASH_IPaddr$ and .OPTIONS_HASH_IPport$ fields of the $OPTIONS # structure for the socket. KCML defines a KCML_OPTIONS_HASH built in DEFRECORD that can be used to access these fields. The data is then sent with a conventional WRITE # statement. Remember that there is no guarantee that the datagram will arrive at its destination. The write will succeed even if the destination address does not exist.

The $IF statement can be used to test if a socket has available data. To receive a datagram use READ# in the normal way. The READ will block if there is no data available. It will return the size of the data read even if this is less than the buffer size. After the read the application can inspect the the .OPTIONS_HASH_IPaddr$ and .OPTIONS_HASH_IPport$ fields of the $OPTIONS # structure to get the senders address.

Broadcasts

The UDP protocol supports broadcasting packets to more than one possible end point. To open a socket for broadcasts, both sending or receiving, add the BRO=Y qualifier. There are several possible broadcast addresses that can be used when sending a datagram:

KCML does not support multicasting.

Example UDP server and client

Open a UDP Server socket on port 2001 and wait for a request, when a request has been made return the string capitalised.

s = OPEN ":2001,UDP=Y", "@"
REPEAT
    rc = $IF #s
    IF (rc > 0 )
        nread = READ #s,sreadbuf$
        IF (nread>0)
            REDIM swritebuf$ = $UPPER(STR(sreadbuf$,,nread))
            nwrite = WRITE #s,swritebuf$
        END IF
    END IF
UNTIL TRUE

We can then use netcat to establish a connection and send and recieve data

nc -u localhost 2001
hello
HELLO

Simple client that opens a UDP socket, opening a socket on port 0 will open a socket on a random available port. We later bind the socket to port 2001 using $OPTIONS, the ip address is also set to INADDR_LOOPBACK aka localhost. Write to the server that we setup previously and print the response. IP address of a hostname can be obtained using the 'KCML_GetHostByName function.

DIM sreadbuf$1024
hskt = OPEN ":0,UDP=Y,RUA=N","@"
FLD($OPTIONS #hskt.options_hash_ipaddr$) = HEX(7F00 0001)
FLD($OPTIONS #hskt.options_hash_ipport) = 2001
nsent = WRITE #hskt,"apples"
nread = READ #hskt,srecvbuf$
PRINT STR(srecvbuf$,,nread)
kcml -p udpclient.src
APPLES

See also:

OPEN#, CLOSE#, READ#, WRITE#, $IF, $OPTIONS# 'KCML_GetHostByName
Secure sockets with SSL
Working with TCP sockets