webbench剖析

2023-10-30

webbench:其为linux上一款web性能压力测试工具,它最多可以模拟3万个并发连接数来测试服务器压力,其原理为fork多个子进程,每个子进程都循环做web访问测试,子进程将访问的结果通过管道告诉父进程,父进程做最终结果统计。

其主要原理如下图:
这里写图片描述

其代码实现中主要运用4个函数:getopt_long()系统命令行解析函数,build_request()函数,bench()函数,benchcore()函数。

流程如下:
webbench.c:
全局变量选项参数:都有默认值
1.命令行参数解析,参数构造(getopt_long()函数实现)(若用户没有传
参设定则为其默认值);

2.根据1得到的一些参数,进行http请求构造,调用build_request()函数:
(1)构造第一行:
请求方法:method–>request[]

url:判断url合法性,合法:
无代理服务器:
将其网络地址–>host[],端口号–>proxyport;
url资源路径–>request[]
有代理服务器:
url直接填入–>request[];

http协议版本:http0.9 http1.0 http1.1

(2)请求报头:Name:Value–>request[]
(3)填入空行–>request[]
构造完成

3.进行压力测试,调用bench()函数:
(1)进行一次连接合法性测试;
(2)建立管道通信;
(3)按照客户端数目派生子进程;
(4)每个子进程调用benchcore()函数进行连接请求:

benchcore函数:
创建自定义信号捕捉函数,建立benchtime时间闹钟:
(1)调用Socket(),得到连接套接字;
(2)向网络中写请求;
(3)http0.9协议处理(关闭连接写一半);
(4)读取服务器响应消息;
(5)关闭连接;
在以上过程中统计各自进程的faild(失败次数)、bytes(服务器回应字节 数)、speed(连接成功次数);

(5)benchcore函数调用完成,每个进程向管道中写入faild、bytes、speed;
(6)父进程打开管道读取每个进程写入的信息,进行统计计算,输出压力测试结果。

Socket.c:
1.建立套接字:调用socket();
2.建立连接:调用connect();
返回套接字:sock.

源码如下:
webbench.c

#include "socket.c"  
#include <unistd.h>  
#include <sys/param.h>  
#include <rpc/types.h>  
#include <getopt.h>  
#include <strings.h>  
#include <time.h>  
#include <signal.h> 

//统计的压力测试最终结果表示
volatile int timerexpired = 0;  
int speed = 0;  
int failed = 0;  
int bytes = 0;

//http请求方法
#define METHOD_GET 0  
#define METHOD_HEAD 1  
#define METHOD_OPTIONS 2  
#define METHOD_TRACE 3  

#define PROGRAM_VERSION "1.5"  

// 默认设置:一般需用户自己传入命令行参数设置  
int method = METHOD_GET;    //默认请求方法为GET方式   
int clients = 1;            //默认只模拟一个客户端  
int force = 0;              //默认需要等待服务器响应
int force_reload = 0;       //失败时重新请求   
int proxyport = 80;         //默认访问服务器端口为80 
char *proxyhost = NULL;     //默认无代理服务器  
int benchtime = 30;         //默认模拟请求时间为30s  

// globals 版本号 
int http10 = 1;   //0:- http/0.9, 1:- http/1.0, 2:- http/1.1 


int mypipe[2];             //管道用于父子进程通信
char host[MAXHOSTNAMELEN]; //存储服务器网络地址  
#define REQUEST_SIZE 2048  
char request[REQUEST_SIZE]; //存放http请求报文信息数组

//函数声明 
static void benchcore(const char* host,const int port, const char *request);  
static int bench(void);  
static void build_request(const char *url); 

