47. web 客户程序

版权声明:本文为博主原创文章,未经博主允许不得转载。 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 非阻塞编程.
  • 该程序也可以改为多线程.

猜你喜欢

转载自blog.csdn.net/Function_Dou/article/details/90064252