2 * Copyright (c) 2019-2020 Arm Limited. All rights reserved.
4 * SPDX-License-Identifier: Apache-2.0
6 * Licensed under the Apache License, Version 2.0 (the License); you may
7 * not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an AS IS BASIS, WITHOUT
14 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
18 * -----------------------------------------------------------------------------
21 * Title: SockServer embedded system application
22 * Purpose: Implements ECHO, DISCARD and CHARGEN services
23 * - Echo Protocol service [RFC 862]
24 * - Discard Protocol service [RFC 863]
25 * - Character Generator Protocol service [RFC 864]
27 * -----------------------------------------------------------------------------
34 #include "cmsis_os2.h"
35 #include "SockServer.h"
37 // Global status variables
38 SOCKADDR_IN remote_addr; // Remote IP address and port
39 uint32_t rx_cnt; // Receive count
40 uint32_t tx_cnt; // Transmit count
43 static void EchoThread (void *argument);
44 static void ChargenThread (void *argument);
45 static void DiscardThread (void *argument);
46 static char gen_char (char *buf, char setchar, uint32_t len);
48 // Generate character array for transmit
49 static char gen_char (char *buf, char setchar, uint32_t len) {
53 if ((++setchar < 0x21) || (setchar == 0x7f)) {
56 for (i = 0, ch = setchar; i < (len-2); i++) {
58 if (++ch == 0x7f) ch = 0x21;
65 // Datagram server thread
66 // (runs ECHO and CHARGEN services)
67 void DgramServer (void *argument) {
69 int32_t sock_echo,sock_chargen;
70 int32_t nfds,rc,sa_len;
76 sock_echo = socket (PF_INET, SOCK_DGRAM, 0);
77 sock_chargen = socket (PF_INET, SOCK_DGRAM, 0);
80 sa.sin_family = AF_INET;
81 sa.sin_addr.s_addr = INADDR_ANY;
83 sa.sin_port = htons (ECHO_PORT);
84 bind (sock_echo, (SOCKADDR *)&sa, sizeof(sa));
86 sa.sin_port = htons (CHARGEN_PORT);
87 bind (sock_chargen, (SOCKADDR *)&sa, sizeof(sa));
90 buff = malloc (BUFF_SIZE);
95 FD_SET(sock_echo, &fds);
96 FD_SET(sock_chargen, &fds);
97 nfds = (sock_echo > sock_chargen) ? sock_echo : sock_chargen;
101 // Wait for the packet
102 select (nfds+1, &fds, 0, 0, &tv);
104 if (FD_ISSET(sock_echo, &fds)) {
105 // Data ready, recvfrom will not block
106 sa_len = sizeof (sa);
107 rc = recvfrom (sock_echo, buff, BUFF_SIZE, 0, (SOCKADDR *)&sa, &sa_len);
110 memcpy (&remote_addr, &sa, sizeof(sa));
111 rc = sendto (sock_echo, buff, rc, 0, (SOCKADDR *)&sa, sa_len);
112 if (rc > 0) tx_cnt += rc;
118 if (FD_ISSET(sock_chargen, &fds)) {
119 // Data ready, recvfrom will not block
120 sa_len = sizeof (sa);
121 rc = recvfrom (sock_chargen, buff, BUFF_SIZE, 0, (SOCKADDR *)&sa, &sa_len);
124 memcpy (&remote_addr, &sa, sizeof(sa));
125 int32_t len = rand() >> 22;
126 if (len < 2) len = 2;
127 if (len > BUFF_SIZE) len = BUFF_SIZE;
128 setchar = gen_char (buff, setchar, len);
129 rc = sendto (sock_chargen, buff, len, 0, (SOCKADDR *)&sa, sa_len);
130 if (rc > 0) tx_cnt += rc;
140 // ECHO stream socket handler (2 instances)
141 static void EchoThread (void *argument) {
142 int32_t sock = (int32_t)argument;
145 char *buff = malloc (BUFF_SIZE);
147 rc = recv (sock, buff, BUFF_SIZE, 0);
150 rc = send (sock, buff, rc, 0);
151 if (rc > 0) tx_cnt += rc;
152 // ESC terminates the thread
153 if (buff[0] == ESC) break;
161 // CHARGEN stream socket handler (2 instances)
162 static void ChargenThread (void *argument) {
163 int32_t rc,sock = (int32_t)argument;
164 char buff[82],setchar = '@';
167 rc = recv (sock, buff, sizeof(buff), MSG_DONTWAIT);
168 if (rc > 0) rx_cnt += rc;
169 // ESC terminates the thread
170 if ((rc > 0) && (buff[0] == ESC)) break;
171 setchar = gen_char (buff, setchar, 81);
172 rc = send (sock, buff, 81, 0);
180 // DISCARD stream socket handler (1 instance)
181 static void DiscardThread (void *argument) {
182 int32_t rc,sock = (int32_t)argument;
186 rc = recv (sock, buff, sizeof(buff), 0);
187 if (rc > 0) rx_cnt += rc;
188 // ESC terminates the thread
189 if ((rc > 0) && (buff[0] == ESC)) break;
195 // Stream server thread
196 // (runs ECHO, CHARGEN and DISCARD services)
197 void StreamServer (void *argument) {
199 int32_t sock_echo,sock_chargen,sock_discard,sock_timeout;
200 int32_t sock,nfds,sa_len;
205 sock_echo = socket (PF_INET, SOCK_STREAM, 0);
206 sock_chargen = socket (PF_INET, SOCK_STREAM, 0);
207 sock_discard = socket (PF_INET, SOCK_STREAM, 0);
208 sock_timeout = socket (PF_INET, SOCK_STREAM, 0);
211 sa.sin_family = AF_INET;
212 sa.sin_addr.s_addr = INADDR_ANY;
214 sa.sin_port = htons (ECHO_PORT);
215 bind (sock_echo, (SOCKADDR *)&sa, sizeof(sa));
217 sa.sin_port = htons (CHARGEN_PORT);
218 bind (sock_chargen, (SOCKADDR *)&sa, sizeof(sa));
220 sa.sin_port = htons (DISCARD_PORT);
221 bind (sock_discard, (SOCKADDR *)&sa, sizeof(sa));
223 sa.sin_port = htons (TCP_TIMEOUT_PORT);
224 bind (sock_timeout, (SOCKADDR *)&sa, sizeof(sa));
227 listen (sock_echo, 2);
228 listen (sock_chargen, 2);
229 listen (sock_discard, 1);
230 listen (sock_timeout, 1);
234 FD_SET(sock_echo, &fds);
235 FD_SET(sock_chargen, &fds);
236 FD_SET(sock_discard, &fds);
239 if (sock_chargen > nfds) nfds = sock_chargen;
240 if (sock_discard > nfds) nfds = sock_discard;
245 // Wait for the client to connect
246 select (nfds+1, &fds, 0, 0, &tv);
247 if (FD_ISSET(sock_echo, &fds)) {
248 // Connect is pending, accept will not block
250 sock = accept (sock_echo, (SOCKADDR *)&sa, &sa_len);
252 memcpy (&remote_addr, &sa, sa_len);
253 // Create spawn thread (max.2)
254 osThreadNew(EchoThread, (void *)sock, NULL);
257 if (FD_ISSET(sock_chargen, &fds)) {
258 // Connect is pending, accept will not block
260 sock = accept (sock_chargen, (SOCKADDR *)&sa, &sa_len);
262 memcpy (&remote_addr, &sa, sa_len);
263 // Create spawn thread (max.2)
264 osThreadNew(ChargenThread, (void *)sock, NULL);
267 if (FD_ISSET(sock_discard, &fds)) {
268 // Connect is pending, accept will not block
270 sock = accept (sock_discard, (SOCKADDR *)&sa, &sa_len);
272 memcpy (&remote_addr, &sa, sa_len);
273 // Create spawn thread (max.1)
274 osThreadNew(DiscardThread, (void *)sock, NULL);
281 // Test assistant thread
282 void TestAssistant (void *argument) {
284 int32_t sock,sd,rc,sa_len;
285 static char buff[1500];
289 sock = socket (PF_INET, SOCK_STREAM, 0);
292 sa.sin_family = AF_INET;
293 sa.sin_addr.s_addr = INADDR_ANY;
294 sa.sin_port = htons (ASSISTANT_PORT);
295 bind (sock, (SOCKADDR *)&sa, sizeof(sa));
299 // Wait for the client to connect
300 sa_len = sizeof (sa);
301 sd = accept (sock, (SOCKADDR *)&sa, &sa_len);
303 // Set blocking receive timeout
304 uint32_t tout = 2000;
305 setsockopt (sd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tout, sizeof(tout));
306 // Receive the command (tout = 2s)
307 rc = recv (sd, buff, sizeof(buff), 0);
310 if ((strncmp (buff, "CONNECT TCP", 11) == 0) ||
311 (strncmp (buff, "CONNECT UDP", 11) == 0) ||
312 (strncmp (buff, "SEND TCP", 8) == 0) ||
313 (strncmp (buff, "RECV TCP", 8) == 0)) {
323 /* Syntax: CONNECT <proto>,<ip_addr>,<port>,<delay_ms>
324 Param: <proto> = protocol (TCP, UDP)
325 <ip_addr> = IP address (0.0.0.0 = sender address)
327 <delay_ms> = startup delay
329 Example: CONNECT TCP,192.168.1.200,80,600
330 (wait 600ms then connect to 192.168.1.200, port 80)
332 if (buff[0] == 'C') { // CONNECT
338 da.s_addr = INADDR_ANY;
339 sscanf (buff+11,",%hhu.%hhu.%hhu.%hhu,%hu,%hu",
340 &da.s_b1, &da.s_b2, &da.s_b3, &da.s_b4, &port, &delay);
341 if (da.s_addr != INADDR_ANY) {
342 // Supplied address not 0.0.0.0 use it
343 sa.sin_addr.s_addr = da.s_addr;
345 sa.sin_port = htons (port);
348 if (delay < 10) delay = 10;
349 if (delay > 5000) delay = 6000;
352 // Create stream or datagram socket
353 sock = socket (PF_INET, (buff[8] == 'T') ? SOCK_STREAM : SOCK_DGRAM, 0);
355 // Connect to requested address
356 rc = connect (sock, (SOCKADDR *)&sa, sa_len);
358 // Send some text, wait and close
359 send (sock, "SockServer", 10, 0);
367 /* Syntax: SEND <proto>,<bsize>,<time_ms>
368 Param: <proto> = protocol (TCP, UDP)
369 <bsize> = size of data block in bytes
370 <time_ms> = test duration in ms
372 if (buff[0] == 'S') { // SEND
373 uint32_t bsize,time,ticks;
374 int32_t i,n,cnt,ch = 'a';
376 // Parse command parameters
377 sscanf (buff+8,",%u,%u",&bsize,&time);
380 if (bsize < 32) bsize = 32;
381 if (bsize > 1460) bsize = 1460;
382 if (time < 500) time = 500;
383 if (time > 60000) time = 60000;
387 time = SYSTICK_MSEC(time);
388 ticks = GET_SYSTICK();
391 n = sprintf (buff,"Block[%d] ",++i);
392 memset (buff+n, ch, bsize-n);
394 if (++ch > '~') ch = ' ';
395 n = send (sd, buff, bsize, 0);
397 } while (GET_SYSTICK() - ticks < time);
399 // Inform the client of the number of bytes received
400 n = sprintf (buff,"STAT %d bytes.",cnt);
401 send (sd, buff, n, 0);
403 // Let the client close the connection
404 while (recv (sd, buff, sizeof(buff), 0) > 0);
410 /* Syntax: RECV <proto>,<bsize>
411 Param: <proto> = protocol (TCP, UDP)
412 <bsize> = size of data block in bytes
414 if (buff[0] == 'R') { // RECV
418 // Parse command parameters
419 sscanf (buff+8,",%u",&bsize);
422 if (bsize < 32) bsize = 32;
423 if (bsize > 1460) bsize = 1460;
427 for (cnt = 0; ; cnt += n) {
428 n = recv (sd, buff, bsize, 0);
429 if (strncmp(buff, "STOP", 4) == 0) {
430 // Client terminated upload
436 // Inform the client of the number of bytes received
437 n = sprintf (buff, "STAT %d bytes.",cnt);
438 send (sd, buff, n, 0);
440 // Let the client close the connection
441 while (recv (sd, buff, sizeof(buff), 0) > 0);