UDP
when calling FreeRTOS_recvfrom()
with the zero-copy flag FREERTOS_ZERO_COPY
, the ownership of a network buffer is transferred from the socket to the application. The contents will be used and the network buffer will be released.
TCP: unlike UDP, TCP data is treated as a stream. All TCP data is stored in the socket in stream buffers, one for transmission and one for reception.
Receive TCP data without copying:
Here is an example taken from the FTP-server project that shows zero-copy reception:
char * pcBuffer = NULL;
const size_t uxMaxCount = 10240U;
/* The "zero-copy" method, pass the address of a pointer: */
xRc = FreeRTOS_recv( xTransferSocket, ( void * ) &( pcBuffer ),
uxMaxCount, FREERTOS_ZERO_COPY | FREERTOS_MSG_DONTWAIT );
if( xRc > 0 )
{
ulRecvBytes += xRc;
xWritten = ff_fwrite( pcBuffer, 1, xRc, pxWriteHandle );
/* In the following call the received data is discarded: */
FreeRTOS_recv( xTransferSocket, ( void * ) NULL, xRc, 0 );
/* The above line shows the answer to your question. */
|
As you see, FreeRTOS_recv()
is called two times:
- With a pointer to a buffer pointer and the maximum number of bytes to receive.
- With NULL and the actual number of bytes received.
Note that in this example the streambuffer is passed directly to the disk driver when calling ff_fwrite()
.
This method works well, but you really have to know what you are doing, it is non-standard and meant to be used for advanced users. That is why there was no formal documentation about it, except in some posts on the forum.
Sending TCP data without copying:
FreeRTOS_send()
can also be used in a zero-copy way, and it is a bit more complex:
#define SECTOR_LENGTH 512U
BaseType_t xBufferLength;
/* FreeRTOS_get_tx_head() returns a pointer to the TX stream and
* sets xBufferLength to know how much space there is left for writing. */
pcBuffer = ( char * ) FreeRTOS_get_tx_head( pxClient->xTransferSocket, &xBufferLength );
if( ( pcBuffer != NULL ) && ( xBufferLength >= SECTOR_LENGTH ) )
{
/* Will read disk data directly to the TX stream of the socket. */
uxCount = FreeRTOS_min_uint32( uxCount, ( uint32_t ) xBufferLength );
}
else
{
/* Fall-back to the normal file i/o buffer. */
pcBuffer = pcFILE_BUFFER;
uxCount = sizeof pcFILE_BUFFER;
}
if( uxCount > 0U )
{
/* Read at most 'uxCount' bytes from disk. */
uxItemsRead = ff_fread( pcBuffer, 1, uxCount, pxClient->pxReadHandle );
if( pcBuffer != pcFILE_BUFFER )
{
/* When FreeRTOS_send gets a NULL pointer, it assumes that the
* transmission stream buffer already contains the data to be sent,
* in other words, a memcopy() is skipped. */
pcBuffer = NULL;
}
/* When zero-copy is used, pcBuffer will be NULL. The call is necessary
* to advance the head pointer in the TX stream buffer. */
xRc = FreeRTOS_send( pxClient->xTransferSocket, pcBuffer, uxCount, 0 );
}
In other words, for sending zero-copy there are two comparable steps:
-
FreeRTOS_get_tx_head()
will return a pointer to the TX stream buffer, tell how many bytes can be written at that address.
-
FreeRTOS_send()
is called with NULL and the number of bytes that have been written to the stream buffer.
I don’t mind to add documentation about “Using TCP with zero-copy methods”, but the text will start with a warning: please understand what you are doing.
I hope it is all clear now.
Hein