Datagrams

Content:

  1. Introduction
  2. UDP
  3. Examples
  4. Client
  5. Server

Introduction:

UDP datagrams are more specialized and less widely used by applications than TCP sockets. But it can still be relevant for developers to know about the option of using UDP instead of TCP.

UDP:

Almost everybody knows that TCP is a reliable protocol and UDP is an unreliable protocol.

That means that for TCP the stream of bytes sent will be received as the same stream of bytes. The TCP protocol will manage packets: missing packets will be requested resent, packets will be reordered to same order as sent and duplicate packets will be dicarded.

UDP is very different. An UDP datagram is fundamentally just a network packet. The API provide a way to send datagrams/packets and a way to receive datagrams/packets. The rest is left to the application.

There is no connection. The sender just send a datagram/packet to an address and port. The receiver just listen on a port, receive datagram/packet and can see address and port of sender.

So with UDP:

Obviously the frequency of these situations depend on the traffic and the quality of the network. But correct application code will handle such situations if necessary.

Note that even though UDP is an unreliable protocol, then protocols on top of UDP can be reliable - they just need to handle these situations properly.

An obvious question would be: why not always use TCP and not UDP?

Two reasons:

I think it is rare that UDP wil be the best choice. But in a few cases it will be.

Example

The examples will show a very simple protocol. Client send a datagram with 7 bytes. Server return a datagram with the same bytes incremented by one.

The examples are not trying to provide any kind of reliability.

Client:

Java comes with a DatagramSocket class to send/receive and a DatagramPacket class with data and address/port.

package udp;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class Client {
    private static final String SERVER = "localhost";
    private static final int SERVER_PORT = 12345;
    private static final int CLIENT_PORT = 12346;
    private static final int SIZE = 7;
    private static final int MAX_SIZE = 1000;
    public static void main(String[] args) throws IOException {
        DatagramSocket dgs = new DatagramSocket(CLIENT_PORT);
        for(int i = 0; i < 3; i++) {
            // setup data
            byte[] buf1 = new byte[SIZE];
            for(int j = 0; j < buf1.length; j++) buf1[j] = (byte)(i + j);
            // send packet
            DatagramPacket dp1 = new DatagramPacket(buf1, buf1.length);
            dp1.setAddress(InetAddress.getByName(SERVER));
            dp1.setPort(SERVER_PORT);
            dgs.send(dp1);
            // receive packet
            DatagramPacket dp2 = new DatagramPacket(new byte[MAX_SIZE], MAX_SIZE);
            dgs.receive(dp2);
            // print data
            byte[] buf2 = dp2.getData(); 
            int len = dp2.getLength();
            for(int j = 0; j < len; j++) System.out.print(" " + buf2[j]);
            System.out.println();
        }
        dgs.close();
    }
}

.NET comes with a UdpClient class to send/receive.

using System;
using System.Net;
using System.Net.Sockets;

namespace UDP.Client
{
    public class Program
    {
        private const string SERVER = "localhost";
        private const int SERVER_PORT = 12345;
        private const int CLIENT_PORT = 12346;
        private const int SIZE = 7;
        private const int MAX_SIZE = 1000;
        public static void Main(string[] args)
        {
            using(UdpClient cli = new UdpClient(CLIENT_PORT))
            {
                for(int i = 0; i < 3; i++)
                {
                    // setup data
                    byte[] buf1 = new byte[SIZE];
                    for(int j = 0; j < buf1.Length; j++) buf1[j] = (byte)(i + j);
                    // send packet
                    cli.Send(buf1, buf1.Length, SERVER, SERVER_PORT);
                    // receive packet
                    IPEndPoint from = new IPEndPoint(IPAddress.Any, 0);
                    byte[] buf2 = cli.Receive(ref from);
                    // print data
                    for(int j = 0; j < buf2.Length; j++) Console.Write(" " + buf2[j]);
                    Console.WriteLine();
                }
            }
            Console.ReadKey();
        }
    }
}

.NET comes with a UdpClient class to send/receive.

Imports System
Imports System.Net
Imports System.Net.Sockets

Namespace UDP.Client
    Public Class Program
        Private Const SERVER As String = "localhost"
        Private Const SERVER_PORT As Integer = 12345
        Private Const CLIENT_PORT As Integer = 12346
        Private Const SIZE As Integer = 7
        Private Const MAX_SIZE As Integer = 1000
        Public Shared Sub Main(args As String())
            Using cli As New UdpClient(CLIENT_PORT)
                For i As Integer = 0 To 2
                    ' setup data
                    Dim buf1 As Byte() = New Byte(SIZE - 1) {}
                    For j As Integer = 0 To buf1.Length - 1
                        buf1(j) = CByte(i + j)
                    Next
                    ' send packet
                    cli.Send(buf1, buf1.Length, SERVER, SERVER_PORT)
                    ' receive packet
                    Dim from As New IPEndPoint(IPAddress.Any, 0)
                    Dim buf2 As Byte() = cli.Receive(from)
                    ' print data
                    For j As Integer = 0 To buf2.Length - 1
                        Console.Write(" " & buf2(j))
                    Next
                    Console.WriteLine()
                Next
            End Using
            Console.ReadKey()
        End Sub
    End Class
