APUE Learning Example Source Code
guowenxue
2023-11-06 d81310d55b9b7d07904c19f879f50e52fd7be489
prj1_tlv/tlv_server.c
@@ -20,6 +20,8 @@
#include "logger.h"
#include "proc.h"
#include "socket.h"
#include "tlv_pack.h"
#include "crc-itu-t.h"
#define PROG_VERSION     "1.0.0"
@@ -28,6 +30,19 @@
#define DEF_LISTEN_PORT  10086
#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_tlvpack(cli_buf_t *cli_buf);
static void term_socket_client(int epollfd, int fd, struct list_head *buf_list);
static void banner(void)
{
@@ -71,9 +86,13 @@
    socket_t              sock;
    int                   epollfd;
    int                   connfd;
    int                   fd;
    int                   nfds;
    struct epoll_event    event_array[MAX_EVENTS];
    char                  buf[1024];
    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'}, 
@@ -86,6 +105,7 @@
    memset(&logger, 0, sizeof(logger));
    memset(&sock, 0, sizeof(sock));
    INIT_LIST_HEAD(&buf_list);
    progname = basename(argv[0]); /* get program name */
@@ -154,14 +174,17 @@
    log_nrml("TLV server program start running\n");
    /* set open file description number to max */
    set_socket_rlimit();
    /* initial listen socket  */
    if( socket_listen(&sock, DEF_LISTEN_IP, port) < 0 )
    {
        log_err("create listen socket failure\n");
        return -2;
    }
    /* create epoll and put listen socket into it  */
    if( (epollfd=epoll_init(MAX_EVENTS, sock.fd)) < 0 )
    {
        log_err("initial epoll for listen socket failure\n");
@@ -171,73 +194,98 @@
    /* g_signal.stop defined in proc.c, and will be set when catch stop signal */
    while( !g_signal.stop )
    {
        /* program will blocked here */
        rv = epoll_wait(epollfd, event_array, MAX_EVENTS, -1/* never timeout */);
        if( rv < 0 )
        /* epoll wailt and program will blocked here */
        nfds = epoll_wait(epollfd, event_array, MAX_EVENTS, -1 /* never timeout */);
        if( nfds <= 0 )
        {
            log_err("epoll failure: %s\n", strerror(errno));
            continue;
        }
        else if( rv == 0)
        {
            log_err("epoll get timeout\n");
            log_err("epoll_wait failure or timeout: %s\n", strerror(errno));
            continue;
        }
        /* rv>0 is the active events count */
        for(i=0; i<rv; i++)
        /* nfds>0 is the active events count */
        for(i=0; i<nfds; i++)
        {
            fd = event_array[i].data.fd;
            /* epoll get error  */
            if ( (event_array[i].events&EPOLLERR) || (event_array[i].events&EPOLLHUP) )
            {
                log_err("epoll_wait get error on fd[%d]: %s\n", event_array[i].data.fd, strerror(errno));
                epoll_del(epollfd, event_array[i].data.fd);
                close(event_array[i].data.fd);
                log_err("epoll_wait get error on fd[%d]: %s\n", fd, strerror(errno));
                if( fd != sock.fd )
                    term_socket_client(epollfd, event_array[i].data.fd, &buf_list);
                continue;
            }
            /* listen socket get event means new client start connect now */ 
            if( event_array[i].data.fd == sock.fd )
            if( fd == sock.fd )
            {
                struct epoll_event       event;
                /* accept new client socket  */
                if( (connfd=accept(sock.fd, (struct sockaddr *)NULL, NULL)) < 0)
                {
                    log_nrml("accept new client failure: %s\n", strerror(errno));
                    continue;
                }
                log_info("accept new client socket[%d]\n", connfd);
                event.data.fd = connfd;
                event.events =  EPOLLIN;
                epoll_ctl(epollfd, EPOLL_CTL_ADD, connfd, &event);
#if 0
                /* add new client socket into epoll  */
                if( epoll_add(epollfd, connfd) < 0 )
                {
                    close(connfd);
                    log_err("epoll add client socket failure, rv=%d\n", rv);
                }
                log_dbg("epoll add new client socket[%d] ok.\n", connfd);
#endif
                /* malloc client buffer and add it into list */
                if( NULL != (cli_buf=(cli_buf_t *)malloc(sizeof(*cli_buf))) )
                {
                    memset(cli_buf, 0, sizeof(cli_buf));
                    cli_buf->fd = 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_nrml("accept and add new client socket[%d] ok.\n", connfd);
            }
            /* already connected client socket get data arrive  */
            else 
            {
                rv=read(event_array[i].data.fd, buf, sizeof(buf));
                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_err("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 )
                {
                    printf("socket[%d] read failure [%s] or disconncet, close and remove now.\n",
                            event_array[i].data.fd, strerror(errno));
                    log_warn("socket[%d] read failure [%s] or disconncet, terminate it now.\n",
                            fd, strerror(errno));
                    epoll_del(epollfd, event_array[i].data.fd);
                    close( event_array[i].data.fd );
                    term_socket_client(epollfd, fd, &buf_list);
                    continue;
                }
                else
                {
                    log_dbg("socket[%d] receive %d bytes data\n", event_array[i].data.fd, rv);
                    logger_dump(LOG_LEVEL_INFO, buf, rv);
                    cli_buf->bytes += rv;
                    log_dbg("socket[%d] receive %d bytes data\n", fd, rv);
                }
                parser_tlvpack(cli_buf);
            }
        }
        sleep(1);
    }
    logger_term();
@@ -245,3 +293,131 @@
    return 0;
static int parser_tlvpack(cli_buf_t *cli_buf)
{
    int                     i;
    int                     left_bytes;
    unsigned short          crc16;
    tlv_pack_t             *tlv_pack;
    if( !cli_buf )
    {
        log_err("Invalid input arguments\n");
        return -1;
    }
    log_info("start to parser client buffer %d bytes data:\n", cli_buf->bytes);
    logger_dump(LOG_LEVEL_INFO, cli_buf->buf, cli_buf->bytes);
    if( cli_buf->bytes < TLV_MIN_SIZE )
    {
        log_warn("TLV packet bytes less than min. size\n");
        return -2;
    }
PARSER_AGAIN:
    for(i=0; i<cli_buf->bytes; i++)
    {
        if( PACK_HEADER == (unsigned char )cli_buf->buf[i] )
        {
            log_dbg("found TLV packet header on [%d]\n", i);
            left_bytes = cli_buf->bytes - i;
            if(left_bytes < TLV_MIN_SIZE)
            {
                log_warn("TLV packet bytes less than min. size\n");
                memmove(cli_buf->buf, &cli_buf->buf[i], left_bytes);
                cli_buf->bytes = left_bytes;
                break;
            }
            tlv_pack = (tlv_pack_t *) &cli_buf->buf[i];
            log_info("header: 0x%02x tag: 0x%02x len: 0x%02x\n", tlv_pack->header, tlv_pack->tag, tlv_pack->len);
            if(tlv_pack->len > left_bytes )
            {
                log_err("TLV packet not integrated, continue to receive left data\n");
                memmove(cli_buf->buf, &cli_buf->buf[i], left_bytes);
                cli_buf->bytes = left_bytes;
                break;
            }
            if( tlv_pack->len > TLV_MAX_SIZE )
            {
                log_err("TLV packet length more than max. length, maybe found wrong header?\n");
                /* skip current header */
                left_bytes -= 1;
                memmove(cli_buf->buf, &cli_buf->buf[i+1], left_bytes);
                cli_buf->bytes = left_bytes;
                goto PARSER_AGAIN;
            }
            crc16 = crc_itu_t(MAGIC_CRC, (unsigned char *)&cli_buf->buf[i], tlv_pack->len-2);
            if( crc16 != bytes_to_ushort((unsigned char *)&cli_buf->buf[i+tlv_pack->len-2], 2) )
            {
                log_err("TLV packet CRC check error, maybe found wrong header?\n");
                /* skip current header */
                left_bytes -= 1;
                memmove(cli_buf->buf, &cli_buf->buf[i+1], left_bytes);
                cli_buf->bytes = left_bytes;
                goto PARSER_AGAIN;
            }
            if( tlv_pack->tag == TAG_SN )
            {
                log_nrml("Found SN TLV data:\n");
                dump_buf(tlv_pack->data, tlv_pack->len-TLV_FIXED_SIZE);
            }
            else if( tlv_pack->tag == TAG_TEMP )
            {
                log_nrml("Found temperature TLV data:\n");
                dump_buf(tlv_pack->data, tlv_pack->len-TLV_FIXED_SIZE);
            }
            else if( tlv_pack->tag == TAG_TIME )
            {
                log_nrml("Found date time TLV data:\n");
                dump_buf(tlv_pack->data, tlv_pack->len-TLV_FIXED_SIZE);
            }
            left_bytes -= tlv_pack->len;
            memmove(cli_buf->buf, &cli_buf->buf[i+tlv_pack->len], left_bytes);
            cli_buf->bytes = left_bytes;
            log_info("left %d bytes data:\n", cli_buf->bytes);
            logger_dump(LOG_LEVEL_INFO, cli_buf->buf, cli_buf->bytes);
            goto PARSER_AGAIN;
        }
    }
    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 );
}