Network Component  Version 6.6
MDK-Professional Middleware for IP Networking
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
UDP Socket

This section describes how to use the User Datagram Protocol (UDP) in the Network Component. UDP runs on top of the Internet Protocol (IP) and was developed for applications that do not require reliability, acknowledgement, or flow control features at the transport layer. This simple protocol provides transport layer addressing in the form of UDP ports and an optional checksum capability.

UDP is a very simple protocol. Messages, so called datagrams, are sent to other hosts on an IP network without the need to set up special transmission channels or data paths beforehand. The UDP socket only needs to be opened for communication. It listens for incoming messages and sends outgoing messages on request.

Sending UDP Data

UDP is a byte stream service. It does not know anything about the format of the data being sent. It simply takes the data, encapsulates it into a UDP packet, and sends it to the remote peer.

The UDP protocol does not wait for any acknowledgement and is unable to detect any lost packets. When acknowledgement or detection is required, it must be done by the application layer. However, it is better to use a TCP Socket for communication when acknowledgement is necessary.

Example for Sending UDP Data

This example demonstrates the basic concept of sending a large amount of data using a UDP socket.

  • The example sends 64 Kbytes to the remote IP address 192.168.0.100, which is listening on port 1000.
  • The UDP socket will be allocated permanently and will not be released when the data is sent.
  • The data is sent as a stream of bytes.
  • The UDP socket does not wait for acknowledge from the remote peer to ascertain whether the data has been accepted. For this reason, a very simple acknowledge protocol is added to the example. The data will be sent in 512-byte blocks. Upon receiving the packet, the remote peer will send back a simple UDP packet with an acknowledgement. The acknowledgement is simply an index of the received packet starting with 0.

The steps for this example are as follows:

  1. Initialize the Network Component (using net_initialize) and allocate a free UDP Socket (using udp_get_socket) and open it for communication (using udp_open):
    #include "rl_net.lib"
    int32_t udp_soc;
    uint16_t bindex;
    bool wait_ack;
    void main (void) {
    udp_soc = udp_get_socket (0, UDP_OPT_SEND_CS | UDP_OPT_CHK_CS, udp_callback);
    udp_open (udp_soc, 0);
    bindex = 0;
    wait_ack = false;
  1. Run net_main of the Network Component and call the send_data() function from an endless loop:
    while (1) {
    send_data ();
    }
    }
  1. The send_data() function sends UDP Data and waits for acknowledge. Note that the UDP sockets do not support any acknowledgement. This example provides its own acknowledgement.
    void send_data (void) {
    static const uint8_t rem_IP[4] = {192,168,0,100};
    uint8_t *sendbuf;
    if (wait_ack == true) {
    return;
    }
    if (bindex < 128) {
    sendbuf = udp_get_buf (512);
    for (i = 0; i < 512; i += 2) {
    sendbuf[i] = bcount >> 8;
    sendbuf[i+1] = bcount & 0xFF;
    }
    udp_send (udp_soc, rem_IP, 1000, sendbuf, 512);
    }
    }
  1. When the packet is sent, wait for the remote acknowledge before proceeding with the next data packet. Use the callback listener function to wait for the remote acknowledge.
    uint16_t udp_callback (uint8_t socket, uint8_t *remip, uint16_t port, uint8_t *buf, uint16_t len) {
    // This function is called when UDP data has been received.
    if ((len == 2) && (bindex == (buf[0]<<8 | buf[1]))) {
    wait_ack == false;
    }
    return (0);
    }
Note
  • The example assumes that the Network Interface Adapter is selected, enabled, and properly configured.
  • If the system runs out of UDP sockets, the application hangs in an endless loop. The system error function with respond with the error code ERR_UDP_ALLOC.

IP Multicasting

IP Multicasting is the transmission of a datagram to a "host group", a set of zero or more hosts identified by a single IP destination address. A multicast datagram is delivered to all the members of its destination host group in the same way as regular unicast datagrams.

To enable reception of multicast packets a host must first join a host group. A membership in a host group is dynamic. Hosts may join and leave groups at any time.

Host groups are identified by Class D IP addresses, those with "1110" as their high-order four bits. Host group addresses range from 224.0.0.0 to 239.255.255.255. The address 224.0.0.0 is reserved and shall not be assigned to any group and 224.0.0.1 is assigned to the permanent group of all IP hosts, including gateways. This group shall also not be used for a dynamic host group.

UDP Multicasting

Incoming multicast UDP datagrams are received by UDP sockets in the same way as normal, unicast datagrams. The only difference is that the host must first join a host group, before the multicast packets for the selected group can be received.

