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;
|
| | | }
|
| | | }
|
| | | }
|
| | | }
|
| | |
|