/* * Copyright (c) 2006-2021, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes */ #include <rtthread.h> #include <sys/socket.h> #ifdef SAL_USING_POSIX #include <sys/select.h> // only dfs_net #include <dfs_file.h> #include <unistd.h> #include <stdio.h> #include <sys/stat.h> #include <sys/statfs.h> #else #define read lwip_read #define write lwip_write #endif /* SAL_USING_POSIX */ #include "netdb.h" #define MAX_SERV 32 /* Maximum number of chargen services. Don't need too many */ #define CHARGEN_THREAD_NAME "chargen" #if RT_THREAD_PRIORITY_MAX == 32 #define CHARGEN_PRIORITY 20 /* Really low priority */ #else #define CHARGEN_PRIORITY 200 /* Really low priority */ #endif #define CHARGEN_THREAD_STACKSIZE 1024 struct charcb { struct charcb *next; int socket; struct sockaddr_in cliaddr; socklen_t clilen; char nextchar; }; static struct charcb *charcb_list = 0; static int do_read(struct charcb *p_charcb); static void close_chargen(struct charcb *p_charcb); extern int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); /************************************************************** * void chargen_thread(void *arg) * * chargen task. This server will wait for connections on well * known TCP port number: 19. For every connection, the server will * write as much data as possible to the tcp port. **************************************************************/ static void chargen_thread(void *arg) { int listenfd; struct sockaddr_in chargen_saddr; fd_set readset; fd_set writeset; int i, maxfdp1; struct charcb *p_charcb; /* First acquire our socket for listening for connections */ listenfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); LWIP_ASSERT("chargen_thread(): Socket create failed.", listenfd >= 0); memset(&chargen_saddr, 0, sizeof(chargen_saddr)); chargen_saddr.sin_family = AF_INET; chargen_saddr.sin_addr.s_addr = htonl(INADDR_ANY); chargen_saddr.sin_port = htons(19); // Chargen server port if (bind(listenfd, (struct sockaddr *) &chargen_saddr, sizeof(chargen_saddr)) == -1) LWIP_ASSERT("chargen_thread(): Socket bind failed.", 0); /* Put socket into listening mode */ if (listen(listenfd, MAX_SERV) == -1) LWIP_ASSERT("chargen_thread(): Listen failed.", 0); /* Wait forever for network input: This could be connections or data */ for (;;) { maxfdp1 = listenfd + 1; /* Determine what sockets need to be in readset */ FD_ZERO(&readset); FD_ZERO(&writeset); FD_SET(listenfd, &readset); for (p_charcb = charcb_list; p_charcb; p_charcb = p_charcb->next) { if (maxfdp1 < p_charcb->socket + 1) maxfdp1 = p_charcb->socket + 1; FD_SET(p_charcb->socket, &readset); FD_SET(p_charcb->socket, &writeset); } /* Wait for data or a new connection */ i = select(maxfdp1, &readset, &writeset, 0, 0); if (i == 0) continue; /* At least one descriptor is ready */ if (FD_ISSET(listenfd, &readset)) { /* We have a new connection request!!! */ /* Lets create a new control block */ p_charcb = (struct charcb *)rt_calloc(1, sizeof(struct charcb)); if (p_charcb) { p_charcb->socket = accept(listenfd, (struct sockaddr *) &p_charcb->cliaddr, &p_charcb->clilen); if (p_charcb->socket < 0) rt_free(p_charcb); else { /* Keep this tecb in our list */ p_charcb->next = charcb_list; charcb_list = p_charcb; p_charcb->nextchar = 0x21; } } else { /* No memory to accept connection. Just accept and then close */ int sock; struct sockaddr cliaddr; socklen_t clilen; sock = accept(listenfd, &cliaddr, &clilen); if (sock >= 0) closesocket(sock); } } /* Go through list of connected clients and process data */ for (p_charcb = charcb_list; p_charcb; p_charcb = p_charcb->next) { if (FD_ISSET(p_charcb->socket, &readset)) { /* This socket is ready for reading. This could be because someone typed * some characters or it could be because the socket is now closed. Try reading * some data to see. */ if (do_read(p_charcb) < 0) break; } if (FD_ISSET(p_charcb->socket, &writeset)) { char line[80]; char setchar = p_charcb->nextchar; for (i = 0; i < 59; i++) { line[i] = setchar; if (++setchar == 0x7f) setchar = 0x21; } line[i] = 0; strcat(line, "\n\r"); if (write(p_charcb->socket, line, strlen(line)) < 0) { close_chargen(p_charcb); break; } if (++p_charcb->nextchar == 0x7f) p_charcb->nextchar = 0x21; } } } } /************************************************************** * void close_chargen(struct charcb *p_charcb) * * Close the socket and remove this charcb from the list. **************************************************************/ static void close_chargen(struct charcb *p_charcb) { struct charcb *p_search_charcb; /* Either an error or tcp connection closed on other * end. Close here */ closesocket(p_charcb->socket); /* Free charcb */ if (charcb_list == p_charcb) charcb_list = p_charcb->next; else for (p_search_charcb = charcb_list; p_search_charcb; p_search_charcb = p_search_charcb->next) { if (p_search_charcb->next == p_charcb) { p_search_charcb->next = p_charcb->next; break; } } rt_free(p_charcb); } /************************************************************** * void do_read(struct charcb *p_charcb) * * Socket definitely is ready for reading. Read a buffer from the socket and * discard the data. If no data is read, then the socket is closed and the * charcb is removed from the list and freed. **************************************************************/ static int do_read(struct charcb *p_charcb) { char buffer[80]; int readcount; /* Read some data */ readcount = read(p_charcb->socket, &buffer, 80); if (readcount <= 0) { close_chargen(p_charcb); return -1; } return 0; } void chargen_init(void) { rt_thread_t chargen; chargen = rt_thread_create(CHARGEN_THREAD_NAME, chargen_thread, RT_NULL, CHARGEN_THREAD_STACKSIZE, CHARGEN_PRIORITY, 5); if (chargen != RT_NULL) rt_thread_startup(chargen); } #ifdef RT_USING_FINSH #include <finsh.h> void chargen() { chargen_init(); } FINSH_FUNCTION_EXPORT(chargen, start chargen server); #endif