echo.c
Go to the documentation of this file.
1 /**
2  * @file echo.c
3  * @brief Echo protocol
4  *
5  * @section License
6  *
7  * Copyright (C) 2010-2018 Oryx Embedded SARL. All rights reserved.
8  *
9  * This file is part of CycloneTCP Open.
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software Foundation,
23  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24  *
25  * @section Description
26  *
27  * The echo service simply sends back to the originating source
28  * any data it receives. Refer to RFC 862 for complete details
29  *
30  * @author Oryx Embedded SARL (www.oryx-embedded.com)
31  * @version 1.9.0
32  **/
33 
34 //Switch to the appropriate trace level
35 #define TRACE_LEVEL STD_SERVICES_TRACE_LEVEL
36 
37 //Dependencies
38 #include "core/net.h"
39 #include "std_services/echo.h"
40 #include "debug.h"
41 
42 //Check TCP/IP stack configuration
43 #if (NET_STATIC_OS_RESOURCES == ENABLED)
44 
45 //UDP Echo service
46 static OsTask udpEchoTaskStruct;
47 static uint_t udpEchoTaskStack[ECHO_SERVICE_STACK_SIZE];
48 
49 #endif
50 
51 
52 /**
53  * @brief Start TCP echo service
54  * @return Error code
55  **/
56 
58 {
59  error_t error;
60  Socket *socket;
61  OsTask *task;
62 
63  //Debug message
64  TRACE_INFO("Starting TCP echo service...\r\n");
65 
66  //Open a TCP socket
68  //Failed to open socket?
69  if(socket == NULL)
70  return ERROR_OPEN_FAILED;
71 
72  //Start of exception handling block
73  do
74  {
75  //Bind the newly created socket to port 7
77  //Failed to bind the socket to the desired port?
78  if(error)
79  break;
80 
81  //Place the socket into listening mode
82  error = socketListen(socket, 0);
83  //Any error to report?
84  if(error)
85  break;
86 
87  //Create a task to handle incoming connection requests
88  task = osCreateTask("TCP Echo Listener", tcpEchoListenerTask,
90 
91  //Unable to create the task?
92  if(task == OS_INVALID_HANDLE)
93  {
94  //Report an error to the calling function
95  error = ERROR_OUT_OF_RESOURCES;
96  break;
97  }
98 
99  //End of exception handling block
100  } while(0);
101 
102  //Any error to report?
103  if(error)
104  {
105  //Clean up side effects...
107  }
108 
109  //Return status code
110  return error;
111 }
112 
113 
114 /**
115  * @brief Task handling connection requests
116  * @param[in] param Pointer to the echo service context
117  **/
118 
119 void tcpEchoListenerTask(void *param)
120 {
121  error_t error;
122  uint16_t clientPort;
123  IpAddr clientIpAddr;
124  Socket *serverSocket;
125  Socket *clientSocket;
126  EchoServiceContext *context;
127  OsTask *task;
128 
129  //Point to the listening socket
130  serverSocket = (Socket *) param;
131 
132  //Main loop
133  while(1)
134  {
135  //Accept an incoming connection
136  clientSocket = socketAccept(serverSocket, &clientIpAddr, &clientPort);
137  //Check whether a valid connection request has been received
138  if(!clientSocket)
139  continue;
140 
141  //Debug message
142  TRACE_INFO("Echo service: connection established with client %s port %" PRIu16 "\r\n",
143  ipAddrToString(&clientIpAddr, NULL), clientPort);
144 
145  //The socket operates in non-blocking mode
146  error = socketSetTimeout(clientSocket, 0);
147 
148  //Any error to report?
149  if(error)
150  {
151  //Close socket
152  socketClose(clientSocket);
153  //Wait for an incoming connection attempt
154  continue;
155  }
156 
157  //Allocate resources for the new connection
158  context = osAllocMem(sizeof(EchoServiceContext));
159 
160  //Failed to allocate memory?
161  if(context == NULL)
162  {
163  //Close socket
164  socketClose(clientSocket);
165  //Wait for an incoming connection attempt
166  continue;
167  }
168 
169  //Record the handle of the newly created socket
170  context->socket = clientSocket;
171 
172  //Create a task to service the current connection
173  task = osCreateTask("TCP Echo Connection", tcpEchoConnectionTask,
175 
176  //Did we encounter an error?
177  if(task == OS_INVALID_HANDLE)
178  {
179  //Close socket
180  socketClose(clientSocket);
181  //Release resources
182  osFreeMem(context);
183  }
184  }
185 }
186 
187 
188 /**
189  * @brief TCP echo service implementation
190  * @param[in] param Pointer to the echo service context
191  **/
192 
193 void tcpEchoConnectionTask(void *param)
194 {
195  error_t error;
196  size_t n;
197  size_t writeIndex;
198  size_t readIndex;
199  size_t bufferLength;
200  size_t rxByteCount;
201  size_t txByteCount;
202  systime_t startTime;
204  SocketEventDesc eventDesc;
205  EchoServiceContext *context;
206 
207  //Get a pointer to the context
208  context = (EchoServiceContext *) param;
209  //Get current time
210  startTime = osGetSystemTime();
211 
212  //Initialize variables
213  writeIndex = 0;
214  readIndex = 0;
215  bufferLength = 0;
216  rxByteCount = 0;
217  txByteCount = 0;
218 
219  //Main loop
220  while(1)
221  {
222  //Buffer is empty?
223  if(!bufferLength)
224  {
225  //Get notified when the socket is readable
226  eventDesc.socket = context->socket;
227  eventDesc.eventMask = SOCKET_EVENT_RX_READY;
228  }
229  //Buffer is not empty of full?
230  else if(bufferLength < ECHO_BUFFER_SIZE)
231  {
232  //Get notified when the socket is readable or writable
233  eventDesc.socket = context->socket;
235  }
236  //Buffer is full?
237  else
238  {
239  //Get notified when the socket is writable
240  eventDesc.socket = context->socket;
241  eventDesc.eventMask = SOCKET_EVENT_TX_READY;
242  }
243 
244  //Wait for an event to be fired
245  error = socketPoll(&eventDesc, 1, NULL, ECHO_TIMEOUT);
246  //Timeout error or any other exception to report?
247  if(error)
248  break;
249 
250  //The socket is available for reading
251  if(eventDesc.eventFlags & SOCKET_EVENT_RX_READY)
252  {
253  //Read as much data as possible
254  n = MIN(ECHO_BUFFER_SIZE - writeIndex, ECHO_BUFFER_SIZE - bufferLength);
255 
256  //Read incoming data
257  error = socketReceive(context->socket, context->buffer + writeIndex, n, &n, 0);
258  //Any error to report?
259  if(error)
260  break;
261 
262  //Increment write index
263  writeIndex += n;
264  //Wrap around if necessary
265  if(writeIndex >= ECHO_BUFFER_SIZE)
266  writeIndex = 0;
267 
268  //Increment buffer length
269  bufferLength += n;
270  //Total number of bytes received
271  rxByteCount += n;
272  }
273 
274  //The socket is available for writing?
275  if(eventDesc.eventFlags & SOCKET_EVENT_TX_READY)
276  {
277  //Write as much data as possible
278  n = MIN(ECHO_BUFFER_SIZE - readIndex, bufferLength);
279 
280  //Send data back to the client
281  error = socketSend(context->socket, context->buffer + readIndex, n, &n, 0);
282  //Any error to report?
283  if(error && error != ERROR_TIMEOUT)
284  break;
285 
286  //Increment read index
287  readIndex += n;
288  //Wrap around if necessary
289  if(readIndex >= ECHO_BUFFER_SIZE)
290  readIndex = 0;
291 
292  //Update buffer length
293  bufferLength -= n;
294  //Total number of bytes sent
295  txByteCount += n;
296  }
297  }
298 
299  //Adjust timeout value
301  //Graceful shutdown
303  //Compute total duration
304  duration = osGetSystemTime() - startTime;
305 
306  //Debug message
307  TRACE_INFO("Echo service: %" PRIuSIZE " bytes received, %" PRIuSIZE " bytes sent in %" PRIu32 " ms\r\n",
308  rxByteCount, txByteCount, duration);
309 
310  //Close socket
311  socketClose(context->socket);
312  //Release previously allocated memory
313  osFreeMem(context);
314 
315  //Kill ourselves
316  osDeleteTask(NULL);
317 }
318 
319 
320 /**
321  * @brief Start UDP echo service
322  * @return Error code
323  **/
324 
326 {
327  error_t error;
328  EchoServiceContext *context;
329 
330 #if (NET_STATIC_OS_RESOURCES == DISABLED)
331  OsTask *task;
332 #endif
333 
334  //Debug message
335  TRACE_INFO("Starting UDP echo service...\r\n");
336 
337  //Allocate a memory block to hold the context
338  context = osAllocMem(sizeof(EchoServiceContext));
339  //Failed to allocate memory?
340  if(context == NULL)
341  return ERROR_OUT_OF_MEMORY;
342 
343  //Start of exception handling block
344  do
345  {
346  //Open a UDP socket
348 
349  //Failed to open socket?
350  if(context->socket == NULL)
351  {
352  //Report an error
353  error = ERROR_OPEN_FAILED;
354  //Exit immediately
355  break;
356  }
357 
358  //The server listens for incoming datagrams on port 7
359  error = socketBind(context->socket, &IP_ADDR_ANY, ECHO_PORT);
360  //Unable to bind the socket to the desired port?
361  if(error)
362  break;
363 
364 #if (NET_STATIC_OS_RESOURCES == ENABLED)
365  //Create a task to handle incoming datagrams
366  osCreateStaticTask(&udpEchoTaskStruct, "UDP Echo", udpEchoTask, context,
368 #else
369  //Create a task to handle incoming datagrams
370  task = osCreateTask("UDP Echo", udpEchoTask,
372 
373  //Unable to create the task?
374  if(task == OS_INVALID_HANDLE)
375  {
376  //Report an error to the calling function
377  error = ERROR_OUT_OF_RESOURCES;
378  break;
379  }
380 #endif
381 
382  //End of exception handling block
383  } while(0);
384 
385  //Any error to report?
386  if(error)
387  {
388  //Clean up side effects...
389  socketClose(context->socket);
390  osFreeMem(context);
391  }
392 
393  //Return status code
394  return error;
395 }
396 
397 
398 /**
399  * @brief UDP echo service implementation
400  * @param[in] param Pointer to the echo service context
401  **/
402 
403 void udpEchoTask(void *param)
404 {
405  error_t error;
406  size_t length;
407  uint16_t port;
408  IpAddr ipAddr;
409  EchoServiceContext *context;
410 
411  //Get a pointer to the context
412  context = (EchoServiceContext *) param;
413 
414  //Main loop
415  while(1)
416  {
417  //Wait for an incoming datagram
418  error = socketReceiveFrom(context->socket, &ipAddr, &port,
419  context->buffer, ECHO_BUFFER_SIZE, &length, 0);
420 
421  //Any datagram received?
422  if(!error)
423  {
424  //Debug message
425  TRACE_INFO("Echo service: %" PRIuSIZE " bytes received from %s port %" PRIu16 "\r\n",
426  length, ipAddrToString(&ipAddr, NULL), port);
427 
428  //Send the data back to the source
429  error = socketSendTo(context->socket, &ipAddr, port,
430  context->buffer, length, NULL, 0);
431  }
432  }
433 }
#define ECHO_TIMEOUT
Definition: echo.h:57
uint32_t systime_t
Definition: compiler_port.h:44
error_t socketReceive(Socket *socket, void *data, size_t size, size_t *received, uint_t flags)
Receive data from a connected socket.
Definition: socket.c:584
Socket * socket
Handle to a socket to monitor.
Definition: socket.h:305
systime_t osGetSystemTime(void)
Retrieve system time.
void osFreeMem(void *p)
Release a previously allocated memory block.
TCP/IP stack core.
bool_t osCreateStaticTask(OsTask *task, const char_t *name, OsTaskCode taskCode, void *param, void *stack, size_t stackSize, int_t priority)
Create a static task.
Debugging facilities.
OsTask * osCreateTask(const char_t *name, OsTaskCode taskCode, void *param, size_t stackSize, int_t priority)
Create a new task.
Socket * socketAccept(Socket *socket, IpAddr *clientIpAddr, uint16_t *clientPort)
Permit an incoming connection attempt on a socket.
Definition: socket.c:457
error_t socketListen(Socket *socket, uint_t backlog)
Place a socket in the listening state.
Definition: socket.c:420
char_t buffer[ECHO_BUFFER_SIZE]
Definition: echo.h:78
IP network address.
Definition: ip.h:57
void socketClose(Socket *socket)
Close an existing socket.
Definition: socket.c:797
char_t * ipAddrToString(const IpAddr *ipAddr, char_t *str)
Convert a binary IP address to a string representation.
Definition: ip.c:685
void tcpEchoConnectionTask(void *param)
TCP echo service implementation.
Definition: echo.c:193
const IpAddr IP_ADDR_ANY
Definition: ip.c:42
uint_t eventFlags
Returned events.
Definition: socket.h:307
uint16_t duration
#define ECHO_SERVICE_STACK_SIZE
Definition: echo.h:38
error_t socketReceiveFrom(Socket *socket, IpAddr *srcIpAddr, uint16_t *srcPort, void *data, size_t size, size_t *received, uint_t flags)
Receive a datagram from a connectionless socket.
Definition: socket.c:604
#define ECHO_BUFFER_SIZE
Definition: echo.h:50
#define ECHO_SERVICE_PRIORITY
Definition: echo.h:45
int_t socket(int_t family, int_t type, int_t protocol)
Create a socket that is bound to a specific transport service provider.
Definition: bsd_socket.c:106
error_t socketPoll(SocketEventDesc *eventDesc, uint_t size, OsEvent *extEvent, systime_t timeout)
Wait for one of a set of sockets to become ready to perform I/O.
Definition: socket.c:857
uint8_t ipAddr[4]
Definition: mib_common.h:185
error_t socketSetTimeout(Socket *socket, systime_t timeout)
Set timeout value for blocking operations.
Definition: socket.c:216
#define Socket
Definition: socket.h:34
Socket * socket
Definition: echo.h:77
uint_t eventMask
Requested events.
Definition: socket.h:306
Task object.
void udpEchoTask(void *param)
UDP echo service implementation.
Definition: echo.c:403
#define ECHO_PORT
Definition: echo.h:63
error_t udpEchoStart(void)
Start UDP echo service.
Definition: echo.c:325
#define MIN(a, b)
Definition: os_port.h:60
Echo service context.
Definition: echo.h:75
error_t socketShutdown(Socket *socket, uint_t how)
Disable reception, transmission, or both.
Definition: socket.c:760
Structure describing socket events.
Definition: socket.h:303
#define TRACE_INFO(...)
Definition: debug.h:86
error_t tcpEchoStart(void)
Start TCP echo service.
Definition: echo.c:57
void * osAllocMem(size_t size)
Allocate a memory block.
error_t
Error codes.
Definition: error.h:40
unsigned int uint_t
Definition: compiler_port.h:43
Socket * socketOpen(uint_t type, uint_t protocol)
Create a socket (UDP or TCP)
Definition: socket.c:92
#define PRIuSIZE
Definition: compiler_port.h:72
void osDeleteTask(OsTask *task)
Delete a task.
uint16_t port
Definition: dns_common.h:221
Echo protocol.
#define OS_INVALID_HANDLE
Definition: os_port.h:77
error_t socketBind(Socket *socket, const IpAddr *localIpAddr, uint16_t localPort)
Associate a local address with a socket.
Definition: socket.c:331
uint8_t length
Definition: dtls_misc.h:140
uint8_t n
error_t socketSendTo(Socket *socket, const IpAddr *destIpAddr, uint16_t destPort, const void *data, size_t length, size_t *written, uint_t flags)
Send a datagram to a specific destination.
Definition: socket.c:511
error_t socketSend(Socket *socket, const void *data, size_t length, size_t *written, uint_t flags)
Send data to a connected socket.
Definition: socket.c:490
void tcpEchoListenerTask(void *param)
Task handling connection requests.
Definition: echo.c:119