echo_server.c
Go to the documentation of this file.
1 /**
2  * @file echo_server.c
3  * @brief Echo server
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2010-2024 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneTCP Open.
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26  *
27  * @section Description
28  *
29  * The Echo service simply sends back to the originating source
30  * any data it receives. Refer to RFC 862 for complete details
31  *
32  * @author Oryx Embedded SARL (www.oryx-embedded.com)
33  * @version 2.4.4
34  **/
35 
36 //Switch to the appropriate trace level
37 #define TRACE_LEVEL ECHO_TRACE_LEVEL
38 
39 //Dependencies
40 #include "core/net.h"
41 #include "echo/echo_server.h"
42 #include "echo/echo_server_misc.h"
43 #include "debug.h"
44 
45 //Check TCP/IP stack configuration
46 #if (ECHO_SERVER_SUPPORT == ENABLED)
47 
48 
49 /**
50  * @brief Initialize settings with default values
51  * @param[out] settings Structure that contains Echo server settings
52  **/
53 
55 {
56  //Default task parameters
57  settings->task = OS_TASK_DEFAULT_PARAMS;
60 
61  //The Echo server is not bound to any interface
62  settings->interface = NULL;
63 
64  //Echo service port number
65  settings->port = ECHO_PORT;
66 }
67 
68 
69 /**
70  * @brief Initialize Echo server context
71  * @param[in] context Pointer to the Echo server context
72  * @param[in] settings Echo server specific settings
73  * @return Error code
74  **/
75 
77  const EchoServerSettings *settings)
78 {
79  error_t error;
80 
81  //Debug message
82  TRACE_INFO("Initializing Echo server...\r\n");
83 
84  //Ensure the parameters are valid
85  if(context == NULL || settings == NULL)
87 
88  //Clear Echo server context
89  osMemset(context, 0, sizeof(EchoServerContext));
90 
91  //Initialize task parameters
92  context->taskParams = settings->task;
93  context->taskId = OS_INVALID_TASK_ID;
94 
95  //Save user settings
96  context->settings = *settings;
97 
98  //Initialize status code
99  error = NO_ERROR;
100 
101  //Create an event object to poll the state of sockets
102  if(!osCreateEvent(&context->event))
103  {
104  //Failed to create event
105  error = ERROR_OUT_OF_RESOURCES;
106  }
107 
108  //Any error to report?
109  if(error)
110  {
111  //Clean up side effects
112  echoServerDeinit(context);
113  }
114 
115  //Return status code
116  return error;
117 }
118 
119 
120 /**
121  * @brief Start Echo server
122  * @param[in] context Pointer to the Echo server context
123  * @return Error code
124  **/
125 
127 {
128  error_t error;
129 
130  //Make sure the Echo server context is valid
131  if(context == NULL)
133 
134  //Debug message
135  TRACE_INFO("Starting Echo server...\r\n");
136 
137  //Make sure the Echo server is not already running
138  if(context->running)
139  return ERROR_ALREADY_RUNNING;
140 
141  //Start of exception handling block
142  do
143  {
144 #if (ECHO_SERVER_TCP_SUPPORT == ENABLED)
145  //Open a TCP socket
147  //Failed to open socket?
148  if(context->tcpSocket == NULL)
149  {
150  //Report an error
151  error = ERROR_OPEN_FAILED;
152  break;
153  }
154 
155  //Force the socket to operate in non-blocking mode
156  error = socketSetTimeout(context->tcpSocket, 0);
157  //Any error to report?
158  if(error)
159  break;
160 
161  //Associate the socket with the relevant interface
162  error = socketBindToInterface(context->tcpSocket,
163  context->settings.interface);
164  //Any error to report?
165  if(error)
166  break;
167 
168  //The Echo server listens for TCP connection requests on port 7
169  error = socketBind(context->tcpSocket, &IP_ADDR_ANY,
170  context->settings.port);
171  //Any error to report?
172  if(error)
173  break;
174 
175  //Place socket in listening state
176  error = socketListen(context->tcpSocket, 0);
177  //Any error to report?
178  if(error)
179  break;
180 #endif
181 
182 #if (ECHO_SERVER_UDP_SUPPORT == ENABLED)
183  //Open a UDP socket
185  //Failed to open socket?
186  if(context->udpSocket == NULL)
187  {
188  //Report an error
189  error = ERROR_OPEN_FAILED;
190  break;
191  }
192 
193  //Force the socket to operate in non-blocking mode
194  error = socketSetTimeout(context->udpSocket, 0);
195  //Any error to report?
196  if(error)
197  break;
198 
199  //Associate the socket with the relevant interface
200  error = socketBindToInterface(context->udpSocket,
201  context->settings.interface);
202  //Any error to report?
203  if(error)
204  break;
205 
206  //The Echo server listens for UDP datagrams on port 7
207  error = socketBind(context->udpSocket, &IP_ADDR_ANY,
208  context->settings.port);
209  //Any error to report?
210  if(error)
211  break;
212 #endif
213 
214  //Start the Echo server
215  context->stop = FALSE;
216  context->running = TRUE;
217 
218  //Create a task
219  context->taskId = osCreateTask("Echo Server", (OsTaskCode) echoServerTask,
220  context, &context->taskParams);
221 
222  //Failed to create task?
223  if(context->taskId == OS_INVALID_TASK_ID)
224  {
225  //Report an error
226  error = ERROR_OUT_OF_RESOURCES;
227  break;
228  }
229 
230  //End of exception handling block
231  } while(0);
232 
233  //Any error to report?
234  if(error)
235  {
236  //Clean up side effects
237  context->running = FALSE;
238 
239 #if (ECHO_SERVER_TCP_SUPPORT == ENABLED)
240  //Close listening TCP socket
241  socketClose(context->tcpSocket);
242  context->tcpSocket = NULL;
243 #endif
244 
245 #if (ECHO_SERVER_UDP_SUPPORT == ENABLED)
246  //Close UDP socket
247  socketClose(context->udpSocket);
248  context->udpSocket = NULL;
249 #endif
250  }
251 
252  //Return status code
253  return error;
254 }
255 
256 
257 /**
258  * @brief Stop Echo server
259  * @param[in] context Pointer to the Echo server context
260  * @return Error code
261  **/
262 
264 {
265 #if (ECHO_SERVER_TCP_SUPPORT == ENABLED)
266  uint_t i;
267 #endif
268 
269  //Make sure the Echo server context is valid
270  if(context == NULL)
272 
273  //Debug message
274  TRACE_INFO("Stopping Echo server...\r\n");
275 
276  //Check whether the Echo server is running
277  if(context->running)
278  {
279 #if (NET_RTOS_SUPPORT == ENABLED)
280  //Stop the Echo server
281  context->stop = TRUE;
282  //Send a signal to the task to abort any blocking operation
283  osSetEvent(&context->event);
284 
285  //Wait for the task to terminate
286  while(context->running)
287  {
288  osDelayTask(1);
289  }
290 #endif
291 
292 #if (ECHO_SERVER_TCP_SUPPORT == ENABLED)
293  //Loop through the TCP connection table
294  for(i = 0; i < ECHO_SERVER_MAX_TCP_CONNECTIONS; i++)
295  {
296  //Close TCP connection
298  }
299 
300  //Close listening TCP socket
301  socketClose(context->tcpSocket);
302  context->tcpSocket = NULL;
303 #endif
304 
305 #if (ECHO_SERVER_UDP_SUPPORT == ENABLED)
306  //Close UDP socket
307  socketClose(context->udpSocket);
308  context->udpSocket = NULL;
309 #endif
310  }
311 
312  //Successful processing
313  return NO_ERROR;
314 }
315 
316 
317 /**
318  * @brief Echo server task
319  * @param[in] context Pointer to the Echo server context
320  **/
321 
323 {
324  error_t error;
325  uint_t i;
326  systime_t timeout;
328 
329 #if (NET_RTOS_SUPPORT == ENABLED)
330  //Task prologue
331  osEnterTask();
332 
333  //Process events
334  while(1)
335  {
336 #endif
337  //Set polling timeout
338  timeout = ECHO_SERVER_TICK_INTERVAL;
339 
340  //Clear event descriptor set
341  osMemset(eventDesc, 0, sizeof(eventDesc));
342 
343 #if (ECHO_SERVER_TCP_SUPPORT == ENABLED)
344  //Specify the events the application is interested in
345  for(i = 0; i < ECHO_SERVER_MAX_TCP_CONNECTIONS; i++)
346  {
347  EchoTcpConnection *connection;
348 
349  //Point to the structure describing the current TCP connection
350  connection = &context->tcpConnection[i];
351 
352  //Loop through active connections only
353  if(connection->state != ECHO_TCP_CONNECTION_STATE_CLOSED)
354  {
355  //Register connection events
356  echoServerRegisterTcpConnectionEvents(connection, &eventDesc[i]);
357 
358  //Check whether the socket is ready for I/O operation
359  if(eventDesc[i].eventFlags != 0)
360  {
361  //No need to poll the underlying socket for incoming traffic
362  timeout = 0;
363  }
364  }
365  }
366 
367  //The Echo server listens for TCP connection requests on port 7
368  eventDesc[i].socket = context->tcpSocket;
369  eventDesc[i++].eventMask = SOCKET_EVENT_RX_READY;
370 #else
371  //TCP Echo service is not supported
372  i = 0;
373 #endif
374 
375 #if (ECHO_SERVER_UDP_SUPPORT == ENABLED)
376  //The Echo server listens for UDP datagrams on port 7
377  eventDesc[i].socket = context->udpSocket;
378  eventDesc[i++].eventMask = SOCKET_EVENT_RX_READY;
379 #endif
380 
381  //Wait for one of the set of sockets to become ready to perform I/O
382  error = socketPoll(eventDesc, i, &context->event, timeout);
383 
384  //Check status code
385  if(error == NO_ERROR || error == ERROR_TIMEOUT ||
386  error == ERROR_WAIT_CANCELED)
387  {
388  //Stop request?
389  if(context->stop)
390  {
391  //Stop Echo server operation
392  context->running = FALSE;
393  //Task epilogue
394  osExitTask();
395  //Kill ourselves
397  }
398 
399 #if (ECHO_SERVER_TCP_SUPPORT == ENABLED)
400  //Event-driven processing
401  for(i = 0; i < ECHO_SERVER_MAX_TCP_CONNECTIONS; i++)
402  {
403  EchoTcpConnection *connection;
404 
405  //Point to the structure describing the current TCP connection
406  connection = &context->tcpConnection[i];
407 
408  //Loop through active connections only
409  if(connection->state != ECHO_TCP_CONNECTION_STATE_CLOSED)
410  {
411  //Check whether the socket is ready to perform I/O
412  if(eventDesc[i].eventFlags != 0)
413  {
414  //Connection event handler
416  }
417  }
418  }
419 
420  //Any TCP connection request received on port 7?
421  if(eventDesc[i++].eventFlags != 0)
422  {
423  //Accept TCP connection request
425  }
426 #else
427  //TCP Echo service is not supported
428  i = 0;
429 #endif
430 
431 #if (ECHO_SERVER_UDP_SUPPORT == ENABLED)
432  //Any UDP datagram received on port 7?
433  if(eventDesc[i].eventFlags != 0)
434  {
435  //Process incoming UDP datagram
437  }
438 #endif
439  }
440 
441  //Handle periodic operations
442  echoServerTick(context);
443 
444 #if (NET_RTOS_SUPPORT == ENABLED)
445  }
446 #endif
447 }
448 
449 
450 /**
451  * @brief Release Echo server context
452  * @param[in] context Pointer to the Echo server context
453  **/
454 
456 {
457  //Make sure the Echo server context is valid
458  if(context != NULL)
459  {
460  //Free previously allocated resources
461  osDeleteEvent(&context->event);
462 
463  //Clear Echo server context
464  osMemset(context, 0, sizeof(EchoServerContext));
465  }
466 }
467 
468 #endif
void echoServerDeinit(EchoServerContext *context)
Release Echo server context.
Definition: echo_server.c:455
OsTaskId osCreateTask(const char_t *name, OsTaskCode taskCode, void *arg, const OsTaskParameters *params)
Create a task.
@ SOCKET_IP_PROTO_UDP
Definition: socket.h:108
error_t socketBind(Socket *socket, const IpAddr *localIpAddr, uint16_t localPort)
Associate a local address with a socket.
Definition: socket.c:1316
#define ECHO_SERVER_TICK_INTERVAL
Definition: echo_server.h:101
#define osExitTask()
#define ECHO_SERVER_PRIORITY
Definition: echo_server.h:54
OsTaskParameters task
Task parameters.
Definition: echo_server.h:137
#define TRUE
Definition: os_port.h:50
#define OS_INVALID_TASK_ID
void socketClose(Socket *socket)
Close an existing socket.
Definition: socket.c:2062
@ ERROR_OUT_OF_RESOURCES
Definition: error.h:64
@ SOCKET_TYPE_DGRAM
Definition: socket.h:93
#define ECHO_PORT
Definition: echo_server.h:112
@ SOCKET_TYPE_STREAM
Definition: socket.h:92
#define OS_SELF_TASK_ID
Structure describing socket events.
Definition: socket.h:426
Echo TCP connection.
Definition: echo_server.h:148
@ ERROR_OPEN_FAILED
Definition: error.h:75
@ ECHO_TCP_CONNECTION_STATE_CLOSED
Definition: echo_server.h:126
void echoServerGetDefaultSettings(EchoServerSettings *settings)
Initialize settings with default values.
Definition: echo_server.c:54
const IpAddr IP_ADDR_ANY
Definition: ip.c:53
void osDeleteTask(OsTaskId taskId)
Delete a task.
#define FALSE
Definition: os_port.h:46
@ ERROR_INVALID_PARAMETER
Invalid parameter.
Definition: error.h:47
void echoServerProcessTcpConnectionEvents(EchoTcpConnection *connection)
Connection event handler.
void echoServerAcceptTcpConnection(EchoServerContext *context)
Accept connection request.
error_t
Error codes.
Definition: error.h:43
Echo server.
Echo server settings.
Definition: echo_server.h:136
void(* OsTaskCode)(void *arg)
Task routine.
NetInterface * interface
Underlying network interface.
Definition: echo_server.h:138
void osDeleteEvent(OsEvent *event)
Delete an event object.
error_t echoServerStop(EchoServerContext *context)
Stop Echo server.
Definition: echo_server.c:263
Socket * tcpSocket
Listening TCP socket.
Definition: echo_server.h:171
const OsTaskParameters OS_TASK_DEFAULT_PARAMS
#define ECHO_SERVER_STACK_SIZE
Definition: echo_server.h:47
#define ECHO_SERVER_MAX_TCP_CONNECTIONS
Definition: echo_server.h:66
#define TRACE_INFO(...)
Definition: debug.h:95
Socket * socketOpen(uint_t type, uint_t protocol)
Create a socket (UDP or TCP)
Definition: socket.c:125
void echoServerTick(EchoServerContext *context)
Handle periodic operations.
EchoTcpConnection tcpConnection[ECHO_SERVER_MAX_TCP_CONNECTIONS]
TCP connections.
Definition: echo_server.h:172
bool_t running
Operational state of the Echo server.
Definition: echo_server.h:165
#define osEnterTask()
uint16_t port
Echo server port number.
Definition: echo_server.h:139
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:2149
#define socketBindToInterface
Definition: net_legacy.h:193
uint32_t systime_t
System time.
@ ERROR_TIMEOUT
Definition: error.h:95
void echoServerTask(EchoServerContext *context)
Echo server task.
Definition: echo_server.c:322
Socket * udpSocket
UDP socket.
Definition: echo_server.h:175
error_t echoServerInit(EchoServerContext *context, const EchoServerSettings *settings)
Initialize Echo server context.
Definition: echo_server.c:76
Echo server context.
Definition: echo_server.h:163
@ SOCKET_EVENT_RX_READY
Definition: socket.h:179
OsTaskId taskId
Task identifier.
Definition: echo_server.h:169
@ ERROR_WAIT_CANCELED
Definition: error.h:73
bool_t osCreateEvent(OsEvent *event)
Create an event object.
Helper functions for Echo server.
void echoServerRegisterTcpConnectionEvents(EchoTcpConnection *connection, SocketEventDesc *eventDesc)
Register TCP connection events.
error_t echoServerStart(EchoServerContext *context)
Start Echo server.
Definition: echo_server.c:126
OsEvent event
Event object used to poll the sockets.
Definition: echo_server.h:167
void osDelayTask(systime_t delay)
Delay routine.
void osSetEvent(OsEvent *event)
Set the specified event object to the signaled state.
void echoServerCloseTcpConnection(EchoTcpConnection *connection)
Close TCP connection.
EchoServerSettings settings
User settings.
Definition: echo_server.h:164
Socket * socket
Handle to a socket to monitor.
Definition: socket.h:427
unsigned int uint_t
Definition: compiler_port.h:50
#define osMemset(p, value, length)
Definition: os_port.h:135
void echoServerProcessUdpDatagram(EchoServerContext *context)
Process incoming UDP datagram.
TCP/IP stack core.
EchoTcpConnectionState state
Connection state.
Definition: echo_server.h:149
bool_t stop
Stop request.
Definition: echo_server.h:166
@ SOCKET_IP_PROTO_TCP
Definition: socket.h:107
error_t socketSetTimeout(Socket *socket, systime_t timeout)
Set timeout value for blocking operations.
Definition: socket.c:148
uint_t eventMask
Requested events.
Definition: socket.h:428
@ ERROR_ALREADY_RUNNING
Definition: error.h:293
@ NO_ERROR
Success.
Definition: error.h:44
Debugging facilities.
OsTaskParameters taskParams
Task parameters.
Definition: echo_server.h:168
error_t socketListen(Socket *socket, uint_t backlog)
Place a socket in the listening state.
Definition: socket.c:1413