/********************************************************************************* * Copyright: (C) 2020 LingYun IoT System Studio * All rights reserved. * * Filename: socket_server.c * Description: This is a socket server program to receive RPi's temperature * * Version: 1.0.0(2020年04月14日) * Author: Guo Wenxue * ChangeLog: 1, Release initial version on "2020年04月14日 00时52分56秒" * ********************************************************************************/ #include #include #include #include #include #include #include #include "logger.h" #include "proc.h" #include "list.h" #include "socket.h" #include "packet.h" #define PROG_VERSION "1.0.0" #define DAEMON_PIDFILE "/tmp/.sockets.pid" #define MAX_EVENTS 512 /* Every client need get a stand alone buffer to save TLV packet data */ typedef struct cli_buf_s { int fd; /* client fd */ char buf[512]; /* client receive buffer */ int bytes; /* left bytes in buffer */ struct list_head list; /* all client buffer saved in a list */ } cli_buf_t; static int parser_tlv_data(cli_buf_t *cli_buf); static void term_socket_client(int epollfd, int fd, struct list_head *buf_list); static void print_usage(char *progname) { printf("Usage: %s [OPTION]...\n", progname); printf(" %s is LingYun studio temperature socket server program running on RaspberryPi\n", progname); printf("\nMandatory arguments to long options are mandatory for short options too:\n"); printf("-p(--port) : sepcify server port.\n"); printf("-d(--debug) : running in debug mode\n"); printf("-v(--version) : display the program version\n"); printf("-h(--help) : display this help information\n"); printf("\n%s version %s build on %s %s\n", progname, PROG_VERSION, __DATE__, __TIME__); return; } int main (int argc, char *argv[]) { char *progname=NULL; int daemon = 1; int opt; int i = 0; int rv = 0; char *logfile="sock_server.log"; int loglevel=LOG_LEVEL_INFO; int logsize=10; /* logfile size max to 10K */ socket_t sock; int port = 10086; int epollfd; int connfd; int fd; int nfds; struct epoll_event event_array[MAX_EVENTS]; cli_buf_t *cli_buf; cli_buf_t *node; struct list_head buf_list; /* every client get a stand alone buffer saved in the list */ struct option long_options[] = { {"port", required_argument, NULL, 'p'}, {"debug", no_argument, NULL, 'd'}, {"version", no_argument, NULL, 'v'}, {"help", no_argument, NULL, 'h'}, {NULL, 0, NULL, 0} }; memset(&sock, 0, sizeof(sock)); INIT_LIST_HEAD(&buf_list); progname = basename(argv[0]); /* get program name */ /* parser the command line parameters */ while ((opt = getopt_long(argc, argv, "p:dvh", long_options, NULL)) != -1) { switch (opt) { case 'p': /* listen port */ port = atoi(optarg); break; case 'd': /* set debug running */ daemon = 0; logfile="console"; loglevel=LOG_LEVEL_DEBUG; break; case 'v': /* print software version */ printf("%s version %s\n", progname, PROG_VERSION); return 0; case 'h': /* print help information */ print_usage(progname); return 0; default: break; } } /* open logger system */ if( log_open(logfile, loglevel, logsize, THREAD_LOCK_NONE) < 0 ) { fprintf(stderr, "Initial log system failed\n"); return 1; } /* install signal proc handler */ install_default_signal(); /* check program already running or not, if already running then exit, or set running as daemon */ if( check_set_program_running(daemon, DAEMON_PIDFILE) < 0 ) goto cleanup; log_info("socket server(TLV) running and listen on port[%d].\n", port); /* initial listen socket */ if( socket_listen(&sock, port) < 0 ) { log_error("create listen socket failure\n"); rv = 2; goto cleanup; } /* create epoll and put listen socket into it */ if( (epollfd=epoll_init(MAX_EVENTS, sock.fd)) < 0 ) { log_error("initial epoll for listen socket failure\n"); rv = 3; goto cleanup; } /* g_signal.stop defined in proc.c, and will be set when catch stop signal */ while( !g_signal.stop ) { /* epoll wailt and program will blocked here */ nfds = epoll_wait(epollfd, event_array, MAX_EVENTS, -1 /* never timeout */); if( nfds <= 0 ) { log_error("epoll_wait failure or timeout: %s\n", strerror(errno)); continue; } /* nfds>0 is the active events count */ for(i=0; ifd = connfd; list_add_tail(&cli_buf->list, &buf_list); log_info("alloc and add socket[%d] buffer[%p] into list\n", cli_buf->fd, cli_buf); } log_info("accept and add new client socket[%d] ok.\n", connfd); } /* already connected client socket get data arrive */ else { cli_buf = NULL; /* Use list_for_each_entry to find the client buf in buf list */ list_for_each_entry(node, &buf_list, list) { if( node->fd == fd ) { log_info("found socket[%d] buffer[%p] in list\n", fd, node); cli_buf = node; } } if( !cli_buf ) { log_error("can not find socket[%d] buffer and close it now\n", fd); term_socket_client(epollfd, fd, NULL); } rv=read(fd, &cli_buf->buf[cli_buf->bytes], sizeof(cli_buf->buf)-cli_buf->bytes); if( rv <= 0 ) { log_warn("socket[%d] read failure [%s] or disconncet, terminate it now.\n", fd, strerror(errno)); term_socket_client(epollfd, fd, &buf_list); continue; } else { cli_buf->bytes += rv; log_debug("socket[%d] receive %d bytes data\n", fd, rv); } parser_tlv_data(cli_buf); } } sleep(1); } cleanup: socket_term(&sock); log_close(); return 0; } static int parser_tlv_data(cli_buf_t *cli_buf) { int i, rv; int left_bytes; pack_info_t pack_info; if( !cli_buf ) { log_error("Invalid input arguments\n"); return -1; } log_info("start to parser client buffer %d bytes data:\n", cli_buf->bytes); log_dump(LOG_LEVEL_INFO, NULL, cli_buf->buf, cli_buf->bytes); if( cli_buf->bytes < TLV_FIXSIZE ) { log_warn("TLV packet bytes less than min. size\n"); return -2; } for(i=0; ibytes; i++) { memset(&pack_info, 0, sizeof(pack_info)); rv = parser_tlv_pack(&pack_info, (uint8_t *)&cli_buf->buf[i], cli_buf->bytes); if( rv <= 0 ) { /* parser failed, continue to parser next byte */ continue; } else if( rv == 0) { log_debug("found TLV packet header on [%d]\n", i); /* remove the parsed garbage data */ left_bytes = cli_buf->bytes - i; memmove(cli_buf->buf, &cli_buf->buf[i], left_bytes); cli_buf->bytes = left_bytes; log_warn("TLV packet not integrated, need receive left data"); break; } /* parser and get a correct TLV packet */ left_bytes = cli_buf->bytes - rv; memmove(cli_buf->buf, &cli_buf->buf[i+rv], left_bytes); cli_buf->bytes = left_bytes; if( TAG_TEMPERATURE == pack_info.tag ) { log_info("devsn: %s temperature:%.2f \n", pack_info.devid, pack_info.temper); } } /* all the data is garbage */ if( rv < 0) { log_error("parser TLV data failed, remove the unused garbage data\n"); /* maybe last byte is the header's first byte */ cli_buf->buf[0] = cli_buf->buf[cli_buf->bytes-1]; cli_buf->bytes = 1; } return 0; } static void term_socket_client(int epollfd, int fd, struct list_head *buf_list) { cli_buf_t *buf, *tmp; log_warn("start terminatl client socket[%d]\n", fd); /* if get buf_list, then remove buf from list */ if( buf_list ) { /* Use list_for_each_entry_safe to travel the socket list and destroy the buffer */ list_for_each_entry_safe(buf, tmp, buf_list, list) { if( buf->fd == fd ) { list_del(&buf->list); free(buf); log_info("free socket[%d] buffer[%p]\n", fd, buf); break; } } } epoll_del(epollfd, fd); close( fd ); }