Webbench实现

// socket.cpp
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

/*
 * 函数作用:连接指定的主机
 */
extern int my_socket(const char *host, const int port){
    int sid = socket(AF_INET, SOCK_STREAM, 0);
    if (sid < 0){
        fprintf(stderr, "网络通信出现了问题。");
        return -1;  // 这用不用断言?
    }
    struct sockaddr_in addr;
    unsigned long inaddr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    inaddr = inet_addr(host);
    if (inaddr != INADDR_NONE){
        memcpy(&addr.sin_addr, &inaddr, sizeof(inaddr));
    } else{
        struct hostent *he;
        he = gethostbyname(host);
        if (he == NULL){
       //     fprintf(stderr, "主机名不正确!\n");
            return -1;
        }
        memcpy(&addr.sin_addr, he->h_addr, he->h_length);
    }
    addr.sin_port = htons(port);
    if (connect(sid, (struct sockaddr *)&addr, sizeof(addr)) < 0){
        return -1;
    }
    return sid;
}

// WebLimitTest.cpp
/*
 * 服务器压力测试1.0
 * 无代理服务器,只能发GET请求
 */
#include <unistd.h>
#include <getopt.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <signal.h>
#include "socket.h"
#include <iostream>
#define MAX_LENGTH 2048

using namespace std;
static struct option options_parameter[] = {
        {"ignore", no_argument, NULL, 'i'},
        {"clients", required_argument, NULL, 'c'},
        {"time", required_argument, NULL, 't'},
        {"method", optional_argument, NULL, 'm'},
        {"help", no_argument, NULL, 'h'}
};
bool isEnd = false;
int clients = 0;
unsigned int times = 0;
bool method = false; // 0代表GET 1代表POST 同下
bool ignore = false;
char *request;
char host[MAX_LENGTH];
int port;

// 据说可以用速度把成功次数算出来?
unsigned int speed = 0;
unsigned int bytes = 0;
unsigned int failed = 0;
/*
 * 函数作用:向屏幕打印使用指南
 */
void help();
/*
 * 函数作用:拼凑http请求
 * 返回1:异常退出
 * 返回0:分析成功
 */
int analyze_request();
/*
 * 函数作用:利用fork()不断生产子进程,向目标不断发起请求
 */
int test();
/*
 * 函数作用:发出请求并收集结果
 */
void transmit_request();
/*
 * 函数作用:关闭管道
 */
void mclose(int *f);

void alarm_handler(){
    isEnd = true;
}

/*
 * 主函数,返回0代表正常退出,返回2代表参数有误或寻求帮助,返回3代表异常退出
 */
int main(int argc, char **argv){
    int opt = 0;
    int option_index = 0;
    bool isImmediateEnd = false;


    while ((opt = getopt_long(argc, argv, "m:c:t:ih", options_parameter, &option_index)) != EOF){

        switch(opt){
            case 'm':
                if (strcmp(optarg, "1") == 0){
                    method = true;
                }else if (strcmp(optarg, "0") == 0){
                    method = false;
                }else{
                    isImmediateEnd = true;
                }
                break;
            case 'c':
                if ((clients = atoi(optarg)) == 0){
                    isImmediateEnd = true;
                }else if(clients <= 0){
                    isImmediateEnd = true;
                }
                break;
            case 't':
                if ((times = atoi(optarg)) == 0){
                    isImmediateEnd = true;
                }else if(times <= 0){
                    isImmediateEnd = true;
                }
                break;
            case 'i':
                ignore = true;
                break;
            default:
                isImmediateEnd = true;

        }
    }
    if (isImmediateEnd){
        help();
        return 1;
    }
    if (strlen(argv[optind]) > MAX_LENGTH || argv[optind] == NULL){
        fprintf(stderr, "请求地址过长,它不应该超过1000。");
    }
    request = argv[optind];

    if (analyze_request() == -1){
        return 3;
    }

    if (test() == -1){
        return 3;
    }
    return 0;
}

void help(){
    fprintf(stderr, "使用帮助(访问地址请添加在所有参数的最后):\n"
            "-i 忽略服务器回复\n"
            "-c 设置客户端数量\n"
            "-m 设置http请求方法,默认GET,1代表POST,0代表GET\n"
            "-t 设置客户端的请求时间(秒),每个客户端会在时间内不断地发起请求\n"
            "-h 使用帮助\n");
}

/*
 * 函数作用:判断s1是否以s2开头,是返回s2长度带'\0',不是返回0
 */
int strstrbeg(const char *s1, const char *s2){
    int i = 0;
    char a,b;
    while((a = s2[i]) != '\0'){
        b = s1[i];
        if (a != b){
            return 0;
        }
        i++;
    }
    return i;
}
/*
 * 函数作用:取子串,这里并没有保证begin,c一定合法
 */
char *substring(const char *s, int begin, const char c){
    int i = 0;
    char res[MAX_LENGTH], a;
    begin--;
    while ((a=s[begin++]) != c){
        if (a == '\0'){
            break;
        }
        res[i++] = a;
    }
    res[i] = '\0';
    return res;
}
/*
 * 函数作用:取子串,这里并没有保证begin,end一定合法
 */
char *strsub(const char *s, size_t begin, size_t end){
    if (begin > end){
        return NULL;
    }
    int i = 0;
    char res[MAX_LENGTH];
    begin--;
    end--;
    while (begin <= end){
        res[i++] = s[begin];
        begin++;
    }
    return res;
}

