From 5e3def9494b67c644d462c5258bab84ef4510b7a Mon Sep 17 00:00:00 2001 From: davidhaas Date: Fri, 11 Apr 2003 15:21:49 +0000 Subject: [PATCH] Added an example program which uses sockets and supports multiple sessions. --- apps/chargen/README | 52 ++++++++ apps/chargen/chargen.c | 261 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 313 insertions(+) create mode 100644 apps/chargen/README create mode 100644 apps/chargen/chargen.c diff --git a/apps/chargen/README b/apps/chargen/README new file mode 100644 index 0000000..7dc2ee5 --- /dev/null +++ b/apps/chargen/README @@ -0,0 +1,52 @@ + + CHARGEN + +This file implements a nice example of handling multiple tcp sockets in a +server environment. Just call chargen_init() from your application after +you have initialized lwip and added your network interfaces. Change the +MAX_SERV option to increase or decrease the number of sessions supported. + +chargen will jam as much data as possible into the output socket, so it +will take up a lot of CPU time. Therefore it will be a good idea to run it +as the lowest possible priority (just ahead of any idle task). + +This is also a good example of how to support multiple sessions in an +embedded system where you might not have fork(). The multiple sessions are +all handled by the same thread and select() is used for demultiplexing. + +No makefile is provided, just add chargen to the makefile for your +application. It is OS and HW independent. + +Once the chargen server is running in your application, go to another system +and open a telnet session to your lwip platform at port 19. You should see an +ASCII pattern start to stream on you screen. + +As an example, lets say that your system running lwip is at IP address +192.168.10.244 and you have a linux system connected to it at IP address +192.168.10.59. Issue the following command at a terminal prompt on the linux system: + +telnet 192.168.10.244 19 + +You will see a pattern similar to the following on streaming by on your +screen: + +ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{ +BCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{| +CDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|} +DEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ +EFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~! +FGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!" +GHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"# +HIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$ +IJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$% +JKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%& +KLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&' +LMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'( + +It even works from windows: At a dos prompt you can also issue the same +telnet command and you will get a similar (but much slower, at least on W98) +data stream. + +David Haas + + diff --git a/apps/chargen/chargen.c b/apps/chargen/chargen.c new file mode 100644 index 0000000..cc8bca8 --- /dev/null +++ b/apps/chargen/chargen.c @@ -0,0 +1,261 @@ +/** @file + * + * chargen server for lwip + */ +/* + * Copyright (c) 2003 NBS Card Technology, Paramus, NJ. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: David Haas + * + * Purpose: chargen server for testing and demonstration purposes + * + * This file implements a nice example of handling multiple tcp sockets in a + * server environment. Just call chargen_init() from your application after + * you have initialized lwip and added your network interfaces. Change the + * MAX_SERV option to increase or decrease the number of sessions supported. + * + * chargen will jam as much data as possible into the output socket, so it + * will take up a lot of CPU time. Therefore it will be a good idea to run it + * as the lowest possible priority (just ahead of any idle task). + * + * This is also a good example of how to support multiple sessions in an + * embedded system where you might not have fork(). + */ + +#include "lwipopts.h" +#include "lwip/sys.h" +#include "lwip/sockets.h" + +#define MAX_SERV 5 /* Maximum number of chargen services. Don't need too many */ +#define CHARGEN_PRIORITY 254 /* Really low priority */ +#define SEND_SIZE TCP_SNDLOWAT /* If we only send this much, then when select + says we can send, we know we won't block */ +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); + +/************************************************************** + * 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, 0); + + 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 + + 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 *)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) + 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) + close(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 */ + close(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; + } + } + 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) + * + * This function initializes the chargen service. This function + * may only be called either before or after tasking has started. + **************************************************************/ +void chargen_init(void) +{ + sys_thread_new(chargen_thread, 0, CHARGEN_PRIORITY); + +} + + +