Sergey Pastor / 1

Dependents:   Nucleo

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers echo.c Source File

echo.c

Go to the documentation of this file.
00001 /**
00002  * @file echo.c
00003  * @brief Echo protocol
00004  *
00005  * @section License
00006  *
00007  * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
00008  *
00009  * This file is part of CycloneTCP Open.
00010  *
00011  * This program is free software; you can redistribute it and/or
00012  * modify it under the terms of the GNU General Public License
00013  * as published by the Free Software Foundation; either version 2
00014  * of the License, or (at your option) any later version.
00015  *
00016  * This program is distributed in the hope that it will be useful,
00017  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  * GNU General Public License for more details.
00020  *
00021  * You should have received a copy of the GNU General Public License
00022  * along with this program; if not, write to the Free Software Foundation,
00023  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00024  *
00025  * @section Description
00026  *
00027  * The echo service simply sends back to the originating source
00028  * any data it receives. Refer to RFC 862 for complete details
00029  *
00030  * @author Oryx Embedded SARL (www.oryx-embedded.com)
00031  * @version 1.7.6
00032  **/
00033 
00034 //Switch to the appropriate trace level
00035 #define TRACE_LEVEL STD_SERVICES_TRACE_LEVEL
00036 
00037 //Dependencies
00038 #include "core/net.h"
00039 #include "std_services/echo.h"
00040 #include "debug.h"
00041 
00042 //Check TCP/IP stack configuration
00043 #if (NET_STATIC_OS_RESOURCES == ENABLED)
00044 
00045 //UDP Echo service
00046 static OsTask udpEchoTaskStruct;
00047 static uint_t udpEchoTaskStack[ECHO_SERVICE_STACK_SIZE];
00048 
00049 #endif
00050 
00051 
00052 /**
00053  * @brief Start TCP echo service
00054  * @return Error code
00055  **/
00056 
00057 error_t tcpEchoStart(void)
00058 {
00059    error_t error;
00060    Socket *socket;
00061    OsTask *task;
00062 
00063    //Debug message
00064    TRACE_INFO("Starting TCP echo service...\r\n");
00065 
00066    //Open a TCP socket
00067    socket = socketOpen(SOCKET_TYPE_STREAM, SOCKET_IP_PROTO_TCP);
00068    //Failed to open socket?
00069    if(socket == NULL)
00070       return ERROR_OPEN_FAILED;
00071 
00072    //Start of exception handling block
00073    do
00074    {
00075       //Bind the newly created socket to port 7
00076       error = socketBind(socket, &IP_ADDR_ANY, ECHO_PORT);
00077       //Failed to bind the socket to the desired port?
00078       if(error)
00079          break;
00080 
00081       //Place the socket into listening mode
00082       error = socketListen(socket, 0);
00083       //Any error to report?
00084       if(error)
00085          break;
00086 
00087       //Create a task to handle incoming connection requests
00088       task = osCreateTask("TCP Echo Listener", tcpEchoListenerTask,
00089          socket, ECHO_SERVICE_STACK_SIZE, ECHO_SERVICE_PRIORITY);
00090 
00091       //Unable to create the task?
00092       if(task == OS_INVALID_HANDLE)
00093       {
00094          //Report an error to the calling function
00095          error = ERROR_OUT_OF_RESOURCES;
00096          break;
00097       }
00098 
00099       //End of exception handling block
00100    } while(0);
00101 
00102    //Any error to report?
00103    if(error)
00104    {
00105       //Clean up side effects...
00106       socketClose(socket);
00107    }
00108 
00109    //Return status code
00110    return error;
00111 }
00112 
00113 
00114 /**
00115  * @brief Task handling connection requests
00116  * @param[in] param Pointer to the echo service context
00117  **/
00118 
00119 void tcpEchoListenerTask(void *param)
00120 {
00121    error_t error;
00122    uint16_t clientPort;
00123    IpAddr clientIpAddr;
00124    Socket *serverSocket;
00125    Socket *clientSocket;
00126    EchoServiceContext *context;
00127    OsTask *task;
00128 
00129    //Point to the listening socket
00130    serverSocket = (Socket *) param;
00131 
00132    //Main loop
00133    while(1)
00134    {
00135       //Accept an incoming connection
00136       clientSocket = socketAccept(serverSocket, &clientIpAddr, &clientPort);
00137       //Check whether a valid connection request has been received
00138       if(!clientSocket) continue;
00139 
00140       //Debug message
00141       TRACE_INFO("Echo service: connection established with client %s port %" PRIu16 "\r\n",
00142          ipAddrToString(&clientIpAddr, NULL), clientPort);
00143 
00144       //The socket operates in non-blocking mode
00145       error = socketSetTimeout(clientSocket, 0);
00146 
00147       //Any error to report?
00148       if(error)
00149       {
00150          //Close socket
00151          socketClose(clientSocket);
00152          //Wait for an incoming connection attempt
00153          continue;
00154       }
00155 
00156       //Allocate resources for the new connection
00157       context = osAllocMem(sizeof(EchoServiceContext));
00158 
00159       //Failed to allocate memory?
00160       if(context == NULL)
00161       {
00162          //Close socket
00163          socketClose(clientSocket);
00164          //Wait for an incoming connection attempt
00165          continue;
00166       }
00167 
00168       //Record the handle of the newly created socket
00169       context->socket = clientSocket;
00170 
00171       //Create a task to service the current connection
00172       task = osCreateTask("TCP Echo Connection", tcpEchoConnectionTask,
00173          context, ECHO_SERVICE_STACK_SIZE, ECHO_SERVICE_PRIORITY);
00174 
00175       //Did we encounter an error?
00176       if(task == OS_INVALID_HANDLE)
00177       {
00178          //Close socket
00179          socketClose(clientSocket);
00180          //Release resources
00181          osFreeMem(context);
00182       }
00183    }
00184 }
00185 
00186 
00187 /**
00188  * @brief TCP echo service implementation
00189  * @param[in] param Pointer to the echo service context
00190  **/
00191 
00192 void tcpEchoConnectionTask(void *param)
00193 {
00194    error_t error;
00195    size_t n;
00196    size_t writeIndex;
00197    size_t readIndex;
00198    size_t bufferLength;
00199    size_t rxByteCount;
00200    size_t txByteCount;
00201    systime_t startTime;
00202    systime_t duration;
00203    SocketEventDesc eventDesc;
00204    EchoServiceContext *context;
00205 
00206    //Get a pointer to the context
00207    context = (EchoServiceContext *) param;
00208    //Get current time
00209    startTime = osGetSystemTime();
00210 
00211    //Initialize variables
00212    writeIndex = 0;
00213    readIndex = 0;
00214    bufferLength = 0;
00215    rxByteCount = 0;
00216    txByteCount = 0;
00217 
00218    //Main loop
00219    while(1)
00220    {
00221       //Buffer is empty?
00222       if(!bufferLength)
00223       {
00224          //Get notified when the socket is readable
00225          eventDesc.socket = context->socket;
00226          eventDesc.eventMask = SOCKET_EVENT_RX_READY;
00227       }
00228       //Buffer is not empty of full?
00229       else if(bufferLength < ECHO_BUFFER_SIZE)
00230       {
00231          //Get notified when the socket is readable or writable
00232          eventDesc.socket = context->socket;
00233          eventDesc.eventMask = SOCKET_EVENT_RX_READY | SOCKET_EVENT_TX_READY;
00234       }
00235       //Buffer is full?
00236       else
00237       {
00238          //Get notified when the socket is writable
00239          eventDesc.socket = context->socket;
00240          eventDesc.eventMask = SOCKET_EVENT_TX_READY;
00241       }
00242 
00243       //Wait for an event to be fired
00244       error = socketPoll(&eventDesc, 1, NULL, ECHO_TIMEOUT);
00245       //Timeout error or any other exception to report?
00246       if(error)
00247          break;
00248 
00249       //The socket is available for reading
00250       if(eventDesc.eventFlags & SOCKET_EVENT_RX_READY)
00251       {
00252          //Read as much data as possible
00253          n = MIN(ECHO_BUFFER_SIZE - writeIndex, ECHO_BUFFER_SIZE - bufferLength);
00254 
00255          //Read incoming data
00256          error = socketReceive(context->socket, context->buffer + writeIndex, n, &n, 0);
00257          //Any error to report?
00258          if(error)
00259             break;
00260 
00261          //Increment write index
00262          writeIndex += n;
00263          //Wrap around if necessary
00264          if(writeIndex >= ECHO_BUFFER_SIZE)
00265             writeIndex = 0;
00266 
00267          //Increment buffer length
00268          bufferLength += n;
00269          //Total number of bytes received
00270          rxByteCount += n;
00271       }
00272 
00273       //The socket is available for writing?
00274       if(eventDesc.eventFlags & SOCKET_EVENT_TX_READY)
00275       {
00276          //Write as much data as possible
00277          n = MIN(ECHO_BUFFER_SIZE - readIndex, bufferLength);
00278 
00279          //Send data back to the client
00280          error = socketSend(context->socket, context->buffer + readIndex, n, &n, 0);
00281          //Any error to report?
00282          if(error && error != ERROR_TIMEOUT)
00283             break;
00284 
00285          //Increment read index
00286          readIndex += n;
00287          //Wrap around if necessary
00288          if(readIndex >= ECHO_BUFFER_SIZE)
00289             readIndex = 0;
00290 
00291          //Update buffer length
00292          bufferLength -= n;
00293          //Total number of bytes sent
00294          txByteCount += n;
00295       }
00296    }
00297 
00298    //Adjust timeout value
00299    socketSetTimeout(context->socket, ECHO_TIMEOUT);
00300    //Graceful shutdown
00301    socketShutdown(context->socket, SOCKET_SD_BOTH);
00302    //Compute total duration
00303    duration = osGetSystemTime() - startTime;
00304 
00305    //Debug message
00306    TRACE_INFO("Echo service: %" PRIuSIZE " bytes received, %" PRIuSIZE " bytes sent in %" PRIu32 " ms\r\n",
00307       rxByteCount, txByteCount, duration);
00308 
00309    //Close socket
00310    socketClose(context->socket);
00311    //Release previously allocated memory
00312    osFreeMem(context);
00313 
00314    //Kill ourselves
00315    osDeleteTask(NULL);
00316 }
00317 
00318 
00319 /**
00320  * @brief Start UDP echo service
00321  * @return Error code
00322  **/
00323 
00324 error_t udpEchoStart(void)
00325 {
00326    error_t error;
00327    EchoServiceContext *context;
00328 
00329 #if (NET_STATIC_OS_RESOURCES == DISABLED)
00330    OsTask *task;
00331 #endif
00332 
00333    //Debug message
00334    TRACE_INFO("Starting UDP echo service...\r\n");
00335 
00336    //Allocate a memory block to hold the context
00337    context = osAllocMem(sizeof(EchoServiceContext));
00338    //Failed to allocate memory?
00339    if(context == NULL)
00340       return ERROR_OUT_OF_MEMORY;
00341 
00342    //Start of exception handling block
00343    do
00344    {
00345       //Open a UDP socket
00346       context->socket = socketOpen(SOCKET_TYPE_DGRAM, SOCKET_IP_PROTO_UDP);
00347 
00348       //Failed to open socket?
00349       if(!context->socket)
00350       {
00351          //Report an error
00352          error = ERROR_OPEN_FAILED;
00353          //Exit immediately
00354          break;
00355       }
00356 
00357       //The server listens for incoming datagrams on port 7
00358       error = socketBind(context->socket, &IP_ADDR_ANY, ECHO_PORT);
00359       //Unable to bind the socket to the desired port?
00360       if(error)
00361          break;
00362 
00363 #if (NET_STATIC_OS_RESOURCES == ENABLED)
00364       //Create a task to handle incoming datagrams
00365       osCreateStaticTask(&udpEchoTaskStruct, "UDP Echo", udpEchoTask, context,
00366          udpEchoTaskStack, ECHO_SERVICE_STACK_SIZE, ECHO_SERVICE_PRIORITY);
00367 #else
00368       //Create a task to handle incoming datagrams
00369       task = osCreateTask("UDP Echo", udpEchoTask,
00370          context, ECHO_SERVICE_STACK_SIZE, ECHO_SERVICE_PRIORITY);
00371 
00372       //Unable to create the task?
00373       if(task == OS_INVALID_HANDLE)
00374       {
00375          //Report an error to the calling function
00376          error = ERROR_OUT_OF_RESOURCES;
00377          break;
00378       }
00379 #endif
00380 
00381       //End of exception handling block
00382    } while(0);
00383 
00384    //Any error to report?
00385    if(error)
00386    {
00387       //Clean up side effects...
00388       socketClose(context->socket);
00389       osFreeMem(context);
00390    }
00391 
00392    //Return status code
00393    return error;
00394 }
00395 
00396 
00397 /**
00398  * @brief UDP echo service implementation
00399  * @param[in] param Pointer to the echo service context
00400  **/
00401 
00402 void udpEchoTask(void *param)
00403 {
00404    error_t error;
00405    size_t length;
00406    uint16_t port;
00407    IpAddr ipAddr;
00408    EchoServiceContext *context;
00409 
00410    //Get a pointer to the context
00411    context = (EchoServiceContext *) param;
00412 
00413    //Main loop
00414    while(1)
00415    {
00416       //Wait for an incoming datagram
00417       error = socketReceiveFrom(context->socket, &ipAddr, &port,
00418          context->buffer, ECHO_BUFFER_SIZE, &length, 0);
00419 
00420       //Any datagram received?
00421       if(!error)
00422       {
00423          //Debug message
00424          TRACE_INFO("Echo service: %" PRIuSIZE " bytes received from %s port %" PRIu16 "\r\n",
00425             length, ipAddrToString(&ipAddr, NULL), port);
00426 
00427          //Send the data back to the source
00428          error = socketSendTo(context->socket, &ipAddr, port,
00429             context->buffer, length, NULL, 0);
00430       }
00431    }
00432 }
00433