/*
 * 临时加出来的函数,因为my_socket直接传指针行不通,读不出来很奇怪
 */
void point_array(char *h){
    int l = strlen(h);
    for (int i = 0; i < l; ++i) {
        host[i] = h[i];
    }
}
int analyze_request(){
    char *req = request;
    if (strstrbeg(req, "http://") == 0){
        fprintf(stderr, "只支持HTTP协议\n");
        return -1;
    }
    // 截取主机地址
    char *hp = substring(req, 8, '/');
    // 默认端口为80
    if (strchr(hp, ':') == NULL){
        port = 80;
        point_array(hp);
    }else{
        char *h = strtok(hp, ":");
        point_array(h);
        port = atoi(strtok(NULL, ":"));
    }
    if (port == 0){
        fprintf(stderr, "非法的服务器地址");
        return -1;
    }

    fprintf(stderr, "%s, %d\n", host, port);
    //测试主机能否连通
    int sid = my_socket(host, 80);
    if (sid < 0){
        fprintf(stderr, "通信异常。\n");
        return -1;
    }
    close(sid);

    //构造http请求
    size_t hp_len = strlen(hp);
    char *request_detail = strsub(request, 7+hp_len+1, strlen(request));
    memset(request, 0, sizeof(request));
    request[0] = '\0';
    if (method){
        strcat(request, "POST ");
    }else{
        strcat(request, "GET ");
    }
    if (request_detail == NULL){
        strcat(request, request_detail);
    } else{
        strcat(request, "/");
    }
    strcat(request, " HTTP/1.1\n");
    strcat(request, "Host: ");
    strcat(request, host);
    strcat(request, "\nUser-Agent: WebLimitTest/1.0\n");
    strcat(request, "Connection: close\n");
    strcat(request, "Cache-Control: no-cache\n\n");
    strcat(request, "\0");
    fprintf(stderr, request);
    return 0;
}

int test(){
    //建立管道
    int fd[2];
    FILE *file;
    if (pipe(fd) < 0){
        fprintf(stderr, "pipe failed");
        return -1;
    }

    int pid;
    for (int i = 0; i < clients; ++i) {
        pid = fork();
        if (pid < 0){
            fprintf(stderr, "fork failed.");
            return -1;
        } else if(pid == 0){
            //子进程
            sleep(1);  //这句会明显增加子进程的速度,为什么?
            break;
        }
    }
    if (pid < (pid_t)0){
        return  3;
    }
    if (pid == (pid_t)0){
        transmit_request(); // 这个函数拿到循环里做会拖累效率,循环体必须尽可能的小
        file = fdopen(fd[1], "w");
        if (file == NULL){
            fprintf(stderr, "pipe failed");
            mclose(fd);
            return -1;
        }
        fprintf(file, "%d %d %d\n", failed, speed, bytes);
        fclose(file);
        return 0;
    }
    // 父进程
    else {
        int a=0,b=0,c=0;
        file = fdopen(fd[0],"r");
        if (file == NULL){
            mclose(fd);
            return  -1;
        }
        setvbuf(file,NULL,  _IONBF, 0);
        while(1){
     //       fprintf(stderr, "wo ka zai zhe le 7");
            pid = fscanf(file, "%d%d%d", &a, &b, &c);
            if (pid < 2){
                fprintf(stderr, "some children died.");
                break;
            }
            failed += a;
            speed += b;
            bytes += c;
            cout<<clients<<"号进程结束,失败次数="<<a<<",总次数="<<b<<",数据量="<<c<<endl;
            if (--clients == 0) break;
        }
        fclose(file);
        fprintf(stderr, "总次数=%d\n成功次数=%d\n"
                "失败次数=%d\n"
                "平均每秒请求数=%.2f\n"
                "平均每秒发送字节=%.2f\n", speed, speed-failed, failed, (float)speed/(float)times, (float)bytes/(float)times/8.0);
    }
    mclose(fd);
}

void mclose(int *fd){
    close(fd[0]);
    close(fd[1]);
}
void transmit_request(){

    ssize_t f;
    int sock = 0;
    char buffer[MAX_LENGTH] = {0};
    //设置信号
    signal(SIGALRM, (__sighandler_t)alarm_handler);
    //设置闹钟 秒为单位
    alarm(times);

    while(1){
        if (isEnd){
            break;
        }
        if ((sock = my_socket(host, port)) < 0){
            failed++;
            continue;
        }
        if (write(sock, request, strlen(request)) < 0){
            failed++;
            close(sock);
            continue;
        }
        if (!ignore){
            // 读取响应
            while(1){
                if(isEnd) break; // 这句话存在  效率提升近一倍!
                f = read(sock, buffer, MAX_LENGTH);
                if (f == 0){
                    break;
                }
                if (f < 0){
                    failed++;
                    break;
                }
                bytes += strlen(buffer);
            }
        }
        close(sock);
        speed++;
    }
}

实现总结:
俗话说的真好,站着说话不腰疼。
看的再懂都没有写的好。
1.OS层次的网络通信。
2.sleep()可以加快子进程运行,so why?
3.I/O读写真的很慢,整个程序的瓶颈就在于I/O。
4.循环体越精简越好,真的会拖累效率。
5.gethostbyname()直接传字符指针会出现非常奇怪的问题,会乱码。

猜你喜欢

转载自blog.csdn.net/nvnnv/article/details/77149286