Skip to main content

UDP Listener in Swift

·4 mins

In this post I am laying out the steps necessary to implement a UDP-listener by using Apples Network Framework. A SwiftUI-View will display all incoming UDP-messages. All code is available in a handy XCode-Playground.

On the “other side” is a UDP-sender which I have explained in this previous post:

Send and Receive UDP Messages with Swift

This post is divided into the following sections:

  • Creating the Listener
  • Accepting new Connections
  • Receiving Messages
  • Starting the Listener
  • Testing the Listener

I have created an XCode-Playground that displays all incoming messages in a SwitUI-View.

https://youtu.be/cd4FLF4XTiE

It can downloaded from here: UDP Listener in Swift

Creating the Listener #

Everything is based on the NWListener, which is described as

“An object you use to listen for incoming network connections.”

Let’s begin by instantiating one.

var listener: NWListener?

do {
    listener = try NWListener(using: .udp, on: port)
} catch {
    print("exception upon creating listener")
}

Next, the behaviour of stateUpdateHandler needs to be defined. It is described as follows:

“A handler that receives listener state updates.”

listener?.stateUpdateHandler = {(newState) in
    switch newState {
    case .ready:
        print("ready")
    default:
        break
    }
}

Accepting new Connections #

For now the listener can handle state updates, but the fun begins when a new connection begins.

Another handler needs to be implemented. It is the [newConnectionHandler](https://developer.apple.com/documentation/network/nwlistener/ 2998663-newconnectionhandler), which is explained in the documentation as

“A handler that receives inbound connections.”

listener?.newConnectionHandler = {(newConnection) in
    newConnection.stateUpdateHandler = {newState in
        switch newState {
        case .ready:
            print("ready")
        default:
            break
        }
    }
    newConnection.start(queue: DispatchQueue(label: "newconn"))
}

Receiving Messages #

When the connection is ready a handler needs to be assigned to receiveMessage:

“Schedules a single receive completion handler for a complete message, as opposed to a range of bytes. "

connection.receiveMessage { (data, context, isComplete, error) in
    // Decode and continue processing data
}

Starting the Listener #

Everything is in place to start up the listener with start. It is described as:

“Registers for listening, and sets the queue on which all listener events are delivered.”

listener?.start(queue: .main)

Testing the Listener #

With everything up and running it is time to test the code. The easiest way to start up the Terminal-application and type in the following:

echo -n "Hello UDP-World" | nc -4u -w1 -localhost 1024

For those not so familiar with the command line I have provided some explanation from the man-pages of the tools being used.

First, the message is being “created” with echo.

What is echo?

“The echo utility writes any specified operands, separated by single blank ( ') characters and followed by a newline (\n’) character, to the standard output.

Whereas the -n parameter is documented as

“Do not print the trailing newline character. This may also be achieved by appending \c' to the end of the string, as is done by iBCS2 compatible systems. Note that this option as well as the effect of \c’ are implementation-defined in IEEE Std 1003.1-2001 (``POSIX.1’’) as amended by Cor. 1-2002. Applications aiming for maximum portability are strongly encouraged to use printf(1) to suppress the newline character.”

With the pipe-command |, this message is being fed into another tool which sends the message over the network: netcat or short nc:

“The nc (or netcat) utility is used for just about anything under the sun involving TCP or UDP. It can open TCP connections, send UDP packets, listen on arbitrary TCP and UDP ports, do port scanning, and deal with both IPv4 and IPv6. Unlike telnet(1), nc scripts nicely, and separates error messages onto standard error instead of sending them to standard output, as telnet(1) does with some.”

The following parameters are being used:

-4: Forces nc to use IPv4 addresses only.

-u: Use UDP instead of the default option of TCP.

-w timeout: If a connection and stdin are idle for more than timeout seconds, then the connection is silently closed. The -w flag has no effect on the -l option, i.e. nc will listen forever for a connection, with or without the -w flag. The default is no timeout.

Conclusion #

With this code you should be able now to receive UDP-message from any sender and process the incoming messages for anything your app is supposed to do.

Feel free to buy me a coffee if you liked this post

Resources #