// 用法与各参数详细含义 
static void usage(void)  
{  
   fprintf(stderr,  
    "webbench [option]... URL\n"       //用法
    "  -f|--force               Don't wait for reply from server.\n"  
    "  -r|--reload              Send reload request - Pragma: no-cache.\n"  
    "  -t|--time <sec>          Run benchmark for <sec> seconds. Default 30.\n"  
    "  -p|--proxy <server:port> Use proxy server for request.\n"  
    "  -c|--clients <n>         Run <n> HTTP clients at once. Default one.\n"  
    "  -9|--http09              Use HTTP/0.9 style requests.\n"  
    "  -1|--http10              Use HTTP/1.0 protocol.\n"  
    "  -2|--http11              Use HTTP/1.1 protocol.\n"  
    "  --get                    Use GET request method.\n"  
    "  --head                   Use HEAD request method.\n"  
    "  --options                Use OPTIONS request method.\n"  
    "  --trace                  Use TRACE request method.\n"  
    "  -?|-h|--help             This information.\n"  
    "  -V|--version             Display program version.\n"  
    );  
};  

//结构体数组:每一个元素格式为:{长选项,选项后是否带有参数,int*指针(为NULL),对应短选项或
static const struct option long_options[]=                  //不为NULL,将第四个参数值给第三个参数} 
{  
    {"force",no_argument,&force,1},  
    {"reload",no_argument,&force_reload,1},  
    {"time",required_argument,NULL,'t'},  
    {"help",no_argument,NULL,'?'},  
    {"http09",no_argument,NULL,'9'},  
    {"http10",no_argument,NULL,'1'},  
    {"http11",no_argument,NULL,'2'},  
    {"get",no_argument,&method,METHOD_GET},    
    {"head",no_argument,&method,METHOD_HEAD},  
    {"options",no_argument,&method,METHOD_OPTIONS},  
    {"trace",no_argument,&method,METHOD_TRACE},  
    {"version",no_argument,NULL,'V'},  
    {"proxy",required_argument,NULL,'p'},  
    {"clients",required_argument,NULL,'c'},  
    {NULL,0,NULL,0}  
};  

