I wrote:
It is only allowed to use a socket for reading in one task, and for writing in a different task.
You might get around it by using mutexes, but I would not recommend that. The best and the safest way is to make one task “the owner” of a (series of) socket(s).
Some people want to use two tasks per socket because they use blocking APIs, one for reading and one for writing to a socket. Sometimes you want to read and write, which ever comes first.
There are some alternatives:
Handling multiple sockets within a single task.
There are 4 possibilities:
- The most “standard way” is to use
FreeRTOS_select()
. It takes a socket-set, and each socket has a set of event types. The select APIs can be included by definingipconfigSUPPORT_SELECT_FUNCTION=1
in yourFreeRTOSIPConfig.h
.
BaseType_t rc = FreeRTOS_select( xSocketSet, pdMS_TO_TICKS( 1000U ) );
if( rc > 0 )
{
/* Handle the triggered events. */
}
- A very easy way of handling multiple sockets from a single task is to use a semaphore, and bind that to all of your sockets, using the socket option
FREERTOS_SO_SET_SEMAPHORE
. Make a main-loop and let it sleep/block on the semaphore. This feature is enabled whenipconfigSOCKET_HAS_USER_SEMAPHORE=1
.
for( ; ; )
{
xSemaphoreTake( xServerSemaphore, pdMS_TO_TICKS( 1000U ) );
/* Check your sockets. */
}
- Another easy approach is to write a callback function and bind to your sockets. The callback (“application hook”) will be called from the IP-task at every important event: received a packet, sent a packet, connected, disconnected, error. It does not specify what exactly has happened. Your handler will just wakeup the owning task, and it will be handled soon. The option is enabled by defining
ipconfigSOCKET_HAS_USER_WAKE_CALLBACK=1
.
void socketWakeupCallback( Socket_t xSocket )
{
/* Wake up the handler of this socket. */
}
- And finally:
ipconfigUSE_CALLBACKS=1
. That allows you to set a specific application hooks for five different types of events like reception, transmission, connect/disconnect.
baseType_t onTCPReceive( Socket_t xSocket, void * pData, size_t xLength )
{
/* Inspect the incoming data, wakeup task. */
return 0;
}
Note that every application hook is called from the IP-task. You may not call any FreeRTOS+TCP API. That should be delegated to the handling task. You can set flags and wakeup that particular task. And also set a LED if you want to show that a device is connected.
Very useful: using Socket ID’s :
BaseType_t xSocketSetSocketID( const Socket_t xSocket, void * pvSocketID );
void * pvSocketGetSocketID( const ConstSocket_t xSocket );
It allows you to connect a socket with “an object” that exists in your application. So when your handler is called with a socket parameter, your can find the object or task that is handling that socket by calling pvSocketGetSocketID()
. For example:
void onConnected( Socket_t xSocket, BaseType_t ulConnected )
{
BaseType_t rc = 0;
class CHandler_t * handler = ( CHandler_t *) pvSocketGetSocketID( xSocket );
if( handler != NULL )
{
rc = handler->onConnected( xSocket, ulConnected );
}
return rc;
}
Isn’t that beautiful?