/* * FreeRTOS V202212.00 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of * the Software, and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * https://www.FreeRTOS.org * https://github.com/FreeRTOS * */ /* * FreeRTOS tasks are used with FreeRTOS+TCP to create a TCP echo server on the * standard echo port number (7). * * See the following web page for essential demo usage and configuration * details: * https://www.FreeRTOS.org/FreeRTOS-Plus/FreeRTOS_Plus_TCP/TCP_Echo_Server.html */ /* Standard includes. */ #include #include /* FreeRTOS includes. */ #include "FreeRTOS.h" #include "task.h" #include "semphr.h" /* FreeRTOS+TCP includes. */ #include "FreeRTOS_IP.h" #include "FreeRTOS_Sockets.h" /* Remove the whole file if FreeRTOSIPConfig.h is set to exclude TCP. */ #if( ipconfigUSE_TCP == 1 ) /* The maximum time to wait for a closing socket to close. */ #define tcpechoSHUTDOWN_DELAY ( pdMS_TO_TICKS( 5000 ) ) /* The standard echo port number. */ #define tcpechoPORT_NUMBER 7 /* If ipconfigUSE_TCP_WIN is 1 then the Tx sockets will use a buffer size set by ipconfigTCP_TX_BUFFER_LENGTH, and the Tx window size will be configECHO_SERVER_TX_WINDOW_SIZE times the buffer size. Note ipconfigTCP_TX_BUFFER_LENGTH is set in FreeRTOSIPConfig.h as it is a standard TCP/IP stack constant, whereas configECHO_SERVER_TX_WINDOW_SIZE is set in FreeRTOSConfig.h as it is a demo application constant. */ #ifndef configECHO_SERVER_TX_WINDOW_SIZE #define configECHO_SERVER_TX_WINDOW_SIZE 2 #endif /* If ipconfigUSE_TCP_WIN is 1 then the Rx sockets will use a buffer size set by ipconfigTCP_RX_BUFFER_LENGTH, and the Rx window size will be configECHO_SERVER_RX_WINDOW_SIZE times the buffer size. Note ipconfigTCP_RX_BUFFER_LENGTH is set in FreeRTOSIPConfig.h as it is a standard TCP/IP stack constant, whereas configECHO_SERVER_RX_WINDOW_SIZE is set in FreeRTOSConfig.h as it is a demo application constant. */ #ifndef configECHO_SERVER_RX_WINDOW_SIZE #define configECHO_SERVER_RX_WINDOW_SIZE 2 #endif /*-----------------------------------------------------------*/ /* * Uses FreeRTOS+TCP to listen for incoming echo connections, creating a task * to handle each connection. */ static void prvConnectionListeningTask( void *pvParameters ); /* * Created by the connection listening task to handle a single connection. */ static void prvServerConnectionInstance( void *pvParameters ); /*-----------------------------------------------------------*/ /* Stores the stack size passed into vStartSimpleTCPServerTasks() so it can be reused when the server listening task creates tasks to handle connections. */ static uint16_t usUsedStackSize = 0; /*-----------------------------------------------------------*/ void vStartSimpleTCPServerTasks( uint16_t usStackSize, UBaseType_t uxPriority ) { /* Create the TCP echo server. */ xTaskCreate( prvConnectionListeningTask, "ServerListener", usStackSize, NULL, uxPriority + 1, NULL ); /* Remember the requested stack size so it can be re-used by the server listening task when it creates tasks to handle connections. */ usUsedStackSize = usStackSize; } /*-----------------------------------------------------------*/ static void prvConnectionListeningTask( void *pvParameters ) { struct freertos_sockaddr xClient, xBindAddress; Socket_t xListeningSocket, xConnectedSocket; socklen_t xSize = sizeof( xClient ); static const TickType_t xReceiveTimeOut = portMAX_DELAY; const BaseType_t xBacklog = 20; #if( ipconfigUSE_TCP_WIN == 1 ) WinProperties_t xWinProps; /* Fill in the buffer and window sizes that will be used by the socket. */ xWinProps.lTxBufSize = ipconfigTCP_TX_BUFFER_LENGTH; xWinProps.lTxWinSize = configECHO_SERVER_TX_WINDOW_SIZE; xWinProps.lRxBufSize = ipconfigTCP_RX_BUFFER_LENGTH; xWinProps.lRxWinSize = configECHO_SERVER_RX_WINDOW_SIZE; #endif /* ipconfigUSE_TCP_WIN */ /* Just to prevent compiler warnings. */ ( void ) pvParameters; /* Attempt to open the socket. */ xListeningSocket = FreeRTOS_socket( FREERTOS_AF_INET, FREERTOS_SOCK_STREAM, FREERTOS_IPPROTO_TCP ); configASSERT( xListeningSocket != FREERTOS_INVALID_SOCKET ); /* Set a time out so accept() will just wait for a connection. */ FreeRTOS_setsockopt( xListeningSocket, 0, FREERTOS_SO_RCVTIMEO, &xReceiveTimeOut, sizeof( xReceiveTimeOut ) ); /* Set the window and buffer sizes. */ #if( ipconfigUSE_TCP_WIN == 1 ) { FreeRTOS_setsockopt( xListeningSocket, 0, FREERTOS_SO_WIN_PROPERTIES, ( void * ) &xWinProps, sizeof( xWinProps ) ); } #endif /* ipconfigUSE_TCP_WIN */ /* Bind the socket to the port that the client task will send to, then listen for incoming connections. */ xBindAddress.sin_port = tcpechoPORT_NUMBER; xBindAddress.sin_port = FreeRTOS_htons( xBindAddress.sin_port ); xBindAddress.sin_family = FREERTOS_AF_INET; FreeRTOS_bind( xListeningSocket, &xBindAddress, sizeof( xBindAddress ) ); FreeRTOS_listen( xListeningSocket, xBacklog ); for( ;; ) { /* Wait for a client to connect. */ xConnectedSocket = FreeRTOS_accept( xListeningSocket, &xClient, &xSize ); configASSERT( xConnectedSocket != FREERTOS_INVALID_SOCKET ); /* Spawn a task to handle the connection. */ xTaskCreate( prvServerConnectionInstance, "EchoServer", usUsedStackSize, ( void * ) xConnectedSocket, tskIDLE_PRIORITY, NULL ); } } /*-----------------------------------------------------------*/ static void prvServerConnectionInstance( void *pvParameters ) { int32_t lBytes, lSent, lTotalSent; Socket_t xConnectedSocket; static const TickType_t xReceiveTimeOut = pdMS_TO_TICKS( 5000 ); static const TickType_t xSendTimeOut = pdMS_TO_TICKS( 5000 ); TickType_t xTimeOnShutdown; uint8_t *pucRxBuffer; xConnectedSocket = ( Socket_t ) pvParameters; /* Attempt to create the buffer used to receive the string to be echoed back. This could be avoided using a zero copy interface that just returned the same buffer. */ pucRxBuffer = ( uint8_t * ) pvPortMalloc( ipconfigTCP_MSS ); if( pucRxBuffer != NULL ) { FreeRTOS_setsockopt( xConnectedSocket, 0, FREERTOS_SO_RCVTIMEO, &xReceiveTimeOut, sizeof( xReceiveTimeOut ) ); FreeRTOS_setsockopt( xConnectedSocket, 0, FREERTOS_SO_SNDTIMEO, &xSendTimeOut, sizeof( xReceiveTimeOut ) ); for( ;; ) { /* Zero out the receive array so there is NULL at the end of the string when it is printed out. */ memset( pucRxBuffer, 0x00, ipconfigTCP_MSS ); /* Receive data on the socket. */ lBytes = FreeRTOS_recv( xConnectedSocket, pucRxBuffer, ipconfigTCP_MSS, 0 ); /* If data was received, echo it back. */ if( lBytes >= 0 ) { lSent = 0; lTotalSent = 0; /* Call send() until all the data has been sent. */ while( ( lSent >= 0 ) && ( lTotalSent < lBytes ) ) { lSent = FreeRTOS_send( xConnectedSocket, pucRxBuffer, lBytes - lTotalSent, 0 ); lTotalSent += lSent; } if( lSent < 0 ) { /* Socket closed? */ break; } } else { /* Socket closed? */ break; } } } /* Initiate a shutdown in case it has not already been initiated. */ FreeRTOS_shutdown( xConnectedSocket, FREERTOS_SHUT_RDWR ); /* Wait for the shutdown to take effect, indicated by FreeRTOS_recv() returning an error. */ xTimeOnShutdown = xTaskGetTickCount(); do { if( FreeRTOS_recv( xConnectedSocket, pucRxBuffer, ipconfigTCP_MSS, 0 ) < 0 ) { break; } } while( ( xTaskGetTickCount() - xTimeOnShutdown ) < tcpechoSHUTDOWN_DELAY ); /* Finished with the socket, buffer, the task. */ vPortFree( pucRxBuffer ); FreeRTOS_closesocket( xConnectedSocket ); vTaskDelete( NULL ); } /*-----------------------------------------------------------*/ /* The whole file is excluded if TCP is not compiled in. */ #endif /* ipconfigUSE_TCP */