End Namespace

The same socket API used for TCP can actually be used for UDP as well.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <sys/socket.h>
#include <netdb.h>
#endif

#include <errno.h>

#define SERVER "localhost"
#define SERVER_PORT "12345"
#define CLIENT_PORT "12346"
#define SIZE 7
#define MAX_SIZE 1000

int main()
{
    int sd, status, i, j, len, fromlen;
    char *buf1, *buf2;
    struct addrinfo hints, *local, *remote;
    struct sockaddr from;
#ifdef WIN32
    WSADATA WSAData;
    WSAStartup(0x0101, &WSAData);
#endif
    /* lookup client */
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_DGRAM;
    hints.ai_protocol = IPPROTO_UDP;
    hints.ai_flags = 0;
    status = getaddrinfo("localhost", CLIENT_PORT, &hints, &local);
    if(status != 0)
    {
        printf("Error looking up self\n");
        exit(0);
    }
    /* lookup server */
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_DGRAM;
    hints.ai_protocol = IPPROTO_UDP;
    hints.ai_flags = 0;
    status = getaddrinfo(SERVER, SERVER_PORT, &hints, &remote);
    if(status != 0)
    {
        printf("Error looking up server: %s\n", SERVER);
        exit(0);
    }
    /* create socket */
    sd = socket(AF_INET, SOCK_DGRAM, 0);
    if(sd < 0)
    {
        printf("Error creating socket: %s\n", strerror(errno));
        exit(0);
    }
    /* bind socket */
    status = bind(sd, local->ai_addr, local->ai_addrlen);
    if(status < 0)
    {
        printf("Error binding socket: %s\n", strerror(errno));
        exit(0);
    }
    for(i = 0; i < 3; i++)
    {
        /* setup data */
        buf1 = malloc(SIZE);
        for(j = 0; j < SIZE; j++) buf1[j] = (i + j);
        /* send packet */
        len = sendto(sd, buf1, SIZE, 0, remote->ai_addr, remote->ai_addrlen);
        if(len < 0)
        {
            printf("Error sending: %s\n", strerror(errno));
            exit(0);
        }
        /* receive packet */
        buf2 = malloc(MAX_SIZE);
        fromlen = sizeof(from);
        len = recvfrom(sd, buf2, MAX_SIZE, 0, &from, &fromlen);
        if(len < 0)
        {
            printf("Error receiving: %s\n", strerror(errno));
            exit(0);
        }
        /* print data */
        for(j = 0; j < len; j++) printf(" %d", (int)buf2[j]);
        printf("\n");
        /* */
        free(buf1);
        free(buf2);
    }
    /* close socket */
#ifdef WIN32
    closesocket(sd);
    WSACleanup();
#else
    close(sd);
#endif
    return 0;
}

The well known Indy library comes with a TIdUdpClient class to send/receive.

program UdpClient;

uses IdUDPClient, IdGlobal, SysUtils;

const
  SERVER = 'localhost';
  SERVER_PORT = 12345;
  CLIENT_PORT = 12346;
  SIZE = 7;
  MAX_SIZE = 100; (* 1000 not valid *)

var
  cli : TIdUDPClient;
  buf1, buf2 : TIdBytes;
  i, j, len : integer;

begin
  cli := TIdUDPClient.Create;
  cli.BoundPort := CLIENT_PORT;
  for i := 0 to 2 do begin
    (* setup data *)
    SetLength(buf1, SIZE);
    for j := 0 to Length(buf1) - 1 do begin
      buf1[j] := i + j;
    end;
    (* send packet *)
    cli.SendBuffer(SERVER, SERVER_PORT, buf1);
    (* receive packet *)
    SetLength(buf2, MAX_SIZE);
    len := cli.ReceiveBuffer(buf2);
    (* print data *)
    for j := 0 to len - 1 do begin
      write(' ' + IntToStr(buf2[j]));
    end;
    writeln;
  end;
  cli.Free;
end.

Server:

Java comes with a DatagramSocket class to send/receive and a DatagramPacket class with data and address/port.

package udp;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;

public class Server {
    private static final int SERVER_PORT = 12345;
    private static final int MAX_SIZE = 1000;
    public static void main(String[] args) throws IOException {
        DatagramSocket dgs = new DatagramSocket(SERVER_PORT);
        while(true) {
            // receive packet
            DatagramPacket dp1 = new DatagramPacket(new byte[MAX_SIZE], MAX_SIZE);
            dgs.receive(dp1);
            // work on data
            byte[] buf1 = dp1.getData();
            int len = dp1.getLength();
            byte[] buf2 = new byte[len];
            for(int i = 0; i < buf2.length; i++) {
                buf2[i] = (byte)(buf1[i] + 1);
            }
            // send packet
            DatagramPacket dp2 = new DatagramPacket(buf2, buf2.length);
            dp2.setAddress(dp1.getAddress());
            dp2.setPort(dp1.getPort());
            dgs.send(dp2);
        }
        //dgs.close();
    }
}

.NET comes with a UdpClient class to send/receive.

