https://docs.scrapy.org/en/latest/intro/tutorial.html
https://github.com/qianlizhixing12/webbench
c++重构webbench,学习socket编程,fork进程,进程间通信
socket网络编程
static int Socket(const string &host, int port) {
// sockaddr_in
struct sockaddr_in ad;
memset(&ad, 0, sizeof(ad));
ad.sin_family = AF_INET;
if (unsigned long inaddr = inet_addr(host.c_str()) != INADDR_NONE)
memcpy(&ad.sin_addr, &inaddr, sizeof(inaddr));
else {
struct hostent *hp = gethostbyname(host.c_str());
if (hp == NULL) {
return -1;
}
memcpy(&ad.sin_addr, hp->h_addr, hp->h_length);
}
ad.sin_port = htons(port);
// socket
int 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;
}
- inet_addr将一个点分十进制的IP字符串转换成一个长整数型数,失败返回INADDR_NONE
- gethostbyname从dns域名系统获取ip地址信息
- htons把机器上的整数转换成网络字节序(big-endian,即整数的高位字节存放在内存的低地址处)
- socket建立套接字
- connect建立tcp连接
fork复制进程
/* fork childs */
pid_t pid = 0;
int i = 0;
for (i = 0; i < clients; i++) {
pid = fork();
// 子进程不循环
if (pid <= (pid_t)0) {
/* child process or error*/
sleep(1); /* make childs faster */
break;
}
}
if (pid < (pid_t)0) {
cout << "problems forking worker no." << i << endl;
perror("fork failed.");
exit(3);
} else if (pid == (pid_t)0) {
bench_child(mypipe[1], host, port, benchtime, data, http_version,
http_force);
} else {
bench_father(mypipe[0], clients, benchtime);
}
- fork复制进程(包括运行堆栈信息),复制失败返回负值,成功时父进程中返回子进程pid,子进程中返回0
pipe(管道)进程通信
int mypipe[2];
if (pipe(mypipe)) {
perror("pipe failed.");
exit(3);
}
static void bench_father(const int pipeno, int clients, const int benchtime) {
FILE *f = fdopen(pipeno, "r");
if (f == NULL) {
perror("open pipe for reading failed.");
exit(3);
}
setvbuf(f, NULL, _IONBF, 0);
int speed = 0;
int failed = 0;
int bytes = 0;
int i, j, k;
while (1) {
if (fscanf(f, "%d %d %d", &i, &j, &k) < 2) {
cout << "Some of our childrens died." << endl;
break;
}
speed += i;
failed += j;
bytes += k;
/* fprintf(stderr,"*Knock* %d %d read=%d\n",speed,failed,pid); */
if (--clients == 0)
break;
}
fclose(f);
}
/* write results to pipe */
FILE *f = fdopen(pipeno, "w");
if (f == NULL) {
perror("open pipe for writing failed.");
exit(3);
}
fprintf(f, "%d %d %d\n", speed, failed, bytes);
fclose(f);
- pipe建立管道,成功返回0,失败返回非0值,管道是半双工的,数据单方向流动;mypipe管道两个文件描述符, 管道的两端是固定的。文件描述符mypipe[0]表示读端,文件描述符mypipe[1]表示写端,有了这两个描述符,可用一般文件的I/O函数(如close、read、write)用于管道通信
sigaction信号处理
volatile int timerexpired = 0;
static void alarm_handler(int signal) { timerexpired = 1; }
static void bench_child(const int pipeno, const string &host, const int port,
const int benchtime, const string &data,
const string &http_version, bool http_force) {
/* setup alarm signal handler */
struct sigaction sa;
sa.sa_handler = alarm_handler;
sa.sa_flags = 0;
if (sigaction(SIGALRM, &sa, NULL)) {
exit(3);
}
alarm(benchtime);
int speed = 0;
int failed = 0;
int bytes = 0;
// int len = ;
nexttry:
while (1) {
if (timerexpired) {
if (failed > 0) {
failed--;
}
break;
}
int s = Socket(host, port);
if (s < 0) {
failed++;
continue;
}
if (data.size() != write(s, data.c_str(), data.size())) {
failed++;
close(s);
continue;
}
if (close(s)) {
failed++;
continue;
}
speed++;
}
}
- 这里是用信号实现定时器
sigaction(int signum, const struct sigaction *act, struct sigaction *oldact)
信号设置- signum要操作的信号
- act信号处理方式,
sa.sa_handler
处理函数 - oldact原信号处理方式
- 返回值0成功,-1有错误
alarm(seconds)
设置信号SIGALRM经过seconds秒数后传送给当前进程,seconds为0之前设置闹钟会取消,并将剩下的时间返回,一个进程只能有一个闹钟时间