Can FreeRTOS_select() be used with TCP type sockets?

eduardo1966 wrote on Monday, April 08, 2019:

Hi

I would like to know if the function FreeRTOS_select() can be used with TCP sockets?
The example provided here:
https://www.freertos.org/FreeRTOS-Plus/FreeRTOS_Plus_UDP/API/select.shtml
creates the sockets as FREERTOS_IPPROTO_UDP

In other words is there anyway of having a non-blocking FreeRTOS_recv? Or a blocking FreeRTOS_recv for say 1 second?

maybe using FreeRTOS_setsockopt()?

heinbali01 wrote on Monday, April 08, 2019:

Sure you can use FreeRTOS_select() on TCP sockets as well.
You were looking at old documentation of the UDP-only library.
Here you find all TCP- and UDP-API calls.

heinbali01 wrote on Tuesday, April 09, 2019:

In other words is there anyway of having a non-blocking
FreeRTOS_recv? Or a blocking FreeRTOS_recv for say 1 second?
maybe using FreeRTOS_setsockopt()?

Yes you can set the maximum time that FreeRTOS_recv() will wait by setting the socket option FREERTOS_SO_RCVTIMEO.

There are three different possibilities to handle multiple socket within a single task:

Method 1) Semaphores
Method 2) Socket call-backs
Method 3) Use select

What you can do is call FreeRTOS_recv() and FreeRTOS_send() in a non-blocking way, and let the task block in a central place, either on a semaphore, or a task-notification, or a call to select().

Method 1) Semaphores

Give the task a semaphore and anyone can wake-up your task by giving to the semaphore ( xSemaphoreGive() or xSemaphoreGiveFromISR() ).

The IP-task can also give to the semaphore if you bind a socket to that semaphore ( on reception of data, on TX space available, on disconnect etc. )

Define in FreeRTOSIPConfig.h:

    #define ipconfigSOCKET_HAS_USER_SEMAPHORE    1

and bind the semaphore to every socket owned by the task. There is a NTP-demo (Network Time Protocol) which uses a semaphore:

    // Declare it
    static SemaphoreHandle_t xNTPWakeupSem = NULL;

    // Create it
    xNTPWakeupSem = xSemaphoreCreateBinary();

    // Bind it
    FreeRTOS_setsockopt(
        xUDPSocket,
        0,
        FREERTOS_SO_SET_SEMAPHORE,
        ( void * ) &xNTPWakeupSem, sizeof( xNTPWakeupSem ) );

This is a UDP socket, but it works the same for TCP sockets. The IP-task will give to the semaphore for any relevant event:

  • Data has arrived
  • Data has been sent and TX space is available ( TCP-only )
  • A connection is established ( either accept() or connect() )
  • A connection is broken ( or an error occurred )
    for( ;; )
    {
        // Sleep for 10 seconds
        xSemaphoreTake( xNTPWakeupSem, pdMS_TO_TICKS( 10000ul ) );

        /* Check sockets and messages in a non-blocking way. */
    }

Method 2) Socket call-backs

Define ipconfigUSE_CALLBACKS as 1 in FreeRTOSIPConfig.h

Now you can bind call-back ( application hooks ) to each of the sockets:

FREERTOS_SO_TCP_CONN_HANDLER  ( 6 )  /* Install a callback for (dis) connection events. */
FREERTOS_SO_TCP_RECV_HANDLER  ( 7 )  /* Install a callback for receiving TCP data. */
FREERTOS_SO_TCP_SENT_HANDLER  ( 8 )  /* Install a callback for sending TCP data. */

FREERTOS_SO_UDP_RECV_HANDLER  ( 9 )  /* Install a callback for receiving UDP data. */
FREERTOS_SO_UDP_SENT_HANDLER  ( 10 ) /* Install a callback for sending UDP data. */

Define a handler, e.g. on-tcp-receive

    BaseType_t xOnTcpReceive( Socket_t xSocket, void * pData, size_t xLength )
    {
        xSemaphoreGive( myTaskSemaphore );
        // or
        xTaskNotifyGive( myTaskHandle );

        return 0; // KEEP the packet, it will be stored in the RX buffer of the socket
                  // FreeRTOS_recv() must be called later-on
        // or
        return 1; // DISCARD the packet, it will not be stored
    }

    F_TCP_UDP_Handler_t handler;
    handler.pxOnTcpReceive = xOnTcpReceive;
    FreeRTOS_setsockopt( sock, 0, FREERTOS_SO_TCP_RECV_HANDLER,
        ( void * ) &handler, sizeof( handler ) );

Call-backs ( Application Hooks ) are always error-prone: it looks like user code, but in fact the code is run ( called ) from the IP-task. Thus most +TCP API’s can not be called from within the call-back.

Method 3) Use select

Define ipconfigSUPPORT_SELECT_FUNCTION as 1 in your FreeRTOSIPConfig.h

And now you can use the select() functions. Here you find documentation.

Using select() is less flexible: you can only wake-up the task if you can access the sockets.

You could use:

    #define ipconfigSUPPORT_SIGNALS     1

and interrupt a select() buy calling :

    FreeRTOS_SignalSocket()

Most compatible is 3) select(), while 2) semaphore is by far the easiest.

The FTP/HTTP demo servers use a single task. It blocks in a call to select(). It can handle an unlimited number of FTP- or HTTP-clients, only limited by the amount of available RAM.

eduardo1966 wrote on Tuesday, April 09, 2019:

wow :slight_smile: thank you for the juicy info.

eduardo1966 wrote on Thursday, April 11, 2019:

When using sockets and after the semaphore has been given how could I find out the reason for the Give?
I’m interested in these 3:
A connection is established ( either accept() or connect() )
A connection is broken ( or an error occurred )
Data has arrived