int main(int argc, char *argv[])  
{  
    int opt = 0;  
    int options_index = 0;  
    char *tmp = NULL;  

    //一、检验命令行参数
    //1.不带选项时直接输出用法help信息
    if(argc == 1)  
    {  
        usage();  
        return 2;  
    }  

    //2.带选项时则解析命令行参数并根据传入选项进行相关设置

    // getopt_long 为命令行解析的库函数,根据argc来寻找(argv,"912Vfrt:p:c:?h")这两个字符串匹配的选项,
    //如果是短选项,则直接返回这个选项给opt,
    //如果是长选项,则到option long_options[]结构体数组中寻找匹配其长选项,返回其对应的短选项给opt,
    //若其第三个参数不为NULL,将第四个参数值给第三个参数,并且返回0给opt

    //此函数自带全局变量:
    //optarg:指向选项后的参数:-t 100,指向100;
    //optind: 当前访问到的argv索引值
    //opterr: 其值非0时,代表有无效选项,缺少参数,输出错误信息
    //optopt: 发现无效选项时,函数返回“? / :”,将其值设为无效选项字符
    while((opt = getopt_long(argc,argv,"912Vfrt:p:c:?h",long_options/*结构体数组指针*/,&options_index)) != EOF )  
    {   
        switch(opt)    //根据返回值判断用户传入的参数进行相关设置
        {  
            case  0 : break;  
            case 'f': force = 1;break;        //force=1代表不等待服务器响应 
            case 'r': force_reload = 1;break; //发送重新加载请求 

            case '9': http10 = 0;break;  
            case '1': http10 = 1;break;  
            case '2': http10 = 2;break;  
            case 'V':  
                      printf(PROGRAM_VERSION"\n");  
                      exit(0);  

            case 't':  
                      benchtime = atoi(optarg);   //设置用户传入的运行时间 
                      break;  
            case 'c': 
                      clients = atoi(optarg);     //设置创建的客户端数
                      break;  

            case 'p':  
                      //使用代理服务器,设置其代理网络号和端口号:格式:-p server:port 
                      tmp = strrchr(optarg,':');   //查找“:”在optarg中最后一次出现的位置    
                      proxyhost = optarg;       //设置网络号

                      if(tmp == NULL)  //没有:号,没有端口号
                      {  
                          break;  
                      }  
                      if(tmp == optarg)    //端口号在首位置,错误:缺失主机名
                      {  
                          fprintf(stderr,"Error in option --proxy %s: Missing hostname.\n",optarg);  
                          return 2;  
                      }  
                      if(tmp == optarg + strlen(optarg)-1) //:号在末位,缺少端口号  
                      {  
                          fprintf(stderr,"Error in option --proxy %s Port number is missing.\n",optarg);  
                          return 2;  
                      }  

                      *tmp = '\0';  //将:号置为“\0”

                      proxyport = atoi(tmp+1);  //设置新的端口号 

                      break;  

            case ':':  
            case 'h':  
            case '?': usage();return 2;break; 
        }  
    } 

    //getopt_long函数将选项解析完成后,读到url不会在读取,此时argv[optind]指向url
    //optind 被 getopt_long设置为命令行参数中未读取的下一个元素下标值 

    if(optind == argc) //若相等即没有输入URL 
    {  
        fprintf(stderr,"webbench: Missing URL!\n");  
        usage();  
        return 2;  
    }  

    //若客户端选项后参数设为0,则更改 
    if(clients == 0) 
        clients = 1;  
    if(benchtime == 0) 
        benchtime = 60;  

    //输出webbench版本相关信息  
    fprintf(stderr,"Webbench - Simple Web Benchmark "PROGRAM_VERSION"\n"  
     "Copyright (c) Radim Kolar 1997-2004, GPL Open Source Software.\n"  
     );  

    //二、构造HTTP请求到request数组
    build_request(argv[optind]);   //传入URL

    //http请求构造成功后
    //以下输出提示信息  
    printf("\nBenchmarking: ");    //测压开始

    switch(method)     //用的请求方法
    {  
        case METHOD_GET:  
        default:  
            printf("GET");break;  
        case METHOD_OPTIONS:  
            printf("OPTIONS");break;  
        case METHOD_HEAD:  
            printf("HEAD");break;  
        case METHOD_TRACE:  
            printf("TRACE");break;  
    }  

    printf(" %s",argv[optind]);   //访问的url 

    switch(http10)    //http协议版本号
    {  
        case 0: printf(" (using HTTP/0.9)");break;  
        case 2: printf(" (using HTTP/1.1)");break;  
    }  

    printf("\n");  

    //模拟连接客户端数目
    if(clients == 1) printf("1 client");  
    else  
        printf("%d clients",clients);  

    //连接测试的时间
    printf(", running %d sec", benchtime); 

    if(force) 
        printf(", early socket close");

    //输出代理服务器的信息    
    if(proxyhost != NULL) 
        printf(", via proxy server %s:%d",proxyhost,proxyport); 

    if(force_reload) 
        printf(", forcing reload");  
    printf(".\n");  

    //开始压力测试,返回 bench 函数执行结果  
    return bench();  
}  

