What does UDPSocket recvfrom return?

20 Feb 2011

Hi everyone. Playing around with EthernetNetIf with some success, but stumbled across something I cannot explain. I thought the recvfrom method of UDPSocket returned the number of bytes that have been received. A number of examples seem to treat it that way. However, when I check the value I get back when a packet containing 105 bytes is sent I find I am getting a return value of 1 every time. Can anyone explain why?

Is the value returned a byte count, or is it just a flag to indicate that data was received? i.e. 0 means failed, 1 means OK.

Here's the code. Have I made a stupid mistake somewhere?

/*
 * SOURCE FILE : main.cpp
 *
 * Main program for a general purpose IO EtherBoard.
 * Whenever it receives a message containing output states it updates the outputs,
 * reads all inputs and transmits a reply containing the inputs.
 *
 * Adapted from the UDPSocketExample program by Donatien Garnier (6th August 2010).
 * Uses Donatien Garnier's EthernetIf library.
 *
 * Richard Ellingworth. 20th February 2011.
 *
 */
 
#include "mbed.h"
#include "EthernetNetIf.h"
#include "UDPSocket.h"
#include "Hexdump.h"
#include "EtherBoardPacket.h"

// Type number for this board.
#define BOARDTYPE ((ubyte)1)        // 1 used to mean GPIO

// Ethernet connection object.
static EthernetNetIf eth(
  IpAddr(172,27,12,99),  //IP Address of the mbed (device running this code)
  IpAddr(255,255,255,0), //Network Mask
  IpAddr(172,27,12,253), //Gateway
  IpAddr(172,27,12,253)  //DNS
);

// UDP communications socket.
static UDPSocket udp;

// Serial port routed through the mbed's USB port back to the development PC.
// Used for debugging messages and so on.
static Serial pc( USBTX, USBRX );

/***************************************/
/* EVENT HANDLER FOR UDP SOCKET EVENTS */
/***************************************/

// Type of event is passed in e parameter.
static void onUDPSocketEvent(UDPSocketEvent e) {
  switch(e) {
  case UDPSOCKET_READABLE: //The only event for now
    EtherBoardPacket rxPacket, txPacket( EBRXTYPE, BOARDTYPE );
    Host host;
    while( int len = udp.recvfrom( (char*)rxPacket.Bytes, ETHERBOARDPACKETLEN, &host ) > 0 ) {
      pc.printf("From %d.%d.%d.%d: ", host.getIp()[0], host.getIp()[1], host.getIp()[2], host.getIp()[3] );
      Hexdump::Dump( pc, rxPacket.Bytes, ETHERBOARDPACKETLEN );
      pc.printf( " Length=%d\r\n", len );
      // Check packet length is correct and packet is valid.
      if( ( len == ETHERBOARDPACKETLEN ) && rxPacket.IsValid( EBTXTYPE, BOARDTYPE ) ) {
          pc.puts( "VALID\r\n" );
          // Send a reply.
          txPacket.UpdateChecksum();
          udp.sendto( (char*)txPacket.Bytes, ETHERBOARDPACKETLEN, &host );
      }
      else {
          pc.puts( "NOT VALID\r\n" );
      }
    }
    break;
  }
}

/****************/
/* MAIN PROGRAM */
/****************/

int main() {
  // Setup serial port to development PC.
  pc.baud( 38400 );
  pc.format( 8, Serial::None, 1 );
  pc.printf( "\r\nSetting up...\r\n" );
  // Setup Ethernet link.
  EthernetErr ethErr = eth.setup();
  if( ethErr ) {
    pc.printf( "Error %d in setup.\r\n", ethErr );
    return -1;
  }
  pc.printf( "Setup OK\r\n" );
  
  Host multicast( IpAddr(), 50000, NULL ); //Join multicast group on port 50000
 
  // Set handler to be called when UDP socket event occurrs.
  udp.setOnEvent(&onUDPSocketEvent);
  
  udp.bind(multicast);
  
  // Timer tmr;
  // tmr.start();
  while( true ) {
    Net::poll();
    /*
    if(tmr.read() > 5) {
      tmr.reset();
      const char* str = "Hello world!";
      udp.sendto( str, strlen(str), &multicast );
      pc.printf("%s\r\n", str);
    }
    */
  }

  
}

