引言

  <<独白>> 席慕蓉 节选一

把向你借来的笔还给你吧。

一切都发生在回首的刹那。

我的彻悟如果是缘自一种迷乱,那么,我的种种迷乱不也就只是因为一种彻悟?

在一回首间,才忽然发现,原来,我的一生的种种努力,不过只是为了周遭的人都对我满意而已。

为了要博得他人的称许与微笑,我战战兢兢地将自己套入所有的模式,所有的桎梏。

走到中途,才忽然发现,我只剩下一副模糊的面目,和一条不能回头的路。

把向你借来的笔还给你吧。

配乐 :

  我在长大 : http://music.163.com/#/song?id=155977

不知疲倦的整夜码代码 像块石头一样滚来滚去
可是找不到家的方向
我在长大 / 我在长大 / 我在长大 / 我在长大

前言

我们先说一下,参照的东西和需要掌握的东西

具体参照

  1. HTTP协议简单介绍 http://www.cnblogs.com/biyeymyhjob/archive/2012/07/28/2612910.html

这里需要掌握简单的Linux 操作,这里分享一个自己总结的socket入门基础文档.

2. socket 简单入门doc http://download.csdn.net/detail/wangzhione/9412538

到这里, 基本准备工作就完成了,那就搞起吧, 看看那些说起来很玄乎的工具是怎么简单实现的.顺带扯一点,

下面代码自己多看几遍写一遍,可以帮助你理解Linux中很多基础概念,例如 信号,多进程, 管道,socket,中断,

协议等.

正文

1.简单说一点格式

  这里我们先看下面一段代码格式,来了解C接口设计中一些潜规则.

/*
* 这是一个对外的接口函数声明,
*相当于public
*/
extern int isbig(void); /*
* 这个接口也具备对外能力,但是省略了extern
*意图就是构建整个 *.h文件的时候需要用它,但是不希望用户用.
*属于 low level api, 内部接口集用,外部不推荐用,有点像protected
*/
int heoo(void); /*
* 这个是个内部声明或定义, 只能在当前接口集的内部使用,外部无法
*访问,相当于pirate
*/
static void* __run(void* arg);

定义部分简单说明一下.以前博文中说过,这里再简单说一次,编程和打游戏一样,要有好节奏,

要有好套路,这样打起来才顺.等同于实践可以总结出理论基础.前任要感谢.

/*
* 定义加声明,放在一块
*/
static int __run(void)
{
static union {
unsigned short _s;
unsigned char _cs[sizeof(unsigned short)];
} __ut = { };
return __ut._cs[] == ;
} /*
* 声明和定义分开
*/
extern void start(void);
//下面是定义部分
void
start(void)
{
puts("你好!");
}

套路很多,都是历代编程王者,开创的各大武功的起手式,各大定式都可以就看你想拜入那个门派,开始这个编程网游之旅.

2.说思路

  经过上面简单格式介绍,这里就说一下思路. 关于 这篇博文,C 对网页的压力测试构建的思路.具体如下:

a. 拼接请求报文 , 解析

b. 发送请求报文 , 请求

c. 处理请求报文, 处理

d.总结请求报文, 输出统计

上面就是这次处理的主要流程, 多次请求采用的fork多个子进程

    for(i=; i<cut; ++i){
pid = fork(); //狂开进程了
if(pid <= ){
sleep();//别让它太快完毕,父进程来不及收尸
break;//结束子进程继续
}
++rt;
}

关于父子进程通信采用 管道

    //创建管道 进行测试, 代码比较杂,需要一些Linux基础知识,多练习
if(pipe(pfds)<-)
CERR_EXIT("pipe create is error!");

还有一个是通过信号处理超时问题

//默认全局标识, 控制开关
volatile int v_timer = 0; //闹钟信号,当它启动那一刻就表示可以退下了.
static void __alarm(int sig)
{
v_timer = 1;
}

后面我们就通过代码来说了,最好你写一遍,就明白容易了.扯一点,关于volatile 可以声明是为了方式编译器优化,导致代码死循环.

3.逐个实现

第一部分头文件,声明解析