//二、构造HTTP请求到request数组
void build_request(const char *url)  
{  
    char tmp[10];  
    int i;  

    //初始化 
    bzero(host,MAXHOSTNAMELEN);  
    bzero(request,REQUEST_SIZE);  

    //判断应该使用的 HTTP 协议  
    if(force_reload && proxyhost != NULL && http10 < 1) 
        http10 = 1;  
    if(method == METHOD_HEAD && http10 < 1) 
        http10 = 1;  
    if(method == METHOD_OPTIONS && http10 < 2) 
        http10 = 2;  
    if(method == METHOD_TRACE && http10 < 2) 
        http10 = 2;  

    //1.填写http请求第一行
    //填写请求方法method
    switch(method)  
    {  
        default:  
        case METHOD_GET: strcpy(request,"GET");break;  
        case METHOD_HEAD: strcpy(request,"HEAD");break;  
        case METHOD_OPTIONS: strcpy(request,"OPTIONS");break;  
        case METHOD_TRACE: strcpy(request,"TRACE");break;  
    }  

    strcat(request," ");  

    //URL 合法性判断  
    //若没有"://"则不合法
    if(NULL == strstr(url,"://"))
    {  
        fprintf(stderr, "\n%s: is not a valid URL.\n",url);  
        exit(2);  
    }  

    //若url过长非法
    if(strlen(url)>1500)  
    {  
        fprintf(stderr,"URL is too long.\n");  
        exit(2);  
    }  

    if(proxyhost == NULL)   //若无代理服务器 
    {       
        if(0 != strncasecmp("http://",url,7)) //忽略大小写比较 
        {  
            //只支持 HTTP 地址  
            fprintf(stderr,"\nOnly HTTP protocol is directly supported, set --proxy for others.\n");  
            exit(2);  
        } 
    }       

    //找到主机名开始的地方:如:http://baidu.com:80/  
    i = strstr(url,"://")-url+3;   //i==7

    // 必须以 / 结束  
    if(strchr(url+i,'/')==NULL) //在字符串中寻找“/” ,找不到则非法URL
    {  
        fprintf(stderr,"\nInvalid URL syntax - hostname don't ends with '/'.\n");  
        exit(2);  
    }  

    if(proxyhost == NULL)  //若无代理服务器 
    {  
        // 得到端口号从主机名
        if(index(url+i,':') != NULL && index(url+i,':') < index(url+i,'/'))  //若带有端口号,index函数与strchr相似
        {  
            //设置网络号
            strncpy(host,url+i,strchr(url+i,':')-url-i);    //如将baidu.com拷贝到host数组里即网络地址

            //初始化 
            bzero(tmp,10);  
            strncpy(tmp,index(url+i,':')+1,strchr(url+i,'/')-index(url+i,':')-1); //将端口号拷贝到tmp数组中 

            //设置端口  
            proxyport = atoi(tmp);  
            if(proxyport==0) 
                proxyport=80;  

        } 
        else   //没有端口号,直接拷贝域名到host数组中
        {  
            strncpy(host,url+i,strcspn(url+i,"/"));  //strcspn找url+i到“/”之间的字符个数 
        }    

        //将资源路径填入请求行里
        strcat(request+strlen(request),url+i+strcspn(url+i,"/")); 

    } 
    else    //若有代理服务器
    {  
         strcat(request,url);  //直接填入URL到请求行中
    }  

    //填入http版本号到请求行中
    if(http10 == 1)  
        strcat(request," HTTP/1.0");  
    else if (http10==2)  
        strcat(request," HTTP/1.1");  
    strcat(request,"\r\n");  

    //2.填请求报头:NAME:VALUE
    if(http10 > 0)  
        strcat(request,"User-Agent: WebBench "PROGRAM_VERSION"\r\n");  

    if(proxyhost == NULL && http10 > 0)  
    {  
        strcat(request,"Host: ");  
        strcat(request,host);  
        strcat(request,"\r\n");  
    }  

    if(force_reload && proxyhost != NULL)  
    {  
        strcat(request,"Pragma: no-cache\r\n");  
    }  

    if(http10 > 1)  
        strcat(request,"Connection: close\r\n");  

    //3.填入空行  
    if(http10>0) 
        strcat(request,"\r\n");  

    //构造完成
} 

