UDP Sender&Receiver

This pair of examples shows how to encode and transmit some random data over a UDP socket, then receive and decode that data at the other end. The sender and receiver applications are standalone, and you can run them on the same computer or over an actual IP network.

Note that the receiver should be started before the sender!

The sender application

The example code for the sender is shown below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
// Copyright Steinwurf ApS 2018.
// Distributed under the "STEINWURF EVALUATION LICENSE 1.0".
// See accompanying file LICENSE.rst or
// http://www.steinwurf.com/licensing

#include <stdint.h>
#include <stdlib.h>
#include <kodo_rlnc_c/encoder.h>

#ifdef _WIN32
    #include <winsock2.h>
#else
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <netdb.h>
    #include <unistd.h>
    #include <sys/time.h>
#endif

#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <assert.h>

#ifdef _WIN32

#include <windows.h>

void sleep_here(uint32_t milliseconds)
{
    Sleep(milliseconds);
}

#else

void sleep_here(uint32_t milliseconds)
{
    usleep(milliseconds * 1000); // takes microseconds
}

#endif


int main(int argc, char* argv[])
{
    // Variables needed for the network / socket usage
    int32_t socket_descriptor;
    int32_t return_code;
    uint32_t i;

    struct sockaddr_in local_address;
    struct sockaddr_in remote_address;
    struct hostent* host;

    uint32_t delay = 0; // Delay between packets

    // Variables needed for the coding
    uint32_t symbols = 32;
    uint32_t symbol_size = 160;
    int32_t finite_field = krlnc_binary8;

    uint32_t packets = 0;

    krlnc_encoder_factory_t encoder_factory = NULL;
    krlnc_encoder_t encoder = NULL;

    // The buffer sent to the receiver
    uint32_t payload_size = 0;
    uint8_t* payload = 0;
    uint32_t bytes_used = 0;

    // The data to be encoded
    uint32_t block_size = 0;
    uint8_t* data_in = 0;

    // Initialize winsock if on Windows
#ifdef _WIN32

    WORD versionWanted = MAKEWORD(1, 1);
    WSADATA wsaData;

    return_code = WSAStartup(versionWanted, &wsaData);

    if (return_code != 0)
    {
        // Tell the user that we could not find a usable
        // Winsock DLL.
        printf("WSAStartup failed with error: %d\n", return_code);
        exit(1);
    }

#endif

    // Check command line args
    if (argc != 6)
    {
        printf("usage : %s <server> <port> <symbols> <packets> <delay_ms>\n",
               argv[0]);

        exit(1);
    }

    // Get the delay
    delay = atol(argv[5]);
    printf("Delay is: %u milliseconds\n", delay);

    // Get server IP address (no check if input is IP address or DNS name)
    host = gethostbyname(argv[1]);
    if (host == NULL)
    {
        printf("%s: unknown host '%s' \n", argv[0], argv[1]);
        exit(1);
    }

    printf("Sending data to '%s:%d' (IP: %s) \n", host->h_name,
           atoi(argv[2]), inet_ntoa(*(struct in_addr*)host->h_addr_list[0]));

    remote_address.sin_family = host->h_addrtype;
    memcpy((char*) &remote_address.sin_addr.s_addr,
           host->h_addr_list[0], host->h_length);
    remote_address.sin_port = htons(atoi(argv[2]));

    // Socket creation
    socket_descriptor = socket(AF_INET, SOCK_DGRAM, 0);
    if (socket_descriptor < 0)
    {
        printf("%s: cannot open socket \n", argv[0]);
        exit(1);
    }

    // Bind any port
    local_address.sin_family = AF_INET;
    local_address.sin_addr.s_addr = htonl(INADDR_ANY);
    local_address.sin_port = htons(0);

    return_code = bind(socket_descriptor, (struct sockaddr*) &local_address,
                       sizeof(local_address));

    if (return_code < 0)
    {
        printf("%s: cannot bind port\n", argv[0]);
        exit(1);
    }

    // Initialize the factory with the chosen symbols and symbol size
    symbols = atoi(argv[3]);
    packets = atoi(argv[4]);
    if (packets < symbols)
    {
        printf("%s: number of packets should be higher than %d \n",
               argv[0], symbols);
    }

    // Create the encoder factory
    encoder_factory = krlnc_new_encoder_factory(
        finite_field, symbols, symbol_size);
    encoder = krlnc_encoder_factory_build(encoder_factory);

    // Create the buffer needed for the payload
    payload_size = krlnc_encoder_payload_size(encoder);
    payload = (uint8_t*)malloc(payload_size);

    // Create some data to encode
    block_size = krlnc_encoder_block_size(encoder);
    data_in = (uint8_t*)malloc(block_size);

    for (i = 0; i < block_size; ++i)
        data_in[i] = rand() % 256;

    // Send data
    for (i = 0; i < packets; ++i)
    {
        if (krlnc_encoder_rank(encoder) < krlnc_encoder_symbols(encoder))
        {
            // The rank of an encoder indicates how many symbols have
            // been added, i.e. how many symbols are available for encoding
            uint32_t rank = krlnc_encoder_rank(encoder);

            // Calculate the offset to the next symbol to insert
            uint8_t* symbol = data_in + rank * symbol_size;
            krlnc_encoder_set_const_symbol(encoder, rank, symbol, symbol_size);
        }

        bytes_used = krlnc_encoder_write_payload(encoder, payload);
        printf("Payload generated by encoder, rank = %d, bytes used = %d\n",
               krlnc_encoder_rank(encoder), bytes_used);

        return_code = sendto(socket_descriptor, payload, bytes_used, 0,
                             (struct sockaddr*) &remote_address,
                             sizeof(remote_address));

        if (return_code < 0)
        {
            printf("%s: cannot send data %d \n", argv[0], i-1);
            close(socket_descriptor);
            exit(1);
        }

        sleep_here(delay);
    }

    // Clean up
    free(data_in);
    free(payload);

    krlnc_delete_encoder(encoder);
    krlnc_delete_encoder_factory(encoder_factory);

    return 0;
}

