小項目之併發測試(客戶端)

本文對很多人來說是沒有一點技術可言的,僅僅是記錄了自己的學習過程.

要求:

輸入:iso_stress -c 5000  -I 500  -f iso.txt  -H 4.2.2.2 -P 8888

-c 5000就是創建5000個線程來連服務器
-I 就是發送的時間間隔爲500ms
-f 就是要發送的文件
-H 就是服務器IP地址
-P 指定端口號

然後根據-c指定的個數創建5000個線程,然後連到服務器上,當線程全部創建完成後就開始同時發送-f指定的文件過去,另外每一次發送文件統計一下,發送到接受數據包的時間


程序各個小模塊

命令解析模塊

   struct option       long_options[] =
    {
        {"thread", require_argument, NULL, 'c'},
        {"time", require_argument, NULL, 'I'},
        {"file",require_argument, NULL, 'f'},
        {"ipaddr", require_argument, NULL,'H'},
        {"port", require_argument, NULL, 'P'},
        {NULL, 0, NULL, 0}
    };


    while ((opt = getopt_long(argc, argv, "c:I:f:H:P:", long_options, NULL)) != -1)
    {
        switch (opt)
        {
            case 'c':
                thread_num = atoi(optarg);
                printf("thread num is [%d].\n", thread_num);
                break;

            case 'I':
                delay_time = atoi(optarg);
                printf("time is [%d].\n", delay_time);
                break;

            case 'f':
                send_file = optarg;
                printf("the send file is [%s].\n", send_file);
                break;

            case 'H':
                serv_ip = optarg;
                printf("the ipaddr is [%s].\n", serv_ip);
                break;

            case 'P': 
               serv_port = atoi(optarg);
                printf("the port is [%d].\n",serv_port);
                break;

            default:
                break;
        }
    }

初始化打開文件數限制

int network_init(void)
{
    struct rlimit rlt;

    rlt.rlim_max = rlt.rlim_cur = 5000;
    if (setrlimit(RLIMIT_NOFILE, &rlt) != 0)
    {
        printf("setrlimit failure: %s\n", strerror(errno));
        return -1;
    }

    return 0;
}

信號控制

void signal_handler(int signo)
{
    switch(signo)
    {
        case SIGTERM:
        case SIGINT:
            printf("program pid [%d] receive SIGINT/SIGTERM signal, exit program now\n", getpid());
            g_stop = 1;
            break;

        case SIGSEGV:
            printf("program pid [%d] SIGSEGV signal, exit program now\n", getpid());
            g_stop = 1;
            break;
            
        case SIGPIPE:
            printf("program pid [%d] SIGPIPE signal, exit program now\n", getpid());
            g_stop = 1;
            break;
            
        default:
            printf("program pid [%d] receive signal [%d]. \n", getpid(), signo);
            break;
    }       
            
    return;
}

void install_signal(void)
{
    struct sigaction sigact, sigin;
    printf("Install signal action\n");

    /* initialize the catch signal */
    sigemptyset(&sigact.sa_mask);
    sigact.sa_flags = 0;
    sigact.sa_handler = signal_handler;

    /* setup the ignore signal */
    sigemptyset(&sigin.sa_mask);
    sigin.sa_flags = 0;
    sigin.sa_handler = SIG_IGN;

    sigaction(SIGTERM, &sigact, 0); /*  catch terminate signal "kill" command */
    sigaction(SIGINT, &sigact, 0);  /*  catch interrupt signal CTRL+C */
    sigaction(SIGCHLD, &sigact, 0); /*  catch child process return */
    sigaction(SIGPIPE, &sigact, 0); /*  catch broken pipe */

    return;
}

取讀取文件長度

int get_file_size(const char *path_file)
{
    int          fd;
    struct stat  get_size_buf;

    if ((fd = open(path_file, O_RDONLY)) < 0)
    {
        printf("open the file and get file size  failure: %s\n", strerror(errno));
        return -1;
    }

    if (fstat(fd, &get_size_buf) != 0)
    {
        printf("get the file size failure: %s\n", strerror(errno));
        return -1;
    }

    close(fd);
    return get_size_buf.st_size;

}

獲取文件數據並轉換成十六進制數(要求)

void get_file_data(const char *path_file, unsigned char *get_data_buf, int file_size)
{
    assert(path_file != NULL);
    assert(get_data_buf != NULL);
    assert(&file_size != NULL);

    size_t          size;
    int             len;
    int             fd;
    int             i;
    char            buf[3];
    unsigned char   hex_buf[1024];


    memset(buf, 0, sizeof(buf));
    if ((fd = open(path_file, O_RDONLY)) < 0)
    {
        printf("open the file failure and read data failure: %s\n", strerror(errno));
        return ;
    }

    if ((size = read(fd, get_data_buf, file_size)) <= 0)
    {
        printf("read data from file failure: %s\n", strerror(errno));
        return ;
    }

    /* change sixteen string to ten */
    for (i=0; i<file_size/2; i++)
    {
        buf[0] = get_data_buf[i*2];
        buf[1] = get_data_buf[i*2+1];

        get_data_buf[i] = strtol(buf, NULL, 16);
    }

    close(fd);
    return ;
}

