Hello again,
I am back with a question regarding the FreeRTOS IP stack implementation for Zynq7, this time regarding UDP servers.
I noticed something strange if you send a message of a known size from a UDP client to the UDP server on the Zynq:
If you try to split up the read process into two separate calls to recvfrom(), the second one will always fail.
When using TCP client & server, the same scenario works. I don’t know if this different behaviour is intended for UDP or a bug.
To demonstrate the issue I wrote a simple test program, based on the example on how to implement UDP recvfrom() from here.
I create two tasks and start the scheduler:
-
InitTask() - starts the TCP/IP stack
-
TutorialTask() - implements the UDP server
void vStartTutorialTask( void )
{
xTaskCreate( vUDPReceivingUsingStandardInterface, "TutorialTask", TUTORIALTASK_STACK_SIZE, NULL, tutuorialTASK_PRIORITY, ( TaskHandle_t * ) NULL );
}
static void vUDPReceivingUsingStandardInterface( void *pvParameters )
{
long lBytes;
uint8_t cReceivedString[ 60 ] = {};
struct freertos_sockaddr xClient, xBindAddress;
uint32_t xClientLength = sizeof( xClient );
Socket_t xListeningSocket;
TickType_t xNextWakeTime = 0;
/* Initialize xNextWakeTime */
xNextWakeTime = xTaskGetTickCount();
// Wait for init task to finish
xEventGroupWaitBits( os_syncEvents,
(1 << OS_FLAG_POS_SYNC_EVENTS_BASIC_RESOURCE_INIT_DONE),
OS_FLAG_KEEP_ON_EXIT,
OS_FLAG_WAIT_FOR_ALL_BITS,
OS_WAIT_FOREVER );
/* Attempt to open the socket. */
xListeningSocket = FreeRTOS_socket( FREERTOS_AF_INET,
FREERTOS_SOCK_DGRAM, /*FREERTOS_SOCK_DGRAM for UDP.*/
FREERTOS_IPPROTO_UDP );
/* Check the socket was created. */
configASSERT( xListeningSocket != FREERTOS_INVALID_SOCKET );
/* Bind to port 10000. */
xBindAddress.sin_port = FreeRTOS_htons( 10000 );
FreeRTOS_bind( xListeningSocket, &xBindAddress, sizeof( xBindAddress ) );
//The sender always sends this amount of bytes (hard-coded)
const uint32_t totalBytesSent = 10;
//Amount of bytes requested to receive
uint32_t requestedBytes = 1;
for( ;; )
{
/* Receive data from the socket. ulFlags is zero, so the standard
interface is used. By default the block time is portMAX_DELAY, but it
can be changed using FreeRTOS_setsockopt(). */
lBytes = FreeRTOS_recvfrom( xListeningSocket,
cReceivedString,
requestedBytes,
0,
&xClient,
&xClientLength );
if( lBytes > 0 )
{
/* Data was received and can be process here. */
xil_printf("\nFirst Part: %d Bytes: ", lBytes);
xil_printf((char *) cReceivedString);
memset(cReceivedString, 0, sizeof(cReceivedString));
//Try to receive second part of data.
if(totalBytesSent - requestedBytes > 0)
{
lBytes = FreeRTOS_recvfrom( xListeningSocket,
cReceivedString,
totalBytesSent - requestedBytes,
0,
&xClient,
&xClientLength );
xil_printf(", Second Part: %d Bytes: ", lBytes);
xil_printf((char *) cReceivedString);
memset(cReceivedString, 0, sizeof(cReceivedString));
}
else
{
xil_printf(", There is no second part, all bytes already received.");
}
//Increase number of bytes read out as the first part, or start over at 1 if max size is reached
if(requestedBytes < totalBytesSent)
requestedBytes++;
else
requestedBytes = 1;
}
vTaskDelayUntil(&xNextWakeTime, TUTORIAL_TASK_TIMEOUT_MS);
}
}
As a UDP client, I used the most basic python implementation, running on my host PC. I’ll attach the code for reference. Basically, it just sends 10 bytes of data (ASCII digits from 0-9), every 10 sec.
The output is the following:
First Part: 1 Bytes: 0, Second Part: -11 Bytes:
First Part: 2 Bytes: 01, Second Part: -11 Bytes:
First Part: 3 Bytes: 012, Second Part: -11 Bytes:
First Part: 4 Bytes: 0123, Second Part: -11 Bytes:
First Part: 5 Bytes: 01234, Second Part: -11 Bytes:
First Part: 6 Bytes: 012345, Second Part: -11 Bytes:
First Part: 7 Bytes: 0123456, Second Part: -11 Bytes:
First Part: 8 Bytes: 01234567, Second Part: -11 Bytes:
First Part: 9 Bytes: 012345678, Second Part: -11 Bytes:
First Part: 10 Bytes: 0123456789, There is no second part, all bytes already received.
First Part: 1 Bytes: 0, Second Part: -11 Bytes:
...
As you can see, the second part says (-11) as byte count, which is the error code that recvfrom()
returns when attempting to read more data. I am aware that (-11) essentially means timeout.
#define pdFREERTOS_ERRNO_EWOULDBLOCK 11 /* Operation would block */
It may looks like, I just have to wait longer for the missing bytes, but given the fact that the data can indeed be received, if it is read during the first attempt, it seems extremly unlikely to me to be the reason. More likely is that the data somehow is lost, if not read all in one go.
In some applications this may not be viable, e.g. if the packet size is not known beforehand, but is transmitted as part of a fixed-sized message header upfront. Once you retrieve the actual message length by first reading the header and try to retrieve it by calling recvfrom()
once more, it is already gone.
All I can think of is that I somehow messed up the configuration of the socket, or that this is indeed desired UDP behaviour, or indeed a bug!
Best Regards,
Emanuel
simpleUdpSenderClient.py.zip (510 Bytes)