and this is the output I get on the USB serial port thing. Note the text "Length=1" :

From 172.27.12.1: EB 54 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0
0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 00 00 BF FE Length=1
NOT VALID
From 172.27.12.1: EB 54 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0
0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 00 00 BF FE Length=1
NOT VALID
From 172.27.12.1: EB 54 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0
0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 00 00 BF FE Length=1
NOT VALID

I haven't bothered to put up the source code for the Hexdump or EtherBoardPacket modules, but I think it is fairly safe to say they are not the culprits.

Any thoughts anyone?

20 Feb 2011

Can you confirm with something like ethereal or tcpdump that you really receive packets with 105 bytes? As far s I understand your code you always dump the whole buffer, regardless of what has been received. The length you submit to recvfrom() is just the maximum length of you buffer, and you get back the number of bytes received - which might be less.

21 Feb 2011

OK. I tried to install WireShark on my Windows XP PC, but I cannot get WinPcap to install. I get this error :

/media/uploads/RichardE/error_message.png

So I sent them an email.

Meanwhile I'm pretty sure that the packets really do contain 105 bytes because if I modify the program that transmits the messages so that it fills bytes 3 to 102 of the packet with a number just before transmitting the packet I can see this getting through OK. The number increments on each packet transmitted, so what comes out of the mbed serial port is this:

 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A
1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1
A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1
 A1 A1 DB BF Length=1
NOT VALID
From 172.27.12.1: EB 54 01 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2
 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A
2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2
A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2 A2
 A2 A2 77 BF Length=1
NOT VALID
From 172.27.12.1: EB 54 01 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3
 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A
3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3
A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3 A3
 A3 A3 13 BF Length=1
NOT VALID
From 172.27.12.1: EB 54 01 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4
 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A
4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4
A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4 A4
 A4 A4 AF BE Length=1
NOT VALID

You can see that bytes 0 to 2 always contain the same data and the last two bytes contain a checksum.

So although all the data seems to be getting through, it is still reporting that the message length is 1.

Has anyone else seen this behaviour?

21 Feb 2011

Look strange, but the code in LwipNetUdpSocket::recvfrom seems OK. But I have seens that you are using multicast, maybe thats the culprit. I'm using the NTP client extensively, which is based on UDP, and it works fine.

The only case I can see would be when your server actually sends 106 bytes, and you somehow lose every second packet - then you would get a response of 1. Can you print the response of recvfrom when a result <0 is received?

21 Feb 2011

Doh! Got it! Nothing to do with the library. It is an operator precedence thing. The culprit is this line :

    while( int len = udp.recvfrom( (char*)rxPacket.Bytes, ETHERBOARDPACKETLEN, &host ) > 0 ) {

This is evaluated as if it was written like this :

    while( int len = ( udp.recvfrom( (char*)rxPacket.Bytes, ETHERBOARDPACKETLEN, &host ) > 0 ) ) {

The recvfrom method does indeed return 105 and since this is greater than 0 the value assigned to len is the int equivalent of true, which on this compiler must be 1. That is why it was displaying "len = 1". All I can say in my own defence is that I didn't write that line of code :)

What is needed is this :

    while( ( int len = udp.recvfrom( (char*)rxPacket.Bytes, ETHERBOARDPACKETLEN, &host ) ) > 0 ) ) {

Too many brackets is always better than too few!

Thanks for your help. I think I might track down the example I got this from and point out the problem.

21 Feb 2011

Double doh! No, it is all my fault. The original example doesn't do it quite like that and I just had to go and "optimise" it. Serves me right.