From 308aaab65c00a6a7d3dccacf32402d398391983a Mon Sep 17 00:00:00 2001 From: Guo Wenxue <guowenxue@gmail.com> Date: Sun, 25 Nov 2018 23:03:59 +0800 Subject: [PATCH] Add ch5_multiplexing for select --- ch5_multiplexing/socket_server_select.c | 326 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 326 insertions(+), 0 deletions(-) diff --git a/ch5_multiplexing/socket_server_select.c b/ch5_multiplexing/socket_server_select.c new file mode 100644 index 0000000..cd98c02 --- /dev/null +++ b/ch5_multiplexing/socket_server_select.c @@ -0,0 +1,326 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <time.h> +#include <pthread.h> +#include <getopt.h> +#include <libgen.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <netinet/in.h> + + +typedef struct link_node_s +{ + int fd; + struct link_node_s *next; +} link_node_t; + + +static inline void msleep(unsigned long ms); +static inline void print_usage(char *progname); +int socket_server_init(char *listen_ip, int listen_port); +int add_fd_list(link_node_t **head, int fd); +link_node_t *list_remove_node(link_node_t **head, link_node_t *node); + + +int main(int argc, char **argv) +{ + int listenfd, connfd; + int serv_port = 0; + int daemon_run = 0; + char *progname = NULL; + pthread_t tid; + int opt; + fd_set rdset; + link_node_t *client_list = NULL; /* client fd link list, we will not use arrary for it get max client limite */ + link_node_t *node; + int rv; + int i; + int maxfd=0; + char buf[1024]; + + struct option long_options[] = + { + {"daemon", no_argument, NULL, 'b'}, + {"port", required_argument, NULL, 'p'}, + {"help", no_argument, NULL, 'h'}, + {NULL, 0, NULL, 0} + }; + + progname = basename(argv[0]); + + /* Parser the command line parameters */ + while ((opt = getopt_long(argc, argv, "bp:h", long_options, NULL)) != -1) + { + switch (opt) + { + case 'b': + daemon_run=1; + break; + + case 'p': + serv_port = atoi(optarg); + break; + + case 'h': /* Get help information */ + print_usage(progname); + return EXIT_SUCCESS; + + default: + break; + } + } + + if( !serv_port ) + { + print_usage(progname); + return -1; + } + + if( (listenfd=socket_server_init(NULL, serv_port)) < 0 ) + { + printf("ERROR: %s server listen on port %d failure\n", argv[0],serv_port); + return -2; + } + printf("%s server start to listen on port %d\n", argv[0],serv_port); + + + /* set program running on background */ + if( daemon_run ) + { + daemon(0, 0); + } + + printf("add listen socket[%d] into client list\n", listenfd ); + add_fd_list(&client_list, listenfd); + + for ( ; ; ) + { + FD_ZERO(&rdset); + for( node=client_list; node!=NULL; node=node->next ) + { + //printf("Add socket[%d] in select readset\n", node->fd); + maxfd = node->fd>maxfd ? node->fd : maxfd; + FD_SET(node->fd, &rdset); + } + + /* program will blocked here */ + rv = select(maxfd+1, &rdset, NULL, NULL, NULL); + if(rv < 0) + { + printf("select failure: %s\n", strerror(errno)); + break; + } + else if(rv == 0) + { + printf("select get timeout\n"); + continue; + } + + /* listen socket get event means new client start connect now */ + if ( FD_ISSET(listenfd, &rdset) ) + { + if( (connfd=accept(listenfd, (struct sockaddr *)NULL, NULL)) < 0) + { + printf("accept new client failure: %s\n", strerror(errno)); + continue; + } + + printf("accept new client[%d] and add it into client list\n", connfd ); + add_fd_list(&client_list, connfd); + } + else /* data arrive from already connected client */ + { + node = client_list; + while( node != NULL ) + { + if( !FD_ISSET(node->fd, &rdset) ) + { + node = node->next; + continue; + } + + if( (rv=read(node->fd, buf, sizeof(buf))) <= 0) + { + printf("socket[%d] read failure or get disconncet.\n", node->fd); + /* list_remove_node will return the previous node */ + node = list_remove_node(&client_list, node); + } + else + { + printf("socket[%d] read get %d bytes data\n", node->fd, rv); + + /* convert letter from lowercase to uppercase */ + for(i=0; i<rv; i++) + buf[i]=toupper(buf[i]); + + if( write(node->fd, buf, rv) < 0 ) + { + printf("socket[%d] write failure: %s\n", node->fd, strerror(errno)); + /* list_remove_node will return the previous node */ + node = list_remove_node(&client_list, node); + } + } + + if( node != NULL ) + { + node = node->next; + } + } /* while( node != NULL ) */ + } + } + +CleanUp: + close(listenfd); /* We must close socket File Description when program exit*/ + return 0; +} + + +static inline void msleep(unsigned long ms) +{ + struct timeval tv; + + tv.tv_sec = ms/1000; + tv.tv_usec = (ms%1000)*1000; + + select(0, NULL, NULL, NULL, &tv); +} + + +static inline void print_usage(char *progname) +{ + printf("Usage: %s [OPTION]...\n", progname); + + printf(" %s is a socket server program, which used to verify client and echo back string from it\n", progname); + printf("\nMandatory arguments to long options are mandatory for short options too:\n"); + + printf(" -b[daemon ] set program running on background\n"); + printf(" -p[port ] Socket server port address\n"); + printf(" -h[help ] Display this help information\n"); + + + printf("\nExample: %s -b -p 8900\n", progname); + return ; +} + + +int socket_server_init(char *listen_ip, int listen_port) +{ + struct sockaddr_in servaddr; + int rv = 0; + int on = 1; + int listenfd; + + if ( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) + { + printf("Use socket() to create a TCP socket failure: %s\n", strerror(errno)); + return -1; + } + + /* Set socket port reuseable, fix 'Address already in use' bug when socket server restart */ + setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + + memset(&servaddr, 0, sizeof(servaddr)); + servaddr.sin_family = AF_INET; + servaddr.sin_port = htons(listen_port); + + if( !listen_ip ) /* Listen all the local IP address */ + { + servaddr.sin_addr.s_addr = htonl(INADDR_ANY); + } + else /* listen the specified IP address */ + { + if (inet_pton(AF_INET, listen_ip, &servaddr.sin_addr) <= 0) + { + printf("inet_pton() set listen IP address failure.\n"); + rv = -2; + goto CleanUp; + } + } + + + if(bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0) + { + printf("Use bind() to bind the TCP socket failure: %s\n", strerror(errno)); + rv = -3; + goto CleanUp; + } + + if(listen(listenfd, 13) < 0) + { + printf("Use bind() to bind the TCP socket failure: %s\n", strerror(errno)); + rv = -4; + goto CleanUp; + } + +CleanUp: + if(rv<0) + close(listenfd); + else + rv = listenfd; + + return rv; +} + +int add_fd_list(link_node_t **head, int fd) +{ + link_node_t *node; + link_node_t *tmp; + + if( NULL == (node=(link_node_t *)malloc(sizeof(*node))) ) + { + printf("malloc for new node failure\n"); + return -1; + } + + memset(node, 0, sizeof(*node)); + node->fd = fd; + node->next = NULL; + + if( *head == NULL ) + { + *head = node; + return 0; + } + + tmp = *head; + while( tmp->next != NULL ) + { + tmp=tmp->next; + } + + tmp->next = node; +} + +/* list_remove_node will return the previous node */ +link_node_t *list_remove_node(link_node_t **head, link_node_t *node) +{ + link_node_t *prev; + + close(node->fd); + + if( node == *head ) + { + *head = NULL; + free(node); + return NULL; + } + else + { + for(prev=*head; prev!=NULL; prev=prev->next) + { + if(prev->next == node) + { + prev->next = node->next; + free(node); + return prev; + } + } + } +} + -- Gitblit v1.9.1