#include #include #include #include #include #include #include #include #include #include #include #include #include #include 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; ifd, 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; } } } }