The receiver application

The example code for the receiver is shown below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
// Copyright Steinwurf ApS 2018.
// Distributed under the "STEINWURF EVALUATION LICENSE 1.0".
// See accompanying file LICENSE.rst or
// http://www.steinwurf.com/licensing

#include <stdint.h>
#include <stdlib.h>
#include <kodo_rlnc_c/decoder.h>

#ifdef _WIN32
    #include <winsock2.h>
    typedef int socklen_t;
#else
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <netdb.h>
    #include <unistd.h>
#endif

#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <assert.h>

// Count the total number of packets received in order to decode
unsigned int rx_packets;

static void exit_on_sigint(int sig)
{
    (void) sig;
    printf("\nTotal number of received packets: %d\n", rx_packets);
    exit(0);
}


int main(int argc, char* argv[])
{
    // Variables needed for the network / socket usage
    int32_t socket_descriptor = 0;
    int32_t return_code = 0;
    int32_t bytes_received = 0;
    socklen_t remote_address_size;
    struct sockaddr_in remote_address;
    struct sockaddr_in local_address;

    // Variables needed for the coding
    uint32_t symbols = 32;
    uint32_t symbol_size = 160;
    int32_t finite_field = krlnc_binary8;

    krlnc_decoder_factory_t decoder_factory = NULL;
    krlnc_decoder_t decoder = NULL;

    // The buffer used to receive incoming packets
    uint32_t payload_size = 0;
    uint8_t* payload = 0;

    // Keeps track of which symbols have been decoded
    uint8_t* decoded = (uint8_t*) malloc(sizeof(uint8_t) * symbols);

    // Initialize winsock if on Windows
#ifdef _WIN32

    WORD versionWanted = MAKEWORD(1, 1);
    WSADATA wsaData;

    return_code = WSAStartup(versionWanted, &wsaData);

    if (return_code != 0)
    {
        // Tell the user that we could not find a usable
        // Winsock DLL.
        printf("WSAStartup failed with error: %d\n", return_code);
        exit(1);
    }

#endif

    // Initialize global variables
    rx_packets = 0;

    if (argc < 3)
    {
        printf("usage : %s <port> <symbols>\n", argv[0]);
        exit(1);
    }

    // Socket creation
    socket_descriptor = socket(AF_INET, SOCK_DGRAM, 0);
    if (socket_descriptor < 0)
    {
        printf("%s: cannot open socket \n", argv[0]);
        exit(1);
    }

    // Bind local server port
    local_address.sin_family = AF_INET;
    local_address.sin_addr.s_addr = htonl(INADDR_ANY);
    local_address.sin_port = htons(atoi(argv[1]));
    return_code = bind(socket_descriptor, (struct sockaddr*) &local_address,
                       sizeof(local_address));

    if (return_code < 0)
    {
        printf("%s: cannot bind port number %d \n", argv[0], atoi(argv[1]));
        exit(1);
    }

    // Install signal handler
    signal(SIGINT, exit_on_sigint);

    // Initialize the factory with the chosen symbols and symbol size
    symbols = atoi(argv[2]);

    // Create the encoder factory
    decoder_factory = krlnc_new_decoder_factory(
        finite_field, symbols, symbol_size);
    decoder = krlnc_decoder_factory_build(decoder_factory);

    // Create the buffer needed for the payload
    payload_size = krlnc_decoder_payload_size(decoder);
    payload = (uint8_t*) malloc(payload_size);

    uint32_t block_size = krlnc_decoder_block_size(decoder);
    uint8_t* data_out = (uint8_t*) malloc(block_size);
    krlnc_decoder_set_mutable_symbols(decoder, data_out, block_size);

    // Zero initialize the decoded array */
    memset(decoded, '\0', sizeof(uint8_t) * symbols);

    printf("%s: waiting for data on UDP port %u\n", argv[0], atoi(argv[1]));

    // Receiver loop
    while (!krlnc_decoder_is_complete(decoder))
    {
        // Receive message
        remote_address_size = sizeof(remote_address);

        bytes_received = recvfrom(
            socket_descriptor, payload, payload_size, 0,
            (struct sockaddr*) &remote_address, &remote_address_size);

        if (bytes_received < 0)
        {
            printf("%s: recvfrom error %d\n", argv[0], bytes_received);
            fflush(stdout);
            continue;
        }

        // Print received message
        printf("%s: UDP packet from %s:%u : %d\n",
               argv[0],inet_ntoa(remote_address.sin_addr),
               ntohs(remote_address.sin_port), bytes_received);

        ++rx_packets;

        // Packet got through - pass that packet to the decoder
        krlnc_decoder_read_payload(decoder, payload);

        if (krlnc_decoder_is_partially_complete(decoder))
        {
            uint32_t i = 0;
            for (; i < symbols; ++i)
            {
                if (!krlnc_decoder_is_symbol_uncoded(decoder, i))
                    continue;

                if (!decoded[i])
                {
                    // Update that this symbol now has been decoded,
                    // in a real application we could copy out the symbol
                    // using the krlnc_copy_from_symbol(..) or use the data_out
                    // directly.
                    printf("Symbol %d was decoded\n", i);
                    decoded[i] = 1;
                }
            }
        }
    }

    printf("Data decoded!\n");

    // Cleanup
    free(decoded);
    free(payload);

    krlnc_delete_decoder(decoder);
    krlnc_delete_decoder_factory(decoder_factory);

    return 0;
}