FreeRTOS TCP: Forcefully close connected server socket

Assuming a device running FreeRTOS TCP which is serving a number of sockets each using the FREERTOS_SO_REUSE_LISTEN_SOCKET option. A client has connected and the device has “accepted()” that connection. If the device now would like to “kick out the user” which is connected to this socket, which API calls should the application perform? I’ve attempted to simply call FreeRTOS_shutdown() on the socket from the application side which works, but it takes around 38 seconds before a new client can connect (assuming this is related to hang and/or keep alive since both options are enabled). Is there a faster way to achieve this on the device side?

Do you also finally FreeRTOS_closesocket ?
Have a look here for an example howto gracefully close a connection:

BTW FREERTOS_SO_REUSE_LISTEN_SOCKET allows only 1 socket connection as documented here FreeRTOS_listen API reference

Yes, I call FreeRTOS_closesocket() and follow the recommended graceful shutdown. So the solution all “works”, but it takes a lot longer than anticipated. So my question is if the time between I call FreeRTOS_shutdown() and until read returns error can be shortened or if there is a faster non-graceful way of doing this.

FREERTOS_SO_REUSE_LISTEN_SOCKET is used to intentionally only allow 1 connection at a time. The device is a 5 port TCP socket to UART serial port terminal server so I’m only interested in having 1 client connected at a time since the TCP socket has a 1:1 mapping to the physical UART ports. The issue is in rare cases where the host connecting the terminal server crashes and the host doesn’t properly disconnect, I’d like to via a REST API on another port be able to “kick out” lingering and bad host connections quickly. Device is typically installed in continuous integration automation so there is little user intervention possible.

Here is an example of a typical shutdown sequence:

    /* Finished using the connected socket, initiate a graceful close:
    FIN, FIN+ACK, ACK. */
    FreeRTOS_shutdown( xSocket, FREERTOS_SHUT_RDWR );
    
    /* Expect FreeRTOS_recv() to return an error once the shutdown is
    complete. */
	/* Let it last at most 4 seconds. */
	static const TickType_t xReceiveTimeOut = pdMS_TO_TICKS( 4000 );
    TickType_t xTimeOnEntering = xTaskGetTickCount();
    do
    {
        xReturned = FreeRTOS_recv( xSocket, &( pcBuffer[ 0 ] ), sizeof( pcBuffer ), 0 );
    
        if( xReturned < 0 )
        {
            break;
        }
    
    } while( ( xTaskGetTickCount() - xTimeOnEntering ) < xReceiveTimeOut );

It should last less than 100 ms.

I would be curious to see a PCAP made on the PC that connects to the TCP device. Somewhere a device seems to be waiting for a time-out.

Also if you like, send some of the TCP handling code.

Hartmut wrote:

BTW FREERTOS_SO_REUSE_LISTEN_SOCKET allows only 1 socket connection as documented here FreeRTOS_listen API reference

Normally a server socket can be used for ever, but when it is a “REUSE” socket, the server socket must be created again.
You can create a new server socket before the earlier socket is closed.

Another method would be the folllowing: do not use FREERTOS_SO_REUSE_LISTEN_SOCKET and call listen() with xBacklog = 2.

  1. A TCP connected is established and everything works fine.
  2. A new TCP connection comes in, and it will take over.
  3. The connection 1 is now put aside and it will follow the above shutdown sequence.
  4. In a meanwhile the new connection will do the job.

There will be no need to close and re-create the TCP server socket.

I just looked at how a TCP connection is shut down normally, see attachment : shutdown_session.zip (620 Bytes)

Or visualised here:

It takes less than a ms before the connection is actually closed.

You could also just close the socket, hoping that the remote end sends another TCP packet. When that happens, the FreeRTOS device will reply with a RST.

Would it be possible for you to attach a PCAP recording?

If I read Daniel’s description correctly, then his problem occurrs when the peer crashes and therefore can not handle a graceful shutdown.

If I remember my RFC 793 correctly, this scenario means that the device will leave its TCP connection in the TIME_WAIT timeout state. @htibosch : What is the FreeRTOS+TCP setsocketopt to set the time-wait timeout?

Hi,

Many thanks for valuable feedback! I’ve performed some captures using wireshark, here’s a link to the results (not allowed to post attachments or links…):

https://drive.google.com/file/d/1a1qKAWb7LYqa5txqtpqlqwle5eXwZdRV/view?usp=share_link

In ts500_shutdown_no_traffic.pcapng I open a connection from host (192.168.2.50) to the device (192.168.2.50 port 3301). Then I issue a HTTP request to port 80 which executes a portrelease action on the device which in turn performs a FreeRTOS_shutdown() on the socket from the device side. Device sends FIN, ACK and the host responds with ACK and then nothing more happens. Socket is in CLOSE_WAIT on the host side until I send some data on it around 642 secs into the trace which is the PSH, ACK → RST, ACK sequence.

In ts500_shutdown_try_open.pcapng I have basically the same scenario but after FreeRTOS_shutdown() I attempt to issue a new connection to device port 3301 from the host, this doesn’t work since the port is “busy” on the device side. Port is closed and ready for new connection first when the host sends data on the first port opened.

@RAc : Yes, you understood the requirement correctly. In a real scenario the host TCP/IP stack basically goes away, that stack is running inside some virtual machine which may crash and disappear (out of memory kill etc). This scenario is hard to reproduce, so in my test here I run against my desktop Linux host.

I wonder though after studying this in more detail if I have a problem on the application side on the device. The sockets maintained by the device are all members of a socket set using FreeRTOS_FD_SET() call, after device application calls FreeRTOS_shutdown() it expects a negative return value from FreeRTOS_recv() before calling FreeRTOS_closesocket(). But actually performing the FreeRTOS_recv() call on this socket is not done until FreeRTOS_select() actually indicates that something happened on this socket. Maybe this is what is missing? FreeRTOS_select() is not returning that the socket which the application has tried to close is readable after FreeRTOS_shutdown() was called on it? Mask set on FreeRTOS_FD_SET() is eSELECT_READ | eSELECT_EXCEPT, is this not enough?