Basic UDP Server

From Nutwiki
Revision as of 17:02, 27 October 2016 by Harald (Talk | contribs) (1 revision imported)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

Overview

This example demosntrates how to receive simple UDP datagrams and evaluate their contents. For the sake of simplicity we will be using the Nut/OS discovery telegrams. This example does NOT show how to work with the discoverer, it simply reuses the data structures to allow easy sending of telegrams via the Nut/OS Discoverer program that comes with the development package.

Source Code

<source lang="c">

  1. include <io.h>
  2. include <stdio.h>
  1. include <dev/board.h>
  1. include <sys/socket.h>
  2. include <sys/confnet.h>
  1. include <arpa/inet.h>
  1. include <netinet/in.h>
  1. include <pro/dhcp.h>
  2. include <pro/discover.h>
  1. define LISTEN_PORT 9806
  2. define LISTEN_TIMEOUT 1000

UDPSOCKET *sock; struct _DISCOVERY_TELE telegram;

int main(void) {

   u_long baud = 115200;
   uint32_t udp_ip = INADDR_ANY;
   uint16_t udp_port = LISTEN_PORT;
   int got = 0;
   NutRegisterDevice(&DEV_DEBUG, 0, 0);
   freopen(DEV_DEBUG_NAME, "w", stdout);
   _ioctl(_fileno(stdout), UART_SETSPEED, &baud);
   puts("\nNut/OS UDP server");
   if (NutRegisterDevice(&DEV_ETHER, 0, 0)) {
       puts("Registering " DEV_ETHER_NAME " failed.");
   }
   else if (NutDhcpIfConfig(DEV_ETHER_NAME, 0, 60000)) {
       puts("Configuring " DEV_ETHER_NAME " failed.");
   }
   if ((sock = NutUdpCreateSocket(LISTEN_PORT)) == 0) {
       puts("Error while creating the UDP socket.");
   }
   puts("The UDP server is now running. Please launch the Nut/OS discoverer to send some telegrams.\n\n");
   for (;;) {
       got = NutUdpReceiveFrom(sock, &udp_ip, &udp_port, &telegram, sizeof(telegram), LISTEN_TIMEOUT);
       if (got == sizeof(telegram)) {
           if (telegram.dist_type == 0) {
               puts("I received a discovery telegram. Since it was a request telegram it only contains zeroes. No need to display.\n\n");
           } else {
               char *last_ip = strdup(inet_ntoa(telegram.dist_ip_addr));
               char *netmask = strdup(inet_ntoa(telegram.dist_ip_mask));
               char *route = strdup(inet_ntoa(telegram.dist_gateway));
               char *conf_ip = strdup(inet_ntoa(telegram.dist_cip_addr));
               puts("I received a discovery telegram. Here is what it contains:");
               printf
                   ("Exchange identifier: %lu \nMessage type: %d \nTelegram version: %d \nEthernet MAC Address: %.6x \nLast used IP address: %s \nIP netmask: %s \nDefault route: %s \nConfigured IP address: %s \nHostname: %.8s \nRequested Bootfile: %.96s\n\n",
                    telegram.dist_xid, telegram.dist_type, telegram.dist_ver, telegram.dist_mac, last_ip, netmask, route, conf_ip,
                    telegram.dist_hostname, telegram.dist_custom);
               free(last_ip);
               free(netmask);
               free(route);
               free(conf_ip);
           }
       } else if (got > 0) {
           puts("I received some other data but I'm not able to handle it.\n\n");
       }
   }
   return 0;

} </source>

Details

<source lang="c">

  1. define LISTEN_PORT 9806
  2. define LISTEN_TIMEOUT 1000

UDPSOCKET *sock; struct _DISCOVERY_TELE telegram; </source>

In order to get started we need some declarations and definitions beforehand. The LISTEN_PORT is the port used by the discoverer to send discovery telegrams to. Since that is what we want to intercept, we'll use the same port. The LISTEN_TIMEOUT is the number of milliseconds we want to wait for incoming transfers.

We also reuse the existing _DISCOVERY_TELE structure that Nut/OS already provides. Because we can.

<source lang="c">

   u_long baud = 115200;

   uint32_t udp_ip = INADDR_ANY;
   uint16_t udp_port = LISTEN_PORT;

   int got = 0;

   NutRegisterDevice(&DEV_DEBUG, 0, 0);
   freopen(DEV_DEBUG_NAME, "w", stdout);
   _ioctl(_fileno(stdout), UART_SETSPEED, &baud);
   puts("\nNut/OS UDP server");

   if (NutRegisterDevice(&DEV_ETHER, 0, 0)) {
       puts("Registering " DEV_ETHER_NAME " failed.");
   }

   else if (NutDhcpIfConfig(DEV_ETHER_NAME, 0, 60000)) {
       puts("Configuring " DEV_ETHER_NAME " failed.");
   }

