版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Function_Dou/article/details/90064252
前面铺垫了非阻塞 connect 以及 HTTP 请求, 本节就已 unp 书上的例子实现一个简单的 web 客户端程序.
客户程序
使用非阻塞式connect 保证同时能建立多个 TCP 连接.
如果使用阻塞式 connect, 每次都必须等待上一个连接成功后才能建立下一个连接; 而我们所写的客户程序只是将第一个连接单独执行, 之后的多个连接再并行连接.
完整代码 : client_web.c
#include "web.h"
#define MAXFILES 64
#define PORT "80"
/* connect 连接的状态 */
#define F_CONNECTING 1 /* 连接中 */
#define F_READING 2 /* 已经连接, 正在读 */
#define F_DONE 4 /* 已完成 */
#define GET_CMD "GET %s HTTP/1.1\r\n\r\n"
struct file{
char *f_name;
char *f_host;
int fd;
int f_flags;
}file[MAXFILES];
int nonblock(int);
void home_page(const char *, const char *);
void start_connect(struct file *);
void write_get_cmd(struct file *);
int tcp_connect(const char *, const char *);
struct addrinfo * host_serv(const char *, const char *, int, int);
/*
* nconn : 当前创建连接的 TCP 个数
* nlefttoconn : 没有连接的 TCP 个数
* nlefttoread : 在准备读取的 TCP 个数
* maxfd : 打开的最大文件描述符
*/
int nconn, nfiles, nlefttoconn, nlefttoread, maxfd;
fd_set rset, wset;
#include "client_web.h"
#include <time.h>
int main(int argc, char *argv[]){
...
// 获取需要连接的网页
nfiles = argc - 4 > MAXFILES ? MAXFILES : argc - 4;
for(int i = 0; i < nfiles; ++i){
file[i].f_name = argv[i+4];
file[i].f_host = argv[2];
file[i].f_flags = 0;
}
home_start = clock();
home_page(argv[2], argv[3]); /* 与主页先保持连接 */
home_end = clock();
...
connectnum_start = clock();
while(nlefttoread > 0){
/* 同时能建立的连接个数 */
while(nconn < maxnconn && nlefttoconn > 0){
for(i = 0; i < nfiles; ++i)
if(file[i].f_flags == 0)
break;
if(i == nfiles)
errPrint("nlefttoconn but nothing found");
start_connect(&file[i]);
nconn++;
nlefttoconn--;
}
rs = rset;
ws = wset;
n = select(maxfd + 1, &rs, &ws, NULL, NULL);
// 向服务端发送要获取的信息, 然后关闭写监听后在来监听读
for(i = 0; i < nfiles; ++i){
flags = file[i].f_flags;
if(flags == 0 || (flags & F_DONE))
continue;
fd = file[i].fd;
if(flags & F_CONNECTING && (FD_ISSET(fd, &rs) || FD_ISSET(fd, &ws))){
n = sizeof(err);
if(getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &n) < 0 || err != 0){
error("connect error");
}
/* 写完后, 关闭写监听, 打开读监听 */
printf("connect established for %s\n", file[i].f_name);
FD_CLR(fd, &wset);
write_get_cmd(&file[i]);
}
else if(flags & F_READING && FD_ISSET(fd, &rs)){
if((n = read(fd, buf, sizeof(buf))) == 0){
printf("end-of-file on %s\n", file[i].f_name);
close(fd);
file[i].f_flags = F_DONE;
FD_CLR(fd, &rset);
nconn--;
nlefttoread--;
}
else
printf("read %d bytes from %s\n", n, file[i].f_name);
}
}
}
connectnum_end = clock();
printf("\n\nhome connet time : %f\n", (double)(home_end - home_start)/CLOCKS_PER_SEC);
printf("num connect time : %f\n", (double)(connectnum_end - connectnum_start)/CLOCKS_PER_SEC);
printf("sum time : %f\n", (double)(connectnum_end - home_start)/CLOCKS_PER_SEC);
return 0;
}
程序运行 :
make
./web 5 www.foobar.com / image1.gif image2.gif image2.gif image3.gif image4.gif image5.gif image6.gif image7.gif
程序传参含义
./web 同时允许最大连接数 主页 文件名 文件路径 ...
多连接性能验证
现在我们改变最大连接数来验证同时多个连接的性能 :
./web 1 www.foobar.com / image1.gif image2.gif image2.gif image3.gif image4.gif image5.gif image6.gif image7.gif
./web 3 www.foobar.com / image1.gif image2.gif image2.gif image3.gif image4.gif image5.gif image6.gif image7.gif
./web 7 www.foobar.com / image1.gif image2.gif image2.gif image3.gif image4.gif image5.gif image6.gif image7.gif
扫描二维码关注公众号,回复:
6191206 查看本文章
虽然总时间的结果会因为网络而有差异, 但基本上多连接执行的时间是越来越少, 性能也就越来越好.
注意 : 并非客户端并发连接数越多, 带来的性能提升就更大; 相反, 可能会导致性能下降, 特别是在网络状况差的时候.
小结
并发连接能适当的提升性能, 但不是绝对的.
- 掌握 connect 非阻塞编程.
- 该程序也可以改为多线程.