// 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()直接传字符指针会出现非常奇怪的问题,会乱码。