FreeRTOS+TCP require calling to vPortFree() and should using heap_1


I’m want to add FreeRTOS+TCP to project who work with heap_1.c
I thought that using FREERTOS_SO_REUSE_LISTEN_SOCKET I will be able to use one socket only.

Apparently it seems impossible. When the socket is closed, the function vPortFree is called, which is illegal in heap_1. There are calls to vEventGroupDelete, for example.

  1. Is there a way to do it? The logic of reusing socket suggest that the same resources can be used without delete, as long as we stay with one (or N predefined number) socket.

  2. If not, it should be clear in the documentation that FreeRTOS+TCP shouldn’t be used with heap_1


You are right in that you cannot use FreeRTOS+TCP with heap_1. We have some feature requests to be able to use FreeRTOS+TCP with statically allocated memory only, but this will take some time to implement and will result in quite a restrictive system as you will have to know in advance how many sockets are required.

I normally use heap_4 or heap_5 for +TCP projects.
FREERTOS_SO_REUSE_LISTEN_SOCKET is useful for low-RAM projects: the space allocated for a server socket is passed to the client socket. Only when the connection is finished, a new server socket will be created.

So is it possible to edit the documentation to make it clear?
I found this only after I was done with importing the TCP stack…

That’s why I thought there will be no problem releasing the resources and close the socket, without delete or free.

I am working on a project on which we also use the heap_1.c memory model. We managed to have the system running and communicate with it. At the moment we are facing the same behavior as the topic/thread starter, that the vPortFree method is called when a socket is closed.
Since this topic is almost a year old, I would like to know if there have been changes regarding to this subject, or that there are workarounds or solutions to this?

There is no change to the above. Changing this behaviour would take some time and currently our TCP development time is on implementing IPv6, multiple interfaces and MISRA compliance.

Like I wrote, I am very much a fan of using either heap_4 or heap_5. They are very well optimised and heap_5 is initialised in a very dynamic way.

I just see that the socket option FREERTOS_SO_REUSE_LISTEN_SOCKET was used. That is an option for advanced users, and must be well understood.

Imagine a telnet server that will only accept one client at a time. You create a server socket, bind it to port 23, and call FreeRTOS_listen(). As soon as it gets connected, it turns the server socket into a client socket. The client socket will get the same address as the server socket:

    client_socket = FreeRTOS_accept( server_socket );
    if( xSocketValid( client_socket ) )
        /* The server socket stops to exist and
         * shall not be referred to any more */
        server_socket = NULL;

When FreeRTOS_accept() succeeds, the identifier server_socket may not be used any more.
The client_socket will be used to talk with the telnet client. As soon as that conversation is ready, the application must close ( delete ) the client socket:

FreeRTOS_closesocket( client_socket );
client_socket = NULL;

and create a new server socket.

It is also possible to create a normal server socket and limit the number of connected clients. In that case use the second parameter of FreeRTOS_listen():

BaseType_t client_count = 3;
FreeRTOS_listen( server_socket, client_count );

This only works when the socket option FREERTOS_SO_REUSE_LISTEN_SOCKET is not used.
Now 3 clients will manage to connect to the server. Number 4 will get a RST response to its attempt to connect.
As soon as one client disconnects, a new connect attempt will succeed.
This behaviour is slightly different from the backlog argument normally seen with listen().
An embedded device has limited resources, and this feature will protect it against SYN attacks from outside: the library will reply with a RST packet, and no pvPortMalloc() will be spilled.

In theory you can get a socket back to listen mode.
call FreeRTOS_shutdown()
wait for the peer to complete the FIN/ACK protocol
call FreeRTOS_listen() now it should succeed.

(Witout any guarntee that it will work, I have never tried this, just read the source).

@bernd-edlinger wrote:

In theory you can get a socket back to listen mode. call FreeRTOS_shutdown() wait for the peer to complete the FIN/ACK protocol call FreeRTOS_listen() now it should succeed.

(Without any guarantee that it will work, I have never tried this, just read the source).

I did try this with a reusable socket, but I ran into problems. Since then I only use a socket a single time.

Note that for a socket with FREERTOS_SO_REUSE_LISTEN_SOCKET, the operation is slightly different from the usual:

    Socket_t xParentSocket = FreeRTOS_socket()
    FreeRTOS_bind( xParentSocket );
    FreeRTOS_listen( xParentSocket, 1 );
    for( ;; )
        Socket_t xChildSocket = FreeRTOS_accept( xParentSocket );
        if( xSocketValid( xChildSocket ) == pdTRUE )
            /* Communicate with child socket */
            /* Close the parent/child socket. */
            FreeRTOS_closesocket( xChildSocket );
            /* Create a new parent socket. */
            xParentSocket = FreeRTOS_socket();
            FreeRTOS_bind( xParentSocket );
            FreeRTOS_listen( xParentSocket, 1 );

Okay, I meant something like this:

    struct freertos_sockaddr sa;
    BaseType_t on= 1;
    FreeRTOS_setsockopt( xParentSocket, 0, FREERTOS_SO_REUSE_LISTEN_SOCKET, &on, sizeof(on) );
    FreeRTOS_bind( xParentSocket, &sa, sizeof(sa));
    FreeRTOS_listen( xParentSocket, 1 );
    for( ;; )
        Socket_t xChildSocket = FreeRTOS_accept( xParentSocket, NULL, 0 );
        if( xChildSocket == xParentSocket )
            /* Communicate with child socket */
            FreeRTOS_send ( xChildSocket, "hello", 5, 0 );
            /* Close the parent/child socket. */
            FreeRTOS_shutdown( xChildSocket, FREERTOS_SHUT_WR );
            /* try to listen again */
            while( FreeRTOS_listen( xParentSocket, 1 ) < 0)
                vTaskDelay(10); /* wait for socket state eCLOSE_WAIT */

But it is probably not all too useful, since it sends a FIN
and has to wait for the FIN-ACK from the peer to make the
socket enter the eCLOSE_WAIT state again.
If the peer does not co-operate here, a rather lengthy time-out
will probably happen, but I have not tried.