using System;
using System.Net;
using System.Net.Sockets;

namespace UDP.Server
{
    public class Program
    {
        private const int SERVER_PORT = 12345;
        private const int MAX_SIZE = 1000;
        public static void Main(string[] args)
        {
            using(UdpClient cli = new UdpClient(SERVER_PORT))
            {
                while(true)
                {
                    // receive packet
                    IPEndPoint from = new IPEndPoint(IPAddress.Any, 0);
                    byte[] buf1 = cli.Receive(ref from);
                    // work on data
                    int len = buf1.Length;
                    byte[] buf2 = new byte[len];
                    for(int i = 0; i < buf2.Length; i++)
                    {
                        buf2[i] = (byte)(buf1[i] + 1);
                    }
                    // send packet
                    cli.Send(buf2, buf2.Length, from.Address.ToString(), from.Port);
                }
            }
        }
    }
}

.NET comes with a UdpClient class to send/receive.

Imports System
Imports System.Net
Imports System.Net.Sockets

Namespace UDP.Server
    Public Class Program
        Private Const SERVER_PORT As Integer = 12345
        Private Const MAX_SIZE As Integer = 1000
        Public Shared Sub Main(args As String())
            Using cli As New UdpClient(SERVER_PORT)
                While True
                    ' receive packet
                    Dim from As New IPEndPoint(IPAddress.Any, 0)
                    Dim buf1 As Byte() = cli.Receive(from)
                    ' work on data
                    Dim len As Integer = buf1.Length
                    Dim buf2 As Byte() = New Byte(len - 1) {}
                    For i As Integer = 0 To buf2.Length - 1
                        buf2(i) = CByte(buf1(i) + 1)
                    Next
                    ' send packet
                    cli.Send(buf2, buf2.Length, from.Address.ToString(), from.Port)
                End While
            End Using
        End Sub
    End Class
End Namespace

The same socket API used for TCP can actually be used for UDP as well.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <sys/socket.h>
#include <netdb.h>
#endif

#include <errno.h>

#define SERVER_PORT "12345"
#define MAX_SIZE 1000

int main()
{
    int sd, status, i, len, fromlen;
    char *buf1, *buf2;
    struct addrinfo hints, *local;
    struct sockaddr from;
#ifdef WIN32
    WSADATA WSAData;
    WSAStartup(0x0101, &WSAData);
#endif
    /* lookup client */
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_DGRAM;
    hints.ai_protocol = IPPROTO_UDP;
    hints.ai_flags = 0;
    status = getaddrinfo("localhost", SERVER_PORT, &hints, &local);
    if(status != 0)
    {
        printf("Error looking up self\n");
        exit(0);
    }
    /* create socket */
    sd = socket(AF_INET, SOCK_DGRAM, 0);
    if(sd < 0)
    {
        printf("Error creating socket: %s\n", strerror(errno));
        exit(0);
    }
    /* bind socket */
    status = bind(sd, local->ai_addr, local->ai_addrlen);
    if(status < 0)
    {
        printf("Error binding socket: %s\n", strerror(errno));
        exit(0);
    }
    for(;;)
    {
        /* receive packet */
        buf1 = malloc(MAX_SIZE);
        fromlen = sizeof(from);
        len = recvfrom(sd, buf1, MAX_SIZE, 0, &from, &fromlen);
        if(len < 0)
        {
            printf("Error receiving: %s\n", strerror(errno));
            exit(0);
        }
        /* work on data */
        buf2 = malloc(len);
        for(i = 0; i < len; i++)
        {
            buf2[i] = buf1[i] + 1;
        }
        /* send packet */
        len = sendto(sd, buf2, len, 0, &from, fromlen);
        if(len < 0)
        {
            printf("Error sending: %s\n", strerror(errno));
            exit(0);
        }
        /* */
        free(buf1);
        free(buf2);
    }
    /* close socket */
#ifdef WIN32
    closesocket(sd);
    WSACleanup();
#else
    close(sd);
#endif
    return 0;
}

The well known Indy library comes with a TIdUdpServer class to send/receive.

program UdpServer;

uses IdUDPServer, IdGlobal, SysUtils;

const
  SERVER_PORT = 12345;
  MAX_SIZE = 100; (* 1000 not valid *)

var
  srv : TIdUDPServer;
  buf1, buf2 : TIdBytes;
  from : string;
  fromport : word;
  j, len : integer;

begin
  srv := TIdUDPServer.Create;
  srv.DefaultPort := SERVER_PORT;
  while true do begin
    (* receive packet *)
    SetLength(buf1, MAX_SIZE);
    from := '0.0.0.0';
    fromport := 0;
    len := srv.ReceiveBuffer(buf1, from, fromport);
    (* work on data *)
    SetLength(buf2, len);
    for j := 0 to len - 1 do begin
      buf2[j] := buf1[j] + 1;
    end;
    (* send packet *)
    srv.SendBuffer(from, fromport, buf2);
  end;
  srv.Free;
end.

Article history:

Version Date Description
1.0 October 14th 2018 Initial version

Other articles:

See list of all articles here

Comments:

Please send comments to Arne Vajhøj