static int bench(void)   //父进程做的工作
{  
    int i,j,k;  
    pid_t pid = 0;  
    FILE *f;  

    //建立网络连接 :先测试一次,服务器是否可以正常连接成功   
    i = Socket(proxyhost == NULL ? host:proxyhost, proxyport);  

    if(i < 0)
    {  
        fprintf(stderr,"\nConnect to server failed. Aborting benchmark.\n");  
        return 1;  
    }  

    close(i);   //测试成功,一次连接完成关闭

    //建立管道通信  
    if(pipe(mypipe))  
    {  
        perror("pipe failed.");  
        return 3;  
    }  

    //派生子进程进行压力测试 :传入多少个客户端则建立多少个子进程进行连接 
    for(i = 0;i < clients;i++)  
    {  
        pid = fork();  
        if(pid <= (pid_t)0)  
        {  
            sleep(1);
            break;     //使子进程立刻跳出循环,要不就子进程继续 fork 了
        }  
    } 

    //子进程创建失败
    if( pid < (pid_t)0)   
    {  
        fprintf(stderr,"problems forking worker no. %d\n",i);  
        perror("fork failed.");  
        return 3;  
    } 

    //子进程执行
    if(pid == (pid_t)0)   
    {  
        //子进程发出实际请求   
        if(proxyhost == NULL)  
            benchcore(host,proxyport,request);  
        else  
            benchcore(proxyhost,proxyport,request);  

        // 打开管道写:连接请求状态的信息 
        f = fdopen(mypipe[1],"w"); //将文件描述符转换为文件指针 
        if(f == NULL)  
        {  
            perror("open pipe for writing failed.");  
            return 3;  
        }  

        //写入f文件中此进程在一定时间中请求成功的次数,失败的次数,读取服务器回复的总字节数  
        fprintf(f,"%d %d %d\n",speed,failed,bytes);  
        fclose(f);  

        return 0;  

    } 
    else {  
        //父进程打开管道读   
        f = fdopen(mypipe[0],"r");  
        if(f == NULL)  
        {  
            perror("open pipe for reading failed.");  
            return 3;  
        }  

        setvbuf(f,NULL,_IONBF,0);  //设置f的缓冲区为无缓冲区
        speed = 0;   //连接成功总次数    
        failed = 0;  //失败请求数  
        bytes = 0;   //传输字节数 

        while(1)  
        {  
            pid = fscanf(f,"%d %d %d",&i,&j,&k);  
            if(pid<2)  
            {  
                fprintf(stderr,"Some of our childrens died.\n");  
                break;  
            } 

            speed += i;   //连接成功总次数 
            failed += j;  //连接失败总次数
            bytes += k;   //传输总字节数

            //子进程是否读取完  
            if(--clients == 0) 
                break;  
        }  
        fclose(f);  

        //统计结果计算 
        printf("\nSpeed=%d pages/min, %d bytes/sec.\nRequests: %d susceed, %d failed.\n",  
                (int)((speed+failed)/(benchtime/60.0f)),   //总连接次数/总时间=每分钟请求连接次数   
                (int)(bytes/(float)benchtime),     //每秒传输字节数
                speed,                             //连接成功次数
                failed);                           //连接失败次数
    }  

    return i;  
}  


//信号处理函数 
static void alarm_handler(int signal)  
{  
    timerexpired = 1;  
}  

//子进程处理发起请求
void benchcore(const char *host,const int port,const char *req)  
{  
    int rlen;  
    char buf[1500];  
    int s,i;  
    struct sigaction sa;  


    //安装信号
    sa.sa_handler = alarm_handler;  
    sa.sa_flags = 0;  
    if(sigaction(SIGALRM,&sa,NULL))  
        exit(3);  

    //设置闹钟函数 
    alarm(benchtime);  

    rlen = strlen(req);  

nexttry:  
    while(1){  
        //收到信号则使 timerexpired = 1   
        if(timerexpired)  
        {  
            if(failed > 0)  
            {  
                failed--;  
            }  
            return;  
        }  
        //建立 socket连接, 进行 HTTP 请求   
        s = Socket(host,port);  
        if(s < 0)  
        {  
            failed++;  //连接失败则++ 
            continue;  
        }  
        if(rlen!=write(s,req,rlen))  
        {  
            failed++;  //写失败++
            close(s);  
            continue;  
        }  
        //HTTP 0.9 的处理 
        if(http10==0)  
            // 如果关闭不成功   
            if(shutdown(s,1)) //关闭连接写一半 
            {  
                failed++;  
                close(s);  
                continue;  
            }  

        // -f 选项时未设置时等待读取服务器回复   
        if(force == 0)  
        {  
            while(1)  
            {  
                if(timerexpired)
                    break;  
                i = read(s,buf,1500);  
                if(i<0)  
                {  
                    failed++;     //读失败++
                    close(s);  
                    goto nexttry;  
                }  
                else  
                    if(i == 0)   //读完退出
                        break;  
                    else 
                        bytes+=i;  //统计服务器回复的字节数 
            }  
        }  
        if(close(s))  
        {  
            failed++;  //关闭失败++
            continue;  
        }  

        speed++;   //成功连接一次++一次
    }  
}     

Socket.c

#include <sys/types.h>  
#include <sys/socket.h>  
#include <fcntl.h>  
#include <netinet/in.h>  
#include <arpa/inet.h>  
#include <netdb.h>  
#include <sys/time.h>  
#include <string.h>  
#include <unistd.h>  
#include <stdio.h>  
#include <stdlib.h>  
#include <stdarg.h>  

