UDP clients are simpler than TCP clients since no connection is required. This module covers building UDP clients.

Basic UDP Client

import socket

# Create UDP socket
client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# Send data (no connection needed)
message = "Hello, Server!"
server_address = ('localhost', 8080)
client.sendto(message.encode(), server_address)

# Receive response
data, server_addr = client.recvfrom(1024)
print(f"Response: {data.decode()}")

# Close socket
client.close()

Complete UDP Client Example

import socket

def udp_client(host='localhost', port=8080):
    """Simple UDP client."""

    client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

    try:
        server_address = (host, port)

        # Send message
        message = "Hello, UDP Server!"
        print(f"Sending to {server_address}: {message}")
        client.sendto(message.encode(), server_address)

        # Receive response (with timeout)
        client.settimeout(5.0)
        try:
            data, address = client.recvfrom(1024)
            print(f"Received from {address}: {data.decode()}")
        except socket.timeout:
            print("Timeout waiting for response")

    except Exception as e:
        print(f"Error: {e}")
    finally:
        client.close()

if __name__ == '__main__':
    udp_client()

Interactive UDP Client

import socket

def interactive_udp_client(host='localhost', port=8080):
    """Interactive UDP client for sending multiple messages."""

    client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    client.settimeout(5.0)
    server_address = (host, port)

    print(f"UDP client ready. Sending to {host}:{port}")
    print("Type messages (or 'quit' to exit)")

    try:
        while True:
            message = input("> ")

            if message.lower() in ('quit', 'exit', 'q'):
                break

            if not message:
                continue

            # Send message
            try:
                client.sendto(message.encode(), server_address)
                print(f"Sent: {message}")

                # Receive response
                try:
                    data, addr = client.recvfrom(1024)
                    print(f"Response: {data.decode()}")
                except socket.timeout:
                    print("No response received (timeout)")

            except Exception as e:
                print(f"Error: {e}")

    except KeyboardInterrupt:
        print("\nExiting...")
    finally:
        client.close()

if __name__ == '__main__':
    interactive_udp_client()

UDP Client with Retry Logic

import socket
import time

def udp_client_with_retry(host='localhost', port=8080, max_retries=3):
    """UDP client with retry logic for reliability."""

    client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    client.settimeout(2.0)
    server_address = (host, port)

    message = "Hello, Server!"

    for attempt in range(max_retries):
        try:
            # Send message
            client.sendto(message.encode(), server_address)
            print(f"Attempt {attempt + 1}: Sent message")

            # Wait for response
            try:
                data, addr = client.recvfrom(1024)
                print(f"Received response: {data.decode()}")
                return  # Success
            except socket.timeout:
                print(f"Attempt {attempt + 1}: No response")
                if attempt < max_retries - 1:
                    time.sleep(1)  # Wait before retry
                    continue

        except Exception as e:
            print(f"Error on attempt {attempt + 1}: {e}")
            if attempt < max_retries - 1:
                time.sleep(1)
                continue

    print("Max retries reached. Giving up.")
    client.close()

if __name__ == '__main__':
    udp_client_with_retry()

Broadcasting Client

UDP clients can send broadcast messages to all devices on the network:

import socket

def broadcast_client(message="Hello, everyone!", port=8080):
    """UDP client that broadcasts a message."""

    client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

    # Enable broadcast
    client.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)

    # Broadcast address (sends to all devices on local network)
    broadcast_address = ('255.255.255.255', port)

    try:
        print(f"Broadcasting: {message}")
        client.sendto(message.encode(), broadcast_address)
        print("Broadcast sent")

        # Listen for responses
        client.settimeout(5.0)
        responses = []

        while True:
            try:
                data, addr = client.recvfrom(1024)
                responses.append((addr, data.decode()))
                print(f"Response from {addr}: {data.decode()}")
            except socket.timeout:
                break

        print(f"\nReceived {len(responses)} responses")

    except Exception as e:
        print(f"Error: {e}")
    finally:
        client.close()

if __name__ == '__main__':
    broadcast_client()

Key Differences from TCP Client

No connect() Call

# TCP: Must connect first
tcp_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcp_client.connect(('localhost', 8080))
tcp_client.send(data)

# UDP: Send directly with sendto()
udp_client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
udp_client.sendto(data, ('localhost', 8080))

Must Specify Address Each Time

# TCP: Address specified once in connect()
tcp_client.connect(('localhost', 8080))
tcp_client.send(data)  # No address needed

# UDP: Address needed for each sendto()
udp_client.sendto(data, ('localhost', 8080))  # Address required

recvfrom() Returns Address

# TCP: recv() returns only data
data = tcp_client.recv(1024)

# UDP: recvfrom() returns data and sender address
data, address = udp_client.recvfrom(1024)

Error Handling

UDP clients need to handle: - Timeouts (server might not respond) - Lost datagrams (no delivery guarantee) - Wrong server address

import socket

def robust_udp_client(host='localhost', port=8080):
    """UDP client with comprehensive error handling."""

    client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    client.settimeout(5.0)

    server_address = (host, port)
    message = "Hello, Server!"

    try:
        # Resolve hostname
        try:
            socket.gethostbyname(host)
        except socket.gaierror:
            print(f"Error: Cannot resolve hostname '{host}'")
            return

        # Send message
        try:
            client.sendto(message.encode(), server_address)
            print(f"Sent to {server_address}: {message}")
        except OSError as e:
            print(f"Error sending: {e}")
            return

        # Receive response
        try:
            data, addr = client.recvfrom(1024)

            # Verify response is from expected server
            if addr[0] == socket.gethostbyname(host):
                print(f"Response from {addr}: {data.decode()}")
            else:
                print(f"Warning: Response from unexpected address {addr}")

        except socket.timeout:
            print("Timeout: No response received")
            print("Possible reasons:")
            print("  - Server is not running")
            print("  - Firewall blocking")
            print("  - Datagram lost in transit")

        except Exception as e:
            print(f"Error receiving: {e}")

    except Exception as e:
        print(f"Unexpected error: {e}")

    finally:
        client.close()

if __name__ == '__main__':
    robust_udp_client()

Testing UDP Client

Test your UDP client with netcat:

# Terminal 1: Start UDP server with netcat
nc -u -l 8080

# Terminal 2: Run your UDP client
python udp_client.py

Or use Python server:

# Terminal 1: Start Python UDP server
python examples/python/03-basic-udp-server.py

# Terminal 2: Run your UDP client
python examples/python/04-basic-udp-client.py

Key Takeaway: UDP clients are simpler than TCP (no connection needed), but require handling timeouts and potential datagram loss. Always specify destination address in sendto() and use recvfrom() to get sender address.