UNP读书笔记之 非阻塞connect

下文实现的程序内容是一个web客户端

这个客户端向web一次性请求多个图片

这些图片并非串行传输,而是同时建立多个连接,在进行传输。

这里利用了非负责的connect, 每次connect并进行三次握手时,先直接返回, 然后一系列设置。

在main的for循环中,  用select检测那个链接是否可写或可读, 可写时三次握手结束,需要发送“GET HTTP”请求。

可读时,指需要读该文件。而且每次只读一次,而不是用 while循环进行阻塞IO。

每当一个文件读完了,就关闭那个连接。

#include "web.h"
int main(int argc, char **argv){
	int i, fd, n, maxconn, flags, error;
	char buf[MAXLINE];
	fd_set rs, ws;
	if(argc < 5)
		err_quit("usage: web <#conns><hostn><homepage><file1>...");
	maxconn = atoi(argv[1]); //获取最大连接数
	nfiles = min(argc - 4, MAXFILES);	//传递的文件数量,最多为20,为何减四?agc0,连接数,hostname,"/"
	for(i = 0; i < nfiles; i++){
		file[i].f_name = argv[i + 4];
		file[i].f_host = argv[2];  //每个文件的主机名都是一样的
		file[i].f_flags = 0; //=0即为尚未处理的意思
	}
	printf("nfiles = %d\n", nfiles);
	home_page(argv[2], argv[3]); //发出读取主页的命令
	FD_ZERO(&rset);
	FD_ZERO(&wset);
	maxfd = -1;
	//nlefttoconn是尚未有TCP连接的文件数, nleftread是尚未读取的TCP连接数, nconn是当前打开着的连接数。
	nlefttoread = nlefttoconn = nfiles;  //nfiles是总共需要读的文件数
	//当前打开的连接数
	nconn = 0;
	while(nlefttoread > 0){
		while(nconn < maxnconn && nlefttoconn > 0){
			for(i = 0;i < nfiles;i++){
				if(file[i].f_flags == 0) //找到一个尚未处理的文件
					break;
			}
			//i等于nfiles,说明没找到,显然有错,即有文件未连接,但是却找不到尚未处理的文件
			if(i == nfiles)
				err_quit("nlefttoconn = %d but nothing found", nelfttoconn);
			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].f_fd;
			//如果这个描述符是已连接,却说可读,说明可以发GET请求头了
			if(flags & F_CONNECTING && (FD_ISSET(fd, &rs)|| FD_ISSET(fd, &ws))){
				n = sizeof(error);
				if(getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &n) < 0 || error != 0){
					err_ret("nonblocking connect failed for %s", file[i].f_name);
				}
				printf("connection established for %s\n", file[o].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(bug))) == 0){
					pritnf("end-of-file on %s\n", file[i].f_name);
					Close(fd);  //有文件读完了
					file[i].f_flags = FDONE;
					FD_CLR(fd, &rest);
					nconn--;
					nlefttoread--;
				}else{
					printf("read %d bytes from %s\n", n , file[i].f_name);
				}
			}
			
		}
	}
}

void home_page(const char *host, const char *fname){
	int fd, n;
	char line[MAXLINE];
	fd = Tcp_connect(host, SERV)
	n = snprintf(line, sizeof(line), GET_CMD, fname);
	Writen(fd, line, n);  //发送Get请求
	for(;;){
		if( (n = Read(fd, line, MAXLINE)) == 0)
			break;
		printf("read %d bytes of home page\n", n);
	}
	printf("end-of-file on home page\n");
	Close(fd);
}

void start_connect(struct file *fptr){
	int fd, flags, n;
	struct addrinfo *ai;
	//转换主机名为和服务名
	ai = Host_serv(fptr->f_host, SERV, 0, SOCK_STREAM);
	//创建一个套接字
	fd = Socket(ai->ai_family, ai->ai_socktype, ai->protocol);
	fptr->f_fd = fd;
	printf("start_connect for %s, fd %d\n", fptr->f_name, fd);
	//先置零,然后设置为非阻塞
	flags = Fcntl(fd, F_GETFL, 0);
	Fcntl(fd, F_SETFL, flags | O_NONBLOCK);
	
	if( (n = connect(fd, ai->ai_addr, ai->ai_addrlen)) < 0){
		//发起非阻塞connect, 在还未完全建立连接时,就自动返回负数
		//这时候我们可以其他操作,例如设置flag,设置FD等
		if(errno != EINPROGRESS)
			err_sys("nonblocking connect error");
		fptr->f_flags = F_CONNECTING;
		FD_SET(fd, &rset);
		FD_SET(fd, &wset);
		if(fd > maxfd)
			maxfd = fd;
		
	}else if(n >= 0)  //若连接早已经建立过了,即之前没关掉,那么我们直接发出Get命令即可
		write_get_cmd(fptr);
}

void write_get_cmd(struct file *fptr){
	int n;
	char line[MAXLINE];
	n = snprintf(line, sizeof(line), GET_CMD, fptr->f_name);
	Writen(fptr->f_fd, line, n);
	printf("wrote %d bytes for %s\n", n, fptr->f_name);
	//发完Get后,应该进行读取了,故修改状态为reading
	fptr->f_flags = F_READING;
	//设置读,即如果该描述符可读,则对应select返回
	FD_SET(fptr->f_fd, &rset);
	if(fptr->f_fd > maxfd)
		maxfd = fptr->f_fd;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值