int Socket(const char *host, int clientPort)  
{  
    int sock;  
    unsigned long inaddr;  
    struct sockaddr_in ad;  
    struct hostent *hp;  

    // 初始化地址 
    memset(&ad, 0, sizeof(ad));  
    ad.sin_family = AF_INET;  

    // 尝试把主机名转化为数字  
    inaddr = inet_addr(host);  
    if (inaddr != INADDR_NONE)  
        memcpy(&ad.sin_addr, &inaddr, sizeof(inaddr));  
    else  
    {  
        // 取得 ip 地址  
        hp = gethostbyname(host);    //得到主机的二进制Ip给hp->h_addr 
        if (hp == NULL)  
            return -1;  
        memcpy(&ad.sin_addr, hp->h_addr, hp->h_length);  
    }  
    ad.sin_port = htons(clientPort);  

    // 建立 socket  
    sock = socket(AF_INET, SOCK_STREAM, 0);  
    if (sock < 0)  
        return sock;  
    // 建立链接   
    if (connect(sock, (struct sockaddr *)&ad, sizeof(ad)) < 0)  
        return -1; 

    return sock;  
}  

webbench参数含义如下:
这里写图片描述

在centos 6.5 测试使用结果如下:
这里写图片描述

即以上模拟50个客户端在30秒期间并发请求百度,结果如下:
每分钟平均有1708次请求连接,服务器每秒传输字节为3260321,在30秒期间请求连接成功为847次,失败7次。

其中webbench的优点为:
1.部署简单,适用于小型网站压力测试,(最多可模拟3万并发);
2.它具有静态页面测试能力也支持动态页面(ASP,PHP,JAVA,CGI)进行测试能力;
3.支持对含有SSL的安全网站如电子商务网站进行动态或静态性能测试;

缺点为:
1.不适合中大型网站测试;
2.其并发采用多进程实现并非线程,长时间其会大量占用内存与CPU,所以一般长时间的压力测试不推荐使用webbench.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

webbench剖析 的相关文章