首先观察下面前戏代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdarg.h>
#include <limits.h>
#include <unistd.h>
#include <fcntl.h>
#include <netdb.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h> //控制台打印错误信息, fmt必须是双引号括起来的宏
#ifndef CERR
#define CERR(fmt, ...) \
fprintf(stderr,"[%s:%s:%d][error %d:%s]" fmt "\r\n",\
__FILE__, __func__, __LINE__, errno, strerror(errno),##__VA_ARGS__) //检测并退出的宏
#define CERR_EXIT(fmt, ...) \
CERR(fmt, ##__VA_ARGS__), exit(EXIT_FAILURE) #endif/* !CERR */ // url长度限制 和 请求串长度限制
#define _INT_URL (1024)
#define _INT_REQ (2048)
//主机地址的最大长度
#define _INT_PORT (255)
//等待时间,在60s后就不在处理了
#define _INT_END (23) //简单结构
struct iport{
short port;
char ip[_INT_PORT];
}; //默认全局标识, 控制开关
volatile int v_timer = ; //闹钟信号,当它启动那一刻就表示可以退下了.
static void __alarm(int sig)
{
v_timer = ;
} /*
* 执行的主要方法,结果会通过写管道 wfd写入
*/
void bcore(struct iport* ips, const char* req, int wfd); /*
* 只接受完整请求
* 例如
* brequest("http://192.168.1.162:80/", ...) 或 http://www.baidu,com
* 通过url拼接请求串,并返回访问结构
*/
struct iport* brequst(const char* url, char rqs[], int len);
//请求主方法,客户端,返回开启的子进程数
int bench(int cut, const char* url, int wfd); /*
* 请求链接主机,根据ip或url
*
* host : url地址
* port : 请求的端口号
*/
int contweb(const char* host, int port);

CERR系列是一个帮助宏,简单打印错误信息. 技巧值得推荐使用.其中 _INT_END 表示每个子进程请求的时间长度(s).

感觉注释也挺详细的关于

//请求主方法,客户端,返回开启的子进程数
int bench(int cut, const char* url, int wfd);

主要开启多进程的,依次调用 breques解析url 和 调用 bcore 处理socket .

其中 contweb 是一个可以根据ip或主机地址url解析,链接主机.具体实现如下

/*
* 请求链接主机,根据ip或url
*
* host : url地址
* port : 请求的端口号
*/
int
contweb(const char* host, int port)
{
int sd;
in_addr_t ia;
struct sockaddr_in sa = { AF_INET };
struct hostent *hp; ia = inet_addr(host);
if(ia != INADDR_NONE)
memcpy(&sa.sin_addr, &ia, sizeof(ia));
else {
hp = gethostbyname(host);
if(hp < ){
CERR("gethostbyname %s error!", host);
return -;
}
memcpy(&sa.sin_addr, hp->h_addr, hp->h_length);
}
if((sd=socket(PF_INET, SOCK_STREAM, ))<){
CERR("socket SOCK_STREAM");
return sd;
} //连接主机
sa.sin_port = htons(port);
if(connect(sd, (struct sockaddr*)&sa, sizeof sa)<){
CERR("connect sd:%d error!", sd);
return -;
} return sd;
}

没有什么花哨的东西就是同步链接HTTP服务器, 如果传入的的host是url那么就调用 gethostbyname 获取到 请求

主机的sin_addr地址量.

扯一点,socket编程大部分也等同于业务代码,固定的套路固定的模式,写写也都会写了.但也挺麻烦了,随便一个简单的

完整的socket demo都4-5百行代码,写的手疼.封装也很麻烦.但是你理解了它的思路或套路,还是很容易上马提枪的.

第二部分各个,定义解析

  首先代码可以优化,我写好后没优化了,就直接分享了.先看http协议解析代码如下

//通过url拼接请求串
struct iport*
brequst(const char* url, char rqs[], int len)
{
static struct iport __ips;
//请求的主机, 中间用的url, 临时数据
const char *hurl, *tmp;
char c;
int pt = ;//临时用的int变量 //简单检查参数检查,检查数组大小,url是否合法
if(len<_INT_REQ || !url || !*url || strlen(url)>_INT_URL)
CERR_EXIT("params url:%s, len[>=%d];%d error.", url, _INT_REQ, len);
//检测url是否是http请求的url,也比较简单,复杂检查要用正则表达式
hurl = strstr(url, "://");
if(!hurl || strncasecmp(url, "http", ))
CERR_EXIT("url is err [%s]!", url);
//简单初始化
memset(rqs, , len);
memset(&__ips, , sizeof __ips); //这种代码可以优化,用指针替代数组索引
strcat(rqs, "GET ");
hurl += ; //跳过 "://"指向下一个字符 //解析url 上是否有端口信息
tmp = index(hurl, ':');
if(tmp && tmp < index(hurl, '/')){
strncpy(__ips.ip, hurl, tmp - hurl);
//没有做安全检查
while((c=*++tmp)!='\0' && c!='/'){
if(c<='' && c>=''){
pt = *pt + c - '';
continue;
}
CERR_EXIT("url is error [%s].", url);
}
}
else{
pt = ; //默认端口
//这句话意思 是 将 www.baidu.com/ 其中/之前的地方写入到 ip[]中
strncpy(__ips.ip, hurl, strcspn(hurl, "/"));
}
__ips.port = pt; //将这些内容链接到rqs中
strcat(rqs, hurl + strcspn(hurl, "/"));
//采用HTTP1.1 协议
strcat(rqs, " HTTP/1.1\r\n");
//添加请求方
strcat(rqs, "User-Agent: Happy is good.\r\n");
//上面这样解析有点慢,最快的是整体for,但是更难维护
strcat(rqs, "Host: ");
strcat(rqs, __ips.ip);
strcat(rqs, "\r\n");
//拼接串最后,连接状态,完毕就关闭
strcat(rqs, "Connection: close\r\n");
//拼接空行, 上面可以优化
strcat(rqs, "\r\n"); //最终检测,是否字符串太长了
if(rqs[len-])
CERR_EXIT("get http too len!"); return &__ips;
}

主要思路是先检查 参数, 看 是否是 右边这样正则格式的串  "http.://.+/"

后面拼接结果截图如下:

更加详细关于HTTP协议解析的,可以参照我前面的连接,HTTP协议简单解析.

后面介绍的是有了请求串,怎么发送请求和处理请求,代码如下:

/*
* 执行的主要方法,结果会通过写管道 wfd写入
*/
void
bcore(struct iport* ips, const char* req, int wfd)
{
int srv, len, rlen;
int speed = , failed = , bytes = ;
FILE* fx;
char buf[_INT_REQ]; //先注册信号
if(SIG_ERR == signal(SIGALRM, __alarm))
CERR_EXIT("signal SIGALRM error!");
alarm(_INT_END); len = strlen(req);
for(;;){
if(v_timer){ //结束之前减一,认为成功过
if(failed>)
--failed;
break;
} srv = contweb(ips->ip, ips->port);
if(srv < ){
++failed;
continue;
}
//开始写入数据
if(len != write(srv, req, len)){
++failed;
close(srv);
continue;
}
//下面读取数据,v_timer标志是否超时,超时直接走
for(;!v_timer;){
rlen = read(srv, buf, _INT_REQ);
if(rlen < ){ //不考虑软中断了,有问题直接退出
++failed;
close(srv);
goto __bcore_exit; //退出结算
}
if(rlen == )//服务端关闭,结束读取
break;
bytes += rlen;
}
close(srv);
++speed;
} __bcore_exit:
//管道写入
fx = fdopen(wfd, "w");
if(NULL == fx)
CERR_EXIT("fdopen wfd:%d error!", wfd);
fprintf(fx,"%d %d %d\n", speed, failed, bytes);
fclose(fx);
}

中间发送了一个alarm延时发送SIGALRM 信号,还有一个信号容易混淆,是SIGCLD信号 子进程退出时候给父进程发送.

其中 fdopen 是将 Linux文件描述符和 C系统文件操作句柄转换.就是统计数据写入到管道中,管道 是 1进 0出.

最后还有个 开启多进程的函数代码

//请求主方法,客户端,返回开启子进程数
int
bench(int cut, const char* url, int wfd)
{
int i, rt = ;//rt 记录正常开启的进程
pid_t pid;
char req[_INT_REQ];
struct iport* ips; //这里初始化一些环境
ips = brequst(url, req, sizeof req);
puts("***********-----------------------------------------------------------***********");
puts(req); //打印数据
puts("***********-----------------------------------------------------------***********"); for(i=; i<cut; ++i){
pid = fork(); //狂开进程了
if(pid <= ){
sleep();//别让它太快完毕,父进程来不及收尸
break;//结束子进程继续
}
++rt;
}
if(pid < )
CERR_EXIT("child %d process create error!", i);
if(pid == ){ //子进程处理
bcore(ips, req, wfd);
exit(EXIT_SUCCESS);//子进程这里就结束了
}
//下面是父进程处理
return rt;
}

也比较好明白,先拼接请求串,再开启多进程挨个请求处理.

到这核心函数基本都完成了,总的业务构建请看下面

第三部分主函数逻辑构建

// 主函数业务,从这开始
int main(int argc, char* argv[])
{
int cut, len; //开启进程数量 和 读取scanf返回值
int pfds[];
FILE* fx;
int speed = , failed = , bytes = ; //简单检测
if((argc != ) || (cut = atoi(argv[]))<= || cut > USHRT_MAX){
CERR_EXIT(
"\r\nuage: ./webtest.out [cut] [url]"
"\r\n :=> ./webtest.out 19 http://www.baidu.com/"
);
} //创建管道 进行测试, 代码比较杂,需要一些Linux基础知识,多练习
if(pipe(pfds)<-)
CERR_EXIT("pipe create is error!"); //结果处理,开启指定多个进程,重新获取进程开启成功数
cut = bench(cut, argv[], pfds[]); //这里读取管道信息
fx = fdopen(pfds[], "r");
if(NULL == fx)
CERR_EXIT("fdopen pfds[0]:%d error.", pfds[]); //统计最后数据
setbuf(fx, NULL);
while(cut > ){
int s, f, b;
len = fscanf(fx, "%d %d %d", &s, &f, &b);
if(len < ){
CERR("fscnaf read error! len:%d.", len);
break;
}
speed += s;
failed += f;
bytes += b;
--cut;
}
fclose(fx);
close(pfds[]);
close(pfds[]); //输出统计结果
puts("***********-----------------------------------------------------------***********");
printf("Collect %.2f pages/min, %.2f kb/sec. Request %d sucess, %d failed.\n",
(speed+failed)*60.0f/_INT_END, bytes/1024.0f/_INT_END, speed, failed);
puts("***********-----------------------------------------------------------***********"); return ;
}

创建管道,调用多进程处理函数,汇总最后结果.注意一点是开启的管道最后需要自己关闭.(可能这里有坑),回头看一下,代码其实

还是比较基础的,主要用的就是Linux编程相关的知识点.

最后输出 每分钟请求的页面数, 每秒请求的bit流,请求成功多少次,请求失败多少次.

4.看案例结果 

首选看下面一个操作图,先请求百度试试

最后统计结果是 每分钟请求了6357个界面左右,开启了23个子进程, 成功2414个,23个错误. 总的而言百度的分流确实不错,负载均衡强.

再来请求一个普通网站的截图

上面打印很多connect error,意思是很多进程请求链接失败,短时间大量链接导致对方服务器拒绝链接, 负载均衡差,

流量差不多,可能和网速有关,请求界面数挺多的这个界面不错,可能是纯静态的. 对于详细查看为什么失败,可以在记录 failed++的时候打印日志看看,

这里就这样了. 麻雀虽小,能用就好. 完整测试 demo 如下

webtest.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdarg.h>
#include <limits.h>
#include <unistd.h>
#include <fcntl.h>
#include <netdb.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h> //控制台打印错误信息, fmt必须是双引号括起来的宏
#ifndef CERR
#define CERR(fmt, ...) \
fprintf(stderr,"[%s:%s:%d][error %d:%s]" fmt "\r\n",\
__FILE__, __func__, __LINE__, errno, strerror(errno),##__VA_ARGS__) //检测并退出的宏
#define CERR_EXIT(fmt, ...) \
CERR(fmt, ##__VA_ARGS__), exit(EXIT_FAILURE) #endif/* !CERR */ // url长度限制 和 请求串长度限制
#define _INT_URL (1024)
#define _INT_REQ (2048)
//主机地址的最大长度
#define _INT_PORT (255)
//等待时间,在60s后就不在处理了
#define _INT_END (23) //简单结构
struct iport{
short port;
char ip[_INT_PORT];
}; //默认全局标识, 控制开关
volatile int v_timer = ; //闹钟信号,当它启动那一刻就表示可以退下了.
static void __alarm(int sig)
{
v_timer = ;
} /*
* 执行的主要方法,结果会通过写管道 wfd写入
*/
void bcore(struct iport* ips, const char* req, int wfd); /*
* 只接受完整请求
* 例如
* brequest("http://192.168.1.162:80/", ...) 或 http://www.baidu,com
* 通过url拼接请求串,并返回访问结构
*/
struct iport* brequst(const char* url, char rqs[], int len);
//请求主方法,客户端,返回开启的子进程数
int bench(int cut, const char* url, int wfd); /*
* 请求链接主机,根据ip或url
*
* host : url地址
* port : 请求的端口号
*/
int contweb(const char* host, int port); // 主函数业务,从这开始
int main(int argc, char* argv[])
{
int cut, len; //开启进程数量 和 读取scanf返回值
int pfds[];
FILE* fx;
int speed = , failed = , bytes = ; //简单检测
if((argc != ) || (cut = atoi(argv[]))<= || cut > USHRT_MAX){
CERR_EXIT(
"\r\nuage: ./webtest.out [cut] [url]"
"\r\n :=> ./webtest.out 19 http://www.baidu.com/"
);
} //创建管道 进行测试, 代码比较杂,需要一些Linux基础知识,多练习
if(pipe(pfds)<-)
CERR_EXIT("pipe create is error!"); //结果处理,开启指定多个进程,重新获取进程开启成功数
cut = bench(cut, argv[], pfds[]); //这里读取管道信息
fx = fdopen(pfds[], "r");
if(NULL == fx)
CERR_EXIT("fdopen pfds[0]:%d error.", pfds[]); //统计最后数据
setbuf(fx, NULL);
while(cut > ){
int s, f, b;
len = fscanf(fx, "%d %d %d", &s, &f, &b);
if(len < ){
CERR("fscnaf read error! len:%d.", len);
break;
}
speed += s;
failed += f;
bytes += b;
--cut;
}
fclose(fx);
close(pfds[]);
close(pfds[]); //输出统计结果
puts("***********-----------------------------------------------------------***********");
printf("Collect %.2f pages/min, %.2f kb/sec. Request %d sucess, %d failed.\n",
(speed+failed)*60.0f/_INT_END, bytes/1024.0f/_INT_END, speed, failed);
puts("***********-----------------------------------------------------------***********"); return ;
} /*
* 请求链接主机,根据ip或url
*
* host : url地址
* port : 请求的端口号
*/
int
contweb(const char* host, int port)
{
int sd;
in_addr_t ia;
struct sockaddr_in sa = { AF_INET };
struct hostent *hp; ia = inet_addr(host);
if(ia != INADDR_NONE)
memcpy(&sa.sin_addr, &ia, sizeof(ia));
else {
hp = gethostbyname(host);
if(hp < ){
CERR("gethostbyname %s error!", host);
return -;
}
memcpy(&sa.sin_addr, hp->h_addr, hp->h_length);
}
if((sd=socket(PF_INET, SOCK_STREAM, ))<){
CERR("socket SOCK_STREAM");
return sd;
} //连接主机
sa.sin_port = htons(port);
if(connect(sd, (struct sockaddr*)&sa, sizeof sa)<){
CERR("connect sd:%d error!", sd);
return -;
} return sd;
} //通过url拼接请求串
struct iport*
brequst(const char* url, char rqs[], int len)
{
static struct iport __ips;
//请求的主机, 中间用的url, 临时数据
const char *hurl, *tmp;
char c;
int pt = ;//临时用的int变量 //简单检查参数检查,检查数组大小,url是否合法
if(len<_INT_REQ || !url || !*url || strlen(url)>_INT_URL)
CERR_EXIT("params url:%s, len[>=%d];%d error.", url, _INT_REQ, len);
//检测url是否是http请求的url,也比较简单,复杂检查要用正则表达式
hurl = strstr(url, "://");
if(!hurl || strncasecmp(url, "http", ))
CERR_EXIT("url is err [%s]!", url);
//简单初始化
memset(rqs, , len);
memset(&__ips, , sizeof __ips); //这种代码可以优化,用指针替代数组索引
strcat(rqs, "GET ");
hurl += ; //跳过 "://"指向下一个字符 //解析url 上是否有端口信息
tmp = index(hurl, ':');
if(tmp && tmp < index(hurl, '/')){
strncpy(__ips.ip, hurl, tmp - hurl);
//没有做安全检查
while((c=*++tmp)!='\0' && c!='/'){
if(c<='' && c>=''){
pt = *pt + c - '';
continue;
}
CERR_EXIT("url is error [%s].", url);
}
}
else{
pt = ; //默认端口
//这句话意思 是 将 www.baidu.com/ 其中/之前的地方写入到 ip[]中
strncpy(__ips.ip, hurl, strcspn(hurl, "/"));
}
__ips.port = pt; //将这些内容链接到rqs中
strcat(rqs, hurl + strcspn(hurl, "/"));
//采用HTTP1.1 协议
strcat(rqs, " HTTP/1.1\r\n");
//添加请求方
strcat(rqs, "User-Agent: Happy is good.\r\n");
//上面这样解析有点慢,最快的是整体for,但是更难维护
strcat(rqs, "Host: ");
strcat(rqs, __ips.ip);
strcat(rqs, "\r\n");
//拼接串最后,连接状态,完毕就关闭
strcat(rqs, "Connection: close\r\n");
//拼接空行, 上面可以优化
strcat(rqs, "\r\n"); //最终检测,是否字符串太长了
if(rqs[len-])
CERR_EXIT("get http too len!"); return &__ips;
} /*
* 执行的主要方法,结果会通过写管道 wfd写入
*/
void
bcore(struct iport* ips, const char* req, int wfd)
{
int srv, len, rlen;
int speed = , failed = , bytes = ;
FILE* fx;
char buf[_INT_REQ]; //先注册信号
if(SIG_ERR == signal(SIGALRM, __alarm))
CERR_EXIT("signal SIGALRM error!");
alarm(_INT_END); len = strlen(req);
for(;;){
if(v_timer){ //结束之前减一,认为成功过
if(failed>)
--failed;
break;
} srv = contweb(ips->ip, ips->port);
if(srv < ){
++failed;
continue;
}
//开始写入数据
if(len != write(srv, req, len)){
++failed;
close(srv);
continue;
}
//下面读取数据,v_timer标志是否超时,超时直接走
for(;!v_timer;){
rlen = read(srv, buf, _INT_REQ);
if(rlen < ){ //不考虑软中断了,有问题直接退出
++failed;
close(srv);
goto __bcore_exit; //退出结算
}
if(rlen == )//服务端关闭,结束读取
break;
bytes += rlen;
}
close(srv);
++speed;
} __bcore_exit:
//管道写入
fx = fdopen(wfd, "w");
if(NULL == fx)
CERR_EXIT("fdopen wfd:%d error!", wfd);
fprintf(fx,"%d %d %d\n", speed, failed, bytes);
fclose(fx);
} //请求主方法,客户端,返回开启子进程数
int
bench(int cut, const char* url, int wfd)
{
int i, rt = ;//rt 记录正常开启的进程
pid_t pid;
char req[_INT_REQ];
struct iport* ips; //这里初始化一些环境
ips = brequst(url, req, sizeof req);
puts("***********-----------------------------------------------------------***********");
puts(req); //打印数据
puts("***********-----------------------------------------------------------***********"); for(i=; i<cut; ++i){
pid = fork(); //狂开进程了
if(pid <= ){
sleep();//别让它太快完毕,父进程来不及收尸
break;//结束子进程继续
}
++rt;
}
if(pid < )
CERR_EXIT("child %d process create error!", i);
if(pid == ){ //子进程处理
bcore(ips, req, wfd);
exit(EXIT_SUCCESS);//子进程这里就结束了
}
//下面是父进程处理
return rt;
}

编译代码如下

gcc -Wall -o webtest.out webtest.c

后面就需要大家尝试了,用的还凑合者.

光鲜的背后都是平凡.喜欢才是人这台机器的最好能源.

后记

  错误是难免,欢迎指正交流,共同消磨有趣的时光. ( ^_^ )/~~拜拜

C 网页压力测试器的更多相关文章

  1. jmeter压力测试的简单实例+badboy脚本录制(一个简单的网页用户登录测试的结果)

    JMeter的安装:在网上下载,在下载后的zip解压后,在bin目录下找到JMeter.bat文件,双击就可以运行JMeter. http://jmeter.apache.org/ 在使用jmeter ...

  2. 十个免费的Web压力测试工具

    两天,jnj在本站发布了<如何在低速率网络中测试 Web 应用>,那是测试网络不好的情况.而下面是十个免费的可以用来进行Web的负载/压力测试的工具,这样,你就可以知道你的服务器以及你的W ...

  3. linux nginx常见问题及优化,压力测试,tomcat服务器优化

    nginx常见问题 nginx优化全局配置优化[root@web2 nginx]# vim conf/nginx.confuser nobody;worker_processes 1;(与cpu核心数 ...

  4. LoadRunner压力测试实例

    1 LoadRunner 概要介绍... 2 .项目背景介绍... 5 .使用LoadRunner进行负载/ 实施测试... 16 6.1 Memory相关... 22 6.2 Processor相关 ...

  5. apache-jmeter-3.3的简单压力测试使用方法

    注: 本文参考:http://www.cnblogs.com/TankXiao/p/4045439.html http://blog.csdn.net/lan_shu/article/details/ ...

  6. 入门级----黑盒测试、白盒测试、手工测试、自动化测试、探索性测试、单元测试、性能测试、数据库性能、压力测试、安全性测试、SQL注入、缓冲区溢出、环境测试

    黑盒测试 黑盒测试把产品软件当成是一个黑箱子,只有出口和入口,测试过程中只要知道往黑盒中输入什么东西,知道黑盒会出来什么结果就可以了,不需要了解黑箱子里面是如果做的. 即测试人员不用费神去理解软件里面 ...

  7. jmeter 压力测试

    转自: https://blog.csdn.net/cbzcbzcbzcbz/article/details/78023327 Jmeter压力测试简单教程(包括服务器状态监控) 2017年09月18 ...

  8. Jmeter压力测试简单教程(包括服务器状态监控)

    前段时间公司需要对服务器进行压力测试,包括登录前的页面和登录后的页面,主要目的是测试负载均衡的实现效果.不知道是不是因为Jmeter不如loadRunner火爆还是什么,网上关于Jmeter的资料有很 ...

  9. JMeter压力测试及服务器状态监控教程

    转载自:https://blog.csdn.net/cbzcbzcbzcbz/article/details/78023327 前段时间公司需要对服务器进行压力测试,包括登录前的页面和登录后的页面,主 ...

随机推荐

  1. gcc编译, gdb调试, makefile写法

    //test.c: #include <stdio.h> int main(void) { printf("hello world!"); return 0; } == ...

  2. 业务gis 怎么让别的开发人员不需要懂gis就可以搞开发? (五)

    我们稍微搭建了一个比较简单的图形使用模板,flex端操作这里我就不说了,按大家喜好写,最后javascript部分可以通过jsduck工具生成一个开发文档给业务开发人员,前提注释部分要写好,要公开的注 ...

  3. spring batch学习笔记

    Spring Batch是什么?       Spring Batch是一个基于Spring的企业级批处理框架,按照我师父的说法,所有基于Spring的框架都是使用了spring的IoC特性,然后加上 ...

  4. C语言中的fread和fwrite

    C语言中的fread和fwrite是专门用来操作文件的方法. 1. fread负责从打开的文件指针中读取文件内容. 函数原型:size_t fread(void *p, size_t size, si ...

  5. ButterKnife的简单使用

    刚刚学习Android,也不知道算不算已经入门!但是总感觉自己没有什么提高,所以就把一些学习内容写一遍下来. 今天接触了ButterKnife这个第三方框架 GitHub地址:https://gith ...

  6. 【spring 7】spring和Hibernate的整合:声明式事务

    一.声明式事务简介 Spring 的声明式事务管理在底层是建立在 AOP 的基础之上的.其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者 ...

  7. 【Python】django权限管理

    参考:http://www.cnblogs.com/esperyong/ 参考:https://docs.djangoproject.com/en/1.8/topics/auth/default/#t ...

  8. unity3d发布Android程序

    unity3d是一个跨平台的游戏开发引擎,可以使用c#开发各种平台上的游戏,如windows,Mac,Android,windows phone,IOS,Flash等.下面说下如何将开发好的unity ...

  9. Track files and folders manipulation in Windows

    The scenario is about Business Secret and our client do worry about data leakage. They want to know ...

  10. .NET常用类库知识总结

    常用类库之.NET中的字符串 字符串的特性 1.不可变性        由于字符串是不可变的的,每次修改字符串,都是创建了一个单独字符串副本(拷贝了一个字符串副本).之所以发生改变只是因为指向了一块新 ...