Ethernet Receive Problem

Why is nsapi_size_or_error_t always -3001 and becomes never > 0, even when I get a message?
Using a STM32-NucleoF767ZI board with meeds 6.8 and Mbed Studio

Following code:

#include "mbed.h"
#include "EthernetInterface.h"

#define UDP_RECEIVE_BUFFER 255

// local network settings
const uint8_t LOCAL_IP[4] = {10, 101, 1, 201};
const uint8_t SUBNET[4] = {255, 255, 0, 0};
const uint8_t GATEWAY[4] = {0, 0, 0, 0};

// grandMA3 network settings
uint8_t eosIP[4] = {10, 101, 1, 100};
uint16_t eosUdpRxPort = 8000; // receive port of the console
uint16_t eosUdpTxPort = 8001; // send port of the console

EthernetInterface eth;
UDPSocket udp;

SocketAddress send;
SocketAddress receive;

int main() {

	SocketAddress localIP(LOCAL_IP, NSAPI_IPv4);
	SocketAddress subnet (SUBNET, NSAPI_IPv4);
	SocketAddress gateway(GATEWAY, NSAPI_IPv4);
	eth.set_network(localIP, subnet, gateway);
	eth.set_blocking(false);
	eth.connect();

	receive.set_ip_bytes(eosIP, NSAPI_IPv4);
	receive.set_port(eosUdpTxPort);
	send.set_ip_bytes(eosIP, NSAPI_IPv4);
	send.set_port(eosUdpRxPort);

	udp.open(&eth);
	udp.set_blocking(false);
	udp.connect(send);
	udp.bind(eosUdpTxPort);

	while (true) {

		static char buffer[UDP_RECEIVE_BUFFER];
		nsapi_size_or_error_t size = udp.recv(buffer, sizeof(buffer));
		if (size > 0) {
			printf("message size: %d\n", size);
			printf("OSC message: %s\n", buffer);
			memset(buffer, 0, UDP_RECEIVE_BUFFER);
			}
		}
	}

Update:
If I use following code in the while loop

	while (true) {
		static char buffer[UDP_RECEIVE_BUFFER];
		nsapi_size_or_error_t size = udp.recv(buffer, sizeof(buffer));
		if (buffer[0] != 0) {
			printf("message size: %d\n", size);
			printf("OSC message: %s\n", buffer);
			memset(buffer, 0, UDP_RECEIVE_BUFFER);
			}
		}

I get follwing out in the console when I send data manually with packetsender app:

for UDP datagrams, you should use udp.recvfrom(), there you’ll get also the senders address.

See also the examples
https://os.mbed.com/docs/mbed-os/v6.7/apis/udpsocket.html

I will try it, but it didn’t answer my question.

I found the problem it is

udp.connect(send);

If I comment it out the nsapi_size_or_error_t is correct.
I think this is a bug.

Update:
The receiveFrom example only works if I bind the socket:

udp.bind(eosUdpTxPort);

something is wrong

UDP is connectionless, it doesn‘t need the connect.
receivefrom works also without bind, a SocketAddress with the listening Port will work also. As in the UDP snippet.

UDP is connectionless, it doesn‘t need the connect.
This right, but connect() allows to use send() instead of sendTo()
I tried with a SocketAddress only with the listing port as you mentioned but this does not work.
The updated example which does not work without binding:

#include "mbed.h"
#include "EthernetInterface.h"
#include <cstdint>
#include <cstring>

#define UDP_RECEIVE_BUFFER 255

// local network settings
const uint8_t LOCAL_IP[4] = {10, 101, 1, 201};
const uint8_t SUBNET[4] = {255, 255, 0, 0};
const uint8_t GATEWAY[4] = {0, 0, 0, 0};

// grandMA3 network settings
uint8_t eosIP[4] = {10, 101, 1, 100};
uint16_t eosUdpRxPort = 8000; // receive port of the console
uint16_t eosUdpTxPort = 8001; // send port of the console

EthernetInterface eth;
UDPSocket udp;

SocketAddress send;
SocketAddress receive;