随机推荐

  • java程序设计项目案例化教程题库及答案

    章节自测 1 第01章 走进Java的世界 1 1 填空题 1 Java源程序文件的后缀是 java Java字节码文件的后缀名称是 class 2 Java程序实现可移值性 依靠的是 JVM 3 Java语言的三个分支是 JAVA SE
  • 使用OpenCV实现WebCam摄像头保存JPEG图片(改良1版)

    http blog sina com cn s blog 3e4774e30100frwi html 本次的代码 OpenCV是1 0的 从上次实现从电脑上的摄像头捕捉视频并一秒保存一张图片的效果出发 增加了可以修改保存的图片的像素功能 代
  • 怎么制作睡袋rust_unturned睡袋怎么做 unturned睡袋合成方法介绍

    自从上个月初unturned游戏发布以来 受到了大批网友的追逐 甚至成为了steam上排名第三的游戏 在游戏中玩家虽然能够无限重生 但是重生的地方却是随机的 有时甚至会随机重生到僵尸群中 而unturned睡袋却可以帮助玩家固定重生点 那u
  • springboot点餐微信小程序系统毕业设计源码221144

    springboot点餐微信小程序 摘 要 点餐微信小程序采用B S模式 采用JAVA语言 springboot框架 mysql数据库 小程序框架uniapp等开工具 促进了点餐微信小程序的业务发展 与传统线下点餐相比 点餐维信小程序不但节
  • Python将图像转成像素风,圆圈、线条、波浪、十字绣、乐高积木、我的世界积木、回形针、字母......

    Python将图像转成像素风 圆圈 线条 波浪 十字绣 乐高积木 我的世界积木 回形针 字母 1 效果图 2 原理 3 源码 参考 1 效果图 回形针效果图如下 十字绣效果图如下 水平线效果图如下 垂直线效果图如下
  • 云原生之使用docker部署NTP时间服务器

    云原生之使用docker部署NTP时间服务器 一 chrony介绍 二 容器镜像介绍 三 检查本地docker状态 四 下载ntp镜像 五 部署ntp服务器 1 创建ntp容器 2 查看ntp容器状态 六 检查ntp服务器的时间源 七 客户
  • 增量训练lightgbm模型,深度学习模型

    1 机器学习 增量训练方法 机器学习 增量训练方法 知乎 包含 sklearn lightgbm增量训练方法 2 深度学习模型增量训练 增量训练主要面临的问题 当增量训练时 主要解决的是新增加的训练样本中的新词问题 如果对新增加的新词不做i
  • Vue之父子组件通信(一)

    1 父组件向子组件传递数据 父组件向子组件传值 1 父组件调用子组件的时候 绑定动态属性
  • ST-Bluenrg-lp芯片编程因为地址重叠导致常量值被更改

    所遇问题 定义的结构体 用于限制范围大小 类似于 struct test SysParaMax test1 5000 test2 5000 test3 100 test4 600 struct test SysParaMin test1 0
  • intellij idea tomcat permGen space

    vmsettings options are Xms128m Xmx700m XX MaxPermSize 250m XX ReservedCodeCacheSize 64m tomcat are Xms64m Xmx256m
  • cin读取数字时遇到字符的情况

    cin读取数字时遇到字符 当定义一个int变量 用cin输入时 如果输入的是一个字符 会发生以下4中情况 1 n的值变成0 2 不匹配的输入被留在输入流中 3 cin对象的一个错误标记被设置 即cin fail 返回true 4 对cin的
  • SpringBoot项目用 jQuery webcam plugin实现调用摄像头拍照并保存图片

    参考博客 http www voidcn com article p oigngyvb kv html 自定义样式
  • TestNG测试用例

    使用TestNG的第一个测试用例 要遵循的步骤 1 按Ctrl N 在TestNG类别下选择 TestNG Class 然后单击Next 要么 右键单击Test Case文件夹 转到TestNG并选择 TestNG Class 2 如果您的
  • 考研/面试 数据结构大题必会代码(理解+记忆,实现使用C++,STL库)

    文章目录 一 线性表 1 逆置顺序表所有元素 2 删除线性链表中数据域为 item 的所有结点 3 逆转线性链表 递归 快速解题 非递归 4 复制线性链表 递归 5 将两个按值有序排列的非空线性链表合并为一个按值有序的线性链表 二 树 1
  • 门面模式

    门面模式是对象的结构模式 外部与一个子系统的通信必须通过一个统一的门面对象进行 门面模式提供一个高层次的接口 使得子系统更易于使用 门面模式有三个角色组成 1 门面角色 facade 这是门面模式的核心 它被客户角色调用 因此它熟悉子系统的
  • DVWA靶场--文件上传/包含(low-high).

    文件上传 low 没有做任何过滤直接上传即可 medium 源码 uploaded type image jpeg uploaded type image png 这段源码可以看出来他对上传到content type值做了过滤 只允许上传这
  • 分享如何建立一个完美的 Python 项目

    当开始一个新的 Python 项目时 大家很容易一头扎进去就开始编码 其实花一点时间选择优秀的库 将为以后的开发节省大量时间 并带来更快乐的编码体验 在理想世界中 所有开发人员的关系是相互依赖和关联的 协作开发 代码要有完美的格式 没有低级
  • 小程序`canvasToTempFilePath:fail:cearte bitmap failed?`

    这个方法的思路来源链接 微信开放社区 主要是通过延迟 重试 以及画质来解决手机性能等问题导致的canvasToImageFile故障 代码仅供参考 欢迎大家提供更多方法或思路 或指出代码异常 谢谢 下面是我用到项目中的代码片段 海报信息 P
  • PID算法的理论分析

    PID算法的理论和应用 PID算法基本原理 PID算法的离散化 PID算法伪代码 PID算法C 实现 pid cpp pid h pid example cpp Python代码 仿真结果 PID算法基本原理 PID算法是控制行业最经典 最
  • webbench剖析

    webbench 其为linux上一款web性能压力测试工具 它最多可以模拟3万个并发连接数来测试服务器压力 其原理为fork多个子进程 每个子进程都循环做web访问测试 子进程将访问的结果通过管道告诉父进程 父进程做最终结果统计 其主要原