</source>

This is mostly stuff you probably already did a few times: Initialize the UART, register it as debug output device and and then configure the ethernet interface via DHCP or stored configuration.

One thing worth noting: The udp_ip and udp_port variables. We NEED those to be actual variables, defines won't suffice here. You'll see why in a moment.

got is simply a variable to hold the number of received bytes later on. It is not needed but makes the code a little easier to read later on.

<source lang="c">

   if ((sock = NutUdpCreateSocket(LISTEN_PORT)) == 0) {
       puts("Error while creating the UDP socket.");
   }

</source>

Here we create our UDP socket. In case the function returns 0, we encountered an error. Since this is an example we'll not deal with the errors but if one should occur you'll probably want to know. Please note that - unlike TCP sockets - UDP sockets take a port number as an argument when creating them. That is because UDP is a connectionless protocol and as such doesn't have the opportunity to specify the port when connecting. Only server UDP sockets need a specific port, though. Client sockets don't.

<source lang="c"> got = NutUdpReceiveFrom(sock, &udp_ip, &udp_port, &telegram, sizeof(telegram), LISTEN_TIMEOUT); </source>

As you can probably tell, this line receives stuff. Again, keep in mind that UDP is a connectionless protocol. It does not have to establish a connection in order to send and receive. This has the advantage of being fast and extremely lightweight. The huge disadvantage is: You never know if whatever you sent actually got where it was supposed to.

Now for the parameters: The first one is the socket we created earlier.

The second and third parameter are our ip and port variables we declared earlier. Note how we don't pass the value but the address of them. This is important. They may be modified by the call to contain the sending IP and port after the call returns.

The fourth parameter is where we want to store the data. This is, of course, our telegram structure. We're using a clever trick called overlaying here: Since we know what kind of data we are expecting, we write it directly to the structure we have. This allows us to access the properties of the structure later on without ever converting the incoming data to a structure. This will only work if you know the exact structure of the data you are receiving. The fifth parameter is the amount of bytes we want to receive. This is of course the size of the structure we intend to fill.

The last parameter is the timeout. If you don't want the call to return after a timeout, just pass NUT_WAIT_INFINITE here.

<source lang="c">

       if (got == sizeof(telegram)) {
           if (telegram.dist_type == 0) {
               puts("I received a discovery telegram. Since it was a request telegram it only contains zeroes. No need to display.\n\n");
           } else {
               char *last_ip = strdup(inet_ntoa(telegram.dist_ip_addr));
               char *netmask = strdup(inet_ntoa(telegram.dist_ip_mask));
               char *route = strdup(inet_ntoa(telegram.dist_gateway));
               char *conf_ip = strdup(inet_ntoa(telegram.dist_cip_addr));

               puts("I received a discovery telegram. Here is what it contains:");
               printf
                   ("Exchange identifier: %lu \nMessage type: %d \nTelegram version: %d \nEthernet MAC Address: %.6x \nLast used IP address: %s \nIP netmask: %s \nDefault route: %s \nConfigured IP address: %s \nHostname: %.8s \nRequested Bootfile: %.96s\n\n",
                    telegram.dist_xid, telegram.dist_type, telegram.dist_ver, telegram.dist_mac, last_ip, netmask, route, conf_ip,
                    telegram.dist_hostname, telegram.dist_custom);

               free(last_ip);
               free(netmask);
               free(route);
               free(conf_ip);
           }
       } else if (got > 0) {
           puts("I received some other data but I'm not able to handle it.\n\n");
       }

</source>

Believe it or not, the hard part is done. Now we just have to evaluate the data if it is correct and then display it or process it or whatever we want to do with it. For this example, displaying the data is more than sufficient.

First we check got (the number of bytes of received) for its size. It should be the same as the size of our structure. If it is not, we didn't receive a discovery telegram or only part of it. Whatever the reason, the data is garbage in that case. If it matches up, we go on and process it:

If the telegram type is a 0 (which is Nut/OS Discoverer's way of asking all nodes to report back to it) we don't have to display the telegram contents since it is only filled with 0s anyway. In any other case we'll output the contents of the telegram to show this stuff really works that easily.

Output

<source lang="text"> Nut/OS UDP server The UDP server is now running. Please launch the Nut/OS discoverer to send some telegrams.


I received a discovery telegram. Since it was a request telegram it only contain s zeroes. No need to display.


I received a discovery telegram. Here is what it contains: Exchange identifier: 421668809 Message type: 2 Telegram version: 16 Ethernet MAC Address: 000556 Last used IP address: 192.168.192.121 IP netmask: 255.255.255.0 Default route: 192.168.192.1 Configured IP address: 0.0.0.0 Hostname: sample Requested Bootfile: </source>

See also