int main() {

	SocketAddress localIP(LOCAL_IP, NSAPI_IPv4);
	SocketAddress subnet (SUBNET, NSAPI_IPv4);
	SocketAddress gateway(GATEWAY, NSAPI_IPv4);
	eth.set_network(localIP, subnet, gateway);
	eth.set_blocking(false);
	eth.connect();

	send.set_ip_bytes(eosIP, NSAPI_IPv4);
	send.set_port(eosUdpRxPort);
	receive.set_port(eosUdpTxPort);

	udp.open(&eth);
	udp.set_blocking(false);
	//udp.bind(eosUdpTxPort);

	while (true) {
		static char buffer[UDP_RECEIVE_BUFFER];
		nsapi_size_or_error_t size = udp.recvfrom(&receive, buffer, sizeof(buffer));
		if (size > 0) {
			printf("NSAPI size: %d\n", size);
			printf("message: %s\n", buffer);
			printf("Console Address: %s Port: %d\n\r", receive.get_ip_address(), receive.get_port());
			udp.sendto(send, buffer, size); // echo
			memset(buffer, 0, UDP_RECEIVE_BUFFER);
			}
		}
	}

When using bind(port) I get the IP and Port!!! of the sender.

And when you set receive SocketAddress to IP 0.0.0.0? Documentation say it is initially unspecified. In the example, the IP is set to the server address with gethostbyname.

I will give a try, meanwhile the bind method works for me.

I tried with SocketAddress 0,0,0,0 also with NULL and server IP (eosIP), no luck.

yes, I tried just the same with the same result. Thats a little bit strange, because the example does the same execept using sendto() before.
I have initialised the socket also with full IP information.

    SocketAddress remoteAddr("0.0.0.0", NSAPI_IPv4, 8000);
    SocketAddress remoteAddr("192.168.100.108", NSAPI_IPv4, 8000);

Maybe the example was done with a WiFi platform and then ported to Ethernet and LWIP?

I will do some more tests tomorrow. I’m sure the example worked and I tested also a lot with IPv6, both should work.
Target is a Nucleo F746.

This explains something:

So the bind() is necessary or a previous sendto(), which does an implicit bind.

I was wondering why the example is working without bind(), but that link explains it.

I think the example works because

  1. blocking mode
  2. no polling

But the initial problem is unsolved.

The initial problem is none, recv works as documented:
https://os.mbed.com/docs/mbed-os/v6.7/mbed-os-api-doxy/class_internet_datagram_socket.html#ad80e0d2c2bbf75a9c80b54e6bebec4a0

The initial problem was that udp.connect(send); blocks the correct output of
nsapi_size_or_error_t size = udp.recv(buffer, sizeof(buffer)); and gives always -3001 when receiving data.

I think you get the error -3001 (NSAPI_ERROR_WOULD_BLOCK) because the socket is non-blocking and you request more data than available. This brings -3001, and the documentation says:

Note
    recv() is allowed write to data buffer even if error occurs.

This is of course strange, I don’t know if this is Posix standard behaviour.

And you are setting also the eth interface to non-blocking, but without event handler:

	eth.set_network(localIP, subnet, gateway);
	eth.set_blocking(false);
	eth.connect();

It is not guaranteed that the interface is set up completely when you acces the netif directly after connect(). It may work now, but when dhcp is enabled, this will defintely fail.

It is definitely a bug. When replace udp.connect(send); and I use udp.sendto() instead udp.send()the udp.recv() function works as expected.
Can you give examples to use event handler for sending and receiving?
I lost a bunch of packets, however.

not yet, but I’m working also on UDP communication now.
In the examples, I found only this for the Eth state change:

I’ve used it similar, but its dangerous because the callback runs in an interrupt context.

The blocking recvfrom() works fine, but I will look also for event driven communication.

In the meantime, I have worked also on my UDP connection. I’ve read your issue, the answers seem reasonable.

I’ve tested first a version with a non-blocking UDP Socket and used sigio(). But that was more inefficient:

  • sigio can run in interrupt context, so you cannot directly send a datagram after receiving
  • in signal handler, I do receiving the packets until I get error would_block. For the incoming packets, I used a memory pool and a queue for storing the pointer to data, remote and packet size.
  • I have a 2nd thread that handles the queue of received packets

Now I have removed this stuff and do a blocking recvfrom() in an own thread. After receiving, I call a message handler that can also respond directly with a sendto(remote,…);

I’ve seen also that the memory setting for lwip is pretty stingy, increasing the lwip memory pool in your mbed_app.json should perform better:

            "lwip.mem-size": 16384,