| New file | 
 |  |  | 
 |  |  | #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;
 | 
 |  |  |        }
 | 
 |  |  |            }
 | 
 |  |  |     }
 | 
 |  |  | }
 | 
 |  |  | 
 |