簡單的輸出函數

print_hex_string(char *buf, int len)
{   
    int     i;
    for (i=0; i<len; i++)
        printf("data[%d] = 0x%02X\n", i, buf[i]);
}

處理線程函數

void *thread_func(void *thread_arg)
{
    assert(thread_arg != NULL);

    int                     sockfd;
    int                     nsize;
    int                     rv = 0;
    int                     opt = 1;
    struct sockaddr_in      serv_addr;
    char                    buf[1024];
    int                     count;
    THREAD_ARG      *arg = (THREAD_ARG *)thread_arg;


    count = ++arg->thread_count;
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        printf("use socket failure: %s\n", strerror(errno));
        return NULL;
    }

    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(arg->port);
    if (inet_pton(AF_INET, arg->ipaddr, &serv_addr.sin_addr) < 0)
    {
        printf("inet_pton failure: %s\n", strerror(errno));
        return NULL;
    }

    if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(struct sockaddr)) < 0)
    {
        printf("connect to server failure: %s\n", strerror(errno));
        return NULL;
    }
    printf("the thread [%d] is connect to server.\n",count);

    /* set the flag, if flag is 1, send data */
    while (g_thread_start_flag != 1)
        ;

    while (!g_stop)
    {
        /* send data every 500ms */
        nanosleep(&(arg->delay_time), NULL);

        /* record time when send the data  */
        struct timeval      now;
        int                 rc;
        double              start_send, recv_end;

        if ((rc = gettimeofday(&now, NULL)) != 0)
        {
            printf("get the time when send data failure\n");
            continue;
        }
        start_send = now.tv_sec + (now.tv_usec / 1000000.0);

        /* send the data */
        nsize = arg->size;
        printf("thread [%d] send %d bytes data to server.\n",count,nsize);
        while(nsize > 0)
        {
            rv= write(sockfd, arg->data, nsize);
            if (rv < 0)
            {
                printf("write() to server failure: %s\n", strerror(errno));
                return NULL;
            }

            nsize -= rv;
        }

        if((nsize = read(sockfd, buf, sizeof(buf))) < 0)
        {
            printf("read failure: %s\n", strerror(errno));
            return NULL;
        }
        else
        {
            printf("thread [%d] receive [%d] bytes data from server.\n", count, nsize);
            print_hex_string(buf, nsize);
        }
       if ((rc = gettimeofday(&now, NULL)) != 0)
        {
            printf("get the time when received data failure\n");
        
        }   
        recv_end = now.tv_sec + (now.tv_usec / 1000000.0);
        printf("%.6lfs interval for send and receive.\n", recv_end - start_send);

    } /* while(1) */

    printf("thread [%d] exit now\n ", count);

    --arg->thread_count;
    close(sockfd);
    pthread_exit(NULL);
    return NULL;
}

主函數中一些初始化

    /* initialize time */
    slptime.tv_sec = 0;
    slptime.tv_nsec = (delay_time * 1000000); /* 500ms */ /* error */


    /* get file size and get file data */
    file_size = get_file_size(send_file);
    get_data_buf = (char *)malloc(sizeof(char) * (file_size + 1));
    get_file_data(send_file, get_data_buf, file_size);

    /* initialize thread_reg */
    thread_arg.num = thread_num;
    thread_arg.delay_time = slptime;
    thread_arg.data = get_data_buf;
    thread_arg.size = file_size/2;
    thread_arg.ipaddr = serv_ip;
    thread_arg.port = serv_port;

    /* intialize network  */
    network_init();

    for (i=0; i<thread_num; i++)
    {
        err = pthread_create(&tid[i], NULL, thread_func, (void *)&thread_arg);
        if (err != 0)
        {
            printf("the thread [%d] is create failure: %s\n", i+1, strerror(err));

        }
        printf("create [%d] thread successfully.\n", i+1);
    }
    g_thread_start_flag = 1;

    for(i=0; i<thread_num;i++)
    {
        err = pthread_join(tid[i], NULL);
        printf("can't get thread [%d] exit.\n", i);
    }

    return 0;

一個不大的程序,卻能從其中學到很多東西.比如:

(1) 多線程中儘量不要使用sleep() 因爲它是非線程安全函數

(2) 關於TCP socket中讀取字節需要注意的一些因素 http://www.360doc.com/content/11/0731/10/2127922_136887195.shtml

當然,我顯然沒按照上面的文章裏面說的去做...因爲要求的關係沒有用到.以後再借鑑

(3)進一步瞭解了多線程的編程,但是還需要更多的努力.

(4)懂得用GDB進行一些初級的調試.

發佈了59 篇原創文章 · 獲贊 10 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章