Outgoing multicast UDP datagrams are sent in the same way as unicast datagrams, only that the destination IP address is a group address instead of a host address.

Multiple UDP Connections

Server applications often require to be able to accept several UDP connections from clients on the same port. One example for such an application is a TFTP Server. The multiplex handling must be implemented in the user application.

Because UDP socket is a connection-less service, several concurrent connections from different remote hosts are accepted. The packet multiplexing must be also done in user application.

The framework of the user application shall contain the following basic functions:

  1. The user_init() function to initialize all user application sessions at startup.
    void user_init () {
    USER_INFO *user_s;
    int i;
    for (i = 0; i < user_num_sess; i++) {
    user_s = &user_session[i];
    memset (user_s->RemIp, 0, IP_ADRLEN);
    user_s->RemPort = 0;
    user_s->Flags = 0;
    user_s->Retries = 0;
    user_s->Tout = 0;
    user_s->File = NULL;
    user_s->State = USER_STATE_IDLE;
    }
    // Allocate one UDP socket for all sessions.
    user_Socket = udp_get_socket (0, UDP_OPT_SEND_CS | UDP_OPT_CHK_CS,
    user_listener);
    if (user_Socket != 0) {
    udp_open (user_Socket, USER_SERVER_PORT);
    }
    }
    All user sessions are now initialized. A common UDP socket is opened for communication on selected USER_SERVER_PORT port.
  1. The user_listener() callback function for UDP socket.
    uint32_t user_listener (int32_t socket, const uint8_t *ip_addr, uint16_t port, const uint8_t *buf, uint32_t len) {
    USER_INFO *user_s;
    uint8_t session;
    int i;
    if (socket != user_Socket)) {
    return (false);
    }
    session = user_map_session (remip, port);
    if (session == 0) {
    return (false);
    }
    user_s = &user_session[session-1];
    switch (user_s->State) {
    case USER_STATE_IDLE:
    // A new connection established.
    ..
    user_s->State = USER_STATE_ACTIVE;
    break;
    case USER_STATE_ACTIVE:
    // Process UDP data.
    ..
    break;
    }
    return (true);
    }
  1. The user_map_session() function to map the UDP packet, which has generated a callback event, to it's owner session.
    static uint8_t user_map_session (uint8_t *remip, uint16_t port) {
    USER_INFO *user_s;
    int i;
    // Check if this is an existing connection.
    for (i = 1; i <= user_num_sess; i++) {
    user_s = &user_session[i-1];
    if ((user_s->State > USER_STATE_IDLE) &&
    (memcmp (remip, user_s->RemIp, IP_ADRLEN) == true) &&
    (port == user_s->RemPort)) {
    return (i);
    }
    }
    // Check if this is a new connection.
    for (i = 1; i <= user_num_sess; i++) {
    user_s = &user_session[i-1];
    if (user_s->State == USER_STATE_IDLE) {
    memcpy (user_s->RemIp, remip, IP_ADRLEN);
    user_s->RemPort = port;
    return (i);
    }
    }
    return (0);
    }
  1. The user_kill_session() function to initialize the session to a default state, close any eventually opened files and release any eventually allocated buffers.
    static void user_kill_session (USER_INFO *user_s) {
    user_s->State = USER_STATE_IDLE;
    if (user_s->File != NULL) {
    user_fclose (user_s->File);
    user_s->File = NULL;
    }
    memset (user_s->RemIp, 0, IP_ADRLEN);
    user_s->RemPort = 0;
    user_s->Flags = 0;
    user_s->Retries = 0;
    user_s->Tout = 0;
    }
  1. The user_run_server() function to maintain the application jobs, timeouts, etc. This function shall be frequently called from the main loop.
    void user_run_server () {
    USER_INFO *user_s;
    int i;
    for (i = 0; i < user_num_sess; i++) {
    user_s = &user_session[i];
    switch (user_s->State) {
    case USER_STATE_ACTIVE:
    if (sec_tick == true) {
    if (--user_s->Tout == 0) {
    // A timeout expired.
    user_kill_session (user_s);
    }
    }
    break;
    }
    }
    }
Note
One UDP socket will be used for every user session. You need to reserve enough UDP sockets in the Net_Config_UDP.h configuration file for all user sessions.

UDP Socket Configuration

The UDP sockets configuration file Net_Config_UDP.h contains only the setting for the Number of UDP Sockets. This number specifies the maximum number of simultaneously opened UDP connections.