How-to — UDP Client Endpoints

Note

This page uses two different API variants:

  • Synchronous API with classic def functions, usable in any context.

  • Asynchronous API with async def functions, using an asynchronous framework to perform I/O operations.

All asynchronous API examples assume that you are using asyncio, but you can use a different library thanks to the asynchronous backend engine API.


The Basics

The Protocol Object

The UDP clients expect a DatagramProtocol instance to communicate with the remote endpoint.

See also

How-to — Communication Protocols

Explains what a DatagramProtocol is and how to use it.

Connecting To The Remote Host

Important

If you are familiar with UDP sockets, you know that there are no real connections (communication pipes) like there are with TCP sockets.

If not, I advise you to read the Unix manual pages udp(7) and connect(2).

You need the host address (domain name or IP) and the port of connection in order to connect to the remote host:

 1from __future__ import annotations
 2
 3from easynetwork.api_sync.client import UDPNetworkClient
 4from easynetwork.protocol import DatagramProtocol
 5from easynetwork.serializers import JSONSerializer
 6
 7
 8def main() -> None:
 9    protocol = DatagramProtocol(JSONSerializer())
10    address = ("127.0.0.1", 9000)
11
12    with UDPNetworkClient(address, protocol) as client:
13        print(f"Remote address: {client.get_remote_address()}")
14
15        ...
16
17
18if __name__ == "__main__":
19    main()

Note

The client does nothing when it enters the with context. Everything is done on object creation.

Using An Already Connected Socket

If you have your own way to obtain a connected socket.socket instance, you can pass it to the client.

If the socket is not connected, an OSError is raised.

Important

It must be a SOCK_DGRAM socket with AF_INET or AF_INET6 family.

Warning

The resource ownership is given to the client. You must close the client to close the socket.

 1from __future__ import annotations
 2
 3import socket
 4
 5from easynetwork.api_sync.client import UDPNetworkClient
 6from easynetwork.protocol import DatagramProtocol
 7from easynetwork.serializers import JSONSerializer
 8
 9
10def obtain_a_connected_socket() -> socket.socket:
11    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
12
13    ...
14
15    return sock
16
17
18def main() -> None:
19    protocol = DatagramProtocol(JSONSerializer())
20    sock = obtain_a_connected_socket()
21
22    with UDPNetworkClient(sock, protocol) as client:
23        print(f"Remote address: {client.get_remote_address()}")
24
25        ...
26
27
28if __name__ == "__main__":
29    main()

Basic Usage

Sending Packets

There’s not much to say, except that objects passed as arguments are automatically converted to bytes to send to the remote host thanks to the protocol object.

1client.send_packet({"data": 42})

Receiving Packets

You get the next available packet, already parsed.

1packet = client.recv_packet()
2print(f"Received packet: {packet!r}")

You can control the receive timeout with the timeout parameter:

1try:
2    packet = client.recv_packet(timeout=30)
3except TimeoutError:
4    print("Timed out")
5else:
6    print(f"Received packet: {packet!r}")

Tip

Remember to catch invalid data parsing errors.

1try:
2    packet = client.recv_packet(timeout=30)
3except DatagramProtocolParseError:
4    print("Received something, but was not valid")
5except TimeoutError:
6    print("Timed out")
7else:
8    print(f"Received packet: {packet!r}")

Receiving Multiple Packets At Once

You can use iter_received_packets() to get all the received packets in a sequence or a set.

1all_packets = [p for p in client.iter_received_packets()]

The timeout parameter defaults to zero to get only the data already in the buffer, but you can change it.

1all_packets = [p for p in client.iter_received_packets(timeout=1)]

See also

UDPNetworkClient.iter_received_packets()

The method description and usage (especially for the timeout parameter).

Advanced Usage

Note

This section is for people who know what they’re doing and are looking for something specific.

Low-Level Socket Operations

For low-level operations such as setsockopt(), the client object exposes the socket through a SocketProxy:

1client.socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, True)

Concurrency And Multithreading

All client methods are thread-safe. Synchronization follows these rules: