FreeRTOS_FD_SET on a Listening TCP Socket

joehinkle wrote on Sunday, August 07, 2016:

I wrote a small web server and have it running on another platform(os and stack).

I’ve ported it into my embedded application (FreeRTOS + IP).

I create a listen socket.

I create a socket_set.

I use FreeRTOS_FD_SET to set an eSELECT_READ to the listen socket.

I then FreeRTOS_select to wait for a connection on the listen socket.

Should this work with the FreeRTOS_IP stack? I’m assuming an incoming connection is treated as a read on the listen socket thereby enabling the select to react.

Your on-line documentation shows a web_server thread that simply listens for a connection and then spawns after the accept. I’m hoping I don’t have to create tasks for every connection and can manage them all in one task.

Thanks for any insight into this.

Joe

joehinkle wrote on Sunday, August 07, 2016:

I traced a TCP message into the stack.

The stack created a new socket based on the listen but I saw no process to then set an event bit on the listening socket so the select would respond.

I probably missed the call (I’m hoping) and it’s just something I missed that is not causing the select to respond to the READ on the listen socket.

Joe

rtel wrote on Sunday, August 07, 2016:

It is not clear from your post - are you saying you tried this and it
didn’t work, or just asking if it should work?

If you tried it and it didn’t work then there is a known issue whereby
you have to ensure the priority of the task running the TCP/IP stack is
higher than the priority of any tasks using the TCP/IP stack, and lower
than any deferred interrupt handling tasks.

If you are asking if it should work then I believe the answer is yes.
All the connections in the FTP and [basic] HTTP server examples in the
labs download all run in the same task, using a call to
FreeRTOS_select(). You will find the code in
\FreeRTOS-Plus\Source\FreeRTOS-Plus-TCP\protocols\FreeRTOS_TCP_server.c

joehinkle wrote on Sunday, August 07, 2016:

I’m stating it does not work.

Here are my priority settings … the stack is higher than the Web_Server.

Select is firing – return based on timeout.

// FreeRtos set up with 15 task priorities ... see config
// priorities from 0 (idle) to 14
#define	SPI_TASK_PRIORITY		( configMAX_PRIORITIES - 1 )    // max priority
#define	Audio_TASK_PRIORITY		( configMAX_PRIORITIES - 2 )    // max priority
#define Emac_RCV_TASK_Priority       ( configMAX_PRIORITIES - 3 )
//#define ipconfigIP_TASK_PRIORITY    (configMAX_PRIORITIES - 4)        in freertos config
#define	MainProcess_TASK_PRIORITY		( configMAX_PRIORITIES - 5 )

#define WebServer_TASK_PRIORITY	(4)
#define UDP_Debug_TASK_PRIORITY	(3)
#define UDP_Debugger_TASK_PRIORITY	(3)
// #define configTIMER_TASK_PRIORITY		( 2 )          in freertos config
#define	Display_TASK_PRIORITY		( 1 )

Any suggestions?

joehinkle wrote on Sunday, August 07, 2016:

I checked my code to your and I can’t see anything.

Do you notice something I’ve done incorrectly?


    while(1)
    {
	
		    /* Create a TCP socket. */
		ListenSocket = FreeRTOS_socket( FREERTOS_AF_INET, FREERTOS_SOCK_STREAM, FREERTOS_IPPROTO_TCP );
		if( ListenSocket != FREERTOS_INVALID_SOCKET)
			break;
TryAgain:	
        vTaskDelay(100 / portTICK_PERIOD_MS);
    }

		
 /* Set a time out so accept() will just wait for a connection. */
    FreeRTOS_setsockopt( ListenSocket,
                         0,
                         FREERTOS_SO_RCVTIMEO,
                         &xReceiveTimeOut,
                         sizeof( xReceiveTimeOut ) );		
        
	ZeroMemory(&server_addr, sizeof(server_addr));		
    server_addr.sin_port = FreeRTOS_htons( ListenPort );
	server_addr.sin_addr = FreeRTOS_GetIPAddress(); // Single NIC, currently not used
    
	// Bind the address to the socket. 
	if(FreeRTOS_bind(ListenSocket, &server_addr, sizeof( server_addr ) ) == -1 )
	{
		FreeRTOS_closesocket( ListenSocket );
		ListenSocket = FREERTOS_INVALID_SOCKET;
		goto TryAgain;
	}
	

	UDP_Debug_Printf1("Socket created and Bind.\n");
	

    while(1)
    {
		SocketSet2Select = FreeRTOS_CreateSocketSet();
		if( SocketSet2Select)
			break;
			
        vTaskDelay(100 / portTICK_PERIOD_MS);
    }
	

	//Listen to incoming connections
	r = FreeRTOS_listen(ListenSocket , MaxSessions);


	// add listen to socket set 
	FreeRTOS_FD_SET(ListenSocket, SocketSet2Select, eSELECT_READ);


	//Accept and incoming connection
	UDP_Debug_Printf1("Waiting for incoming connections...\n");

	while(1)
	{
	
		/* loop through list of open sessions looking for work */
		for(i = 0; i < MaxSessions; i++)
		{
			if (Sessions[i].S == FREERTOS_INVALID_SOCKET) 
			{
				continue;
			}


			/* If socket is reading, load for a select */
			if(Sessions[i].State == WS_HEADER) 
			{
				if((Sessions[i].HasBeenAddedToSetSelect & 1) == 0)	// no read in place
				{
					Sessions[i].HasBeenAddedToSetSelect |= 1;
					FreeRTOS_FD_SET(Sessions[i].S, SocketSet2Select, (eSELECT_READ | eSELECT_EXCEPT));
				}
			}
			else if(Sessions[i].HasBeenAddedToSetSelect & 1)	// in select - remove it
			{
				Sessions[i].HasBeenAddedToSetSelect &= ~1;
				FreeRTOS_FD_CLR(Sessions[i].S, SocketSet2Select, (eSELECT_READ));
			}


			if(Sessions[i].Flags & WF_DataWasSentLookForComplete)	//(Sessions[i].XmitBuf) || (Sessions[i].Flags & WF_BINARY))
			{
				if((Sessions[i].HasBeenAddedToSetSelect & 2) == 0)	// no write in place
				{
					Sessions[i].HasBeenAddedToSetSelect |= 2;
					FreeRTOS_FD_SET(Sessions[i].S, SocketSet2Select, (eSELECT_WRITE  | eSELECT_EXCEPT));
				}
			}
			else if(Sessions[i].HasBeenAddedToSetSelect & 2)	// in select - remove it
			{
				Sessions[i].HasBeenAddedToSetSelect &= ~2;
				FreeRTOS_FD_CLR(Sessions[i].S, SocketSet2Select, (eSELECT_WRITE));
			}
			
		}

		TimeoutActive = 0;
		
		/* See if any of the sockets have input or ready to send */
		r = FreeRTOS_select(SocketSet2Select, (10000 / portTICK_PERIOD_MS));	// Blocks until activity
		if(r < 0)
			continue;	// error
		if(r == 0)
		{
			TimeoutActive = 1;
		}
		else
		{

			// check if new read on listen
			if(FreeRTOS_FD_ISSET(ListenSocket, SocketSet2Select) & eSELECT_READ)  // read on listen means new connection waiting
				AcceptConnection();
		}


rtel wrote on Sunday, August 07, 2016:

The example is calling:

FreeRTOS_FD_SET( xSocket, xSocketSet, eSELECT_READ | eSELECT_EXCEPT );

What happens if you use eSELECT_EXCEPT too? Maybe the socket is getting
disconnected?

heinbali01 wrote on Sunday, August 07, 2016:

I use FreeRTOS_FD_SET to set an eSELECT_READ to the listen socket.

That is correct, a new incoming connection is treated as a READ event. A successful outgoing connection would cause a WRITE event.

As Richard stated, in the current release of +TCP, there is a problem with FreeRTOS_accept() if the IP-task runs at a low priority.
Your configured priorities seem to be OK.

If you want, publish some code of your web server. Or, alternatively, you can contact us directly (at freertos.org) and we’ll find the cause of the problem.

Regards.

joehinkle wrote on Sunday, August 07, 2016:

Hein:

I posted the code … 2 posts up.

See anything?

Thanks in advance for any insight.

joehinkle wrote on Sunday, August 07, 2016:

I added eSELECT_EXCEPT per your suggestion — still not firing

	FreeRTOS_FD_SET(ListenSocket, SocketSet2Select, (eSELECT_READ | eSELECT_EXCEPT ));

heinbali01 wrote on Sunday, August 07, 2016:

Hi Joe,

Our messages crossed. Tomorrow I will look at the source code that you published.

joehinkle wrote on Sunday, August 07, 2016:

This statement was made above:

If you tried it and it didn’t work then there is a known issue whereby
you have to ensure the priority of the task running the TCP/IP stack is
higher than the priority of any tasks using the TCP/IP stack, and lower
than any deferred interrupt handling tasks.

Can you explain the BOLD section better?

I have a ENET Message Received task that feeds the stack. THAT task is higher priority than your stack priority … is THAT what is meant by the bold statement? If so … I’m good there also.

rtel wrote on Sunday, August 07, 2016:

A deferred interrupt handler is a task that is used to perform what
would otherwise be performed in an interrupt service routine.

Often an Ethernet interrupt wants to allocate a buffer. Depending on
the buffer allocation scheme it might be that the buffer cannot be
allocated from the interrupt, so the interrupt handler just unblocks a
high priority task, and the buffer allocation and other interrupt
processing is then performed in the task.

The idea is a deferred interrupt handling task is blocked to wait for an
interrupt then, when an interrupt occurs, the interrupt handler unblocks
the task that is waiting and returns directly to that task so the
interrupt processing occurs contiguous in time just as if it had been
performed in the interrupt itself. (the interrupt handler interrupts a
lower priority application task, and returns to a higher priority
deferred interrupt handler task.

Search for ‘deferred’ on the following page:
http://www.freertos.org/FreeRTOS-Plus/FreeRTOS_Plus_TCP/Embedded_Ethernet_Porting.html

joehinkle wrote on Sunday, August 07, 2016:

That’s what I thought – That is what I have in stated in the post prior to your reply … so I’m OK there.

Thanks for further explaining.

joehinkle wrote on Monday, August 08, 2016:

Hein:

Have you had a chance to look over the code I posted to help me identify the issue?

Thanks.

Joe

joehinkle wrote on Monday, August 08, 2016:

I’m using the latestest release of the stack.

When did Select processing switch from using a queue to event bits?

From a user perspective, it’s pretty simple and straight forward.

I can’t see anything wrong with my user code.

As I posted earlier, I can trace a TCP connect message coming into the stack and it gets processed by the waiting listen socket. The stack creates a new socket but not sure what you do with it since I’m never notified. I was NOT able to see any code that would process event bits based on that listen action… that process may be hard to trace as your comments in the stack suggest you are posting messages within the stack to handle things at a later time … I’m not to the point where I can absolutly follow that.

Can you please confirm that Select using event bits has been verified to work?

Thanks.

Joe

joehinkle wrote on Tuesday, August 09, 2016:

I found the issue.

At this ime, I would call it a stack issue related to conflicting config options.

If the user has enabled config setting (as I had) ipconfigTCP_HANG_PROTECTION, the the new socket created by the listen is NOT assigned to the listening sockets’s “pxPeerSocket”.

A select on a listening socket for eSELECT_READ is not capable because the listening’s socket “pxPeerSocket” is not set.

Root-cause of my issue … FreeRTOSIPConfig.h CAN NOT have option “ipconfigTCP_HANG_PROTECTION” enabled and expect select to respond to a “eSELECT_READ” on a listening socket.

Dis-Abled option ipconfigTCP_HANG_PROTECTION – it works as expected.

So, I would suggest you have an issue with the implementation of option ipconfigTCP_HANG_PROTECTION as it breaks the select capability.

Joe

joehinkle wrote on Wednesday, August 10, 2016:

Update:

There is NO issue with option ipconfigTCP_HANG_PROTECTION as I stated above.

Option ipconfigTCP_HANG_PROTECTION delays the Listen Select from firing until the connection is completed. By removing the ipconfigTCP_HANG_PROTECTION, the Listen Select will fire with 2 of three 3 connection hand-shakes complete (after the stack replies with SYN+ACK). The ipconfigTCP_HANG_PROTECTION prevents the Listen Select from firing until the stack receives the ACK from the client.

My issue was I was NOT receiving the ACK reply because of another issue: see post

Stack is GREAT!!!
User had the issue.

Joe

heinbali01 wrote on Thursday, August 11, 2016:

About ‘ipconfigTCP_HANG_PROTECTION’ : I don’t think there is an issue with the anti-hanging protection. There is indeed a difference in behaviour, and for good reasons:

#if( ipconfigTCP_HANG_PROTECTION != 0 )
    /* You will only see a new socket as soon as the 3-way hand-shake is ready. */
#else
    /* You will receive a new socket immediately after its creation,
    i.e. after the first SYN. */
#endif

With anti-hanging protection, the IP-stack takes care of the new socket until it is fully connected. In case it fails, the socket will be closed by the stack. It will have a timer running, so incase the peer dies, the socket will be closed by the stack.

Without the anti-hanging protection, the application is made responsible for closing the new socket in case the 3-way handshake was not successful.

With ‘ipconfigTCP_HANG_PROTECTION == 0’ your select() will return a READ event, and accept() does succeed, but the 3-way handshake is still failing.

Later we found why the handshake didn’t work: there was a misconception about xNetworkInterfaceOutput() should work.

BaseType_t xNetworkInterfaceOutput(
    NetworkBufferDescriptor_t * const pxNetworkBuffer,
    BaseType_t xReleaseAfterSend )

If xReleaseAfterSend is false, it means that the NetworkBuffer is passed as read-only. Once the function xNetworkInterfaceOutput() returns, the NetworkBuffer may not be accessed any more.

If xReleaseAfterSend is true, the ownership of the NetworkBuffer is passed to the driver and the driver may (must) release the buffer after using it. It may still be accessed after returning from the function.

A driver that works with so-called zero-copy transmissions will define:

#define ipconfigZERO_COPY_TX_DRIVER                ( 1 ) 

These drivers will always be called with xReleaseAfterSend == true. The Network Buffers will be passed to DMA. When DMA is ready transmitting a buffer, it shall be released by the driver.