tcp客户/服务器回射程序之四-----select函数的使用

tcp客户/服务器回射程序之四-----select函数的使用

我们看到tcp客户同时处理两个输入:标准输入和tcp套接口。遇到的问题是就在客户阻塞于(标准输入上的)fgets调用期间,服务器进程被杀死。服务器tcp虽然正确的给客户tcp发送了一个FIN,但是既然客户进程正阻塞于从标准输入读入,它将看不到这个EOF,直到从套接口读时为止(可能已经过了很长一段时间)。这样的进程需要一种预先告知内核的能力,使得内核一旦发现进程指定的一个或者多个I/O条件就绪(也就是说输入已准备好被读取,或者描述字已能承接更多的输出),它就通知进程。这个能力称为I/O复用(I/O multiplexing),是由select和poll这两个函数支持的。

I/O复用典型使用在下列网络应用场合:

1).当处理多个描述字(通常是交互式输入和网络套接口)时,必须使用I/O复用。

2).一个客户同时处理多个套接口是可能的。

3).如果一个tcp服务器既要处理监听套接口,又要处理已连接套接口,一般就要使用I/O复用

4).如果一个服务器既要处理tcp,又要处理udp,一般需要使用I/O复用。

5).如果一个服务器要处理多个服务或者多个协议,一般就要使用I/O复用。

select函数:

该函数允许进程指示内核等待多个事件中的任何一个发生,并仅在有一个或多个事件发生或经历一段指定的时间后才唤醒它。

select函数原型:

#include <sys/select.h>
#include <sys/time.h>

int select(int maxfdp1, fd_set *readset, fd_set *writeset,
			fd_set *exceptset, const struct timeval *timeout);
//返回:就绪描述字的正数目。0--超时, -1---出错
struct timeval {
	long tv_sec;
	long tv_usec;
};
4个参数的解释:

maxfdp1:指定待测试的描述字个数,它的值是待测试的最大描述字加1.描述字0、1、2.....一直到maxfdp1-1均被测试。这个参数存在的意义:为了效率的原因。每个fd_set都有表示大量描述字的空间,然而一个普通进程所用的数量却少得多。内核正是通过在进程与内核之间不拷贝描述字集中不必要的部分,从而不测试总为0的那些位来获得效率的。

readset、writeset、exceptset指定我们让内核测试读、写和异常条件的描述字。如何让这3个参数的每一个指定一个或者多个描述字是一个设计上的问题。select使用描述字集,典型的是一个整数数组,其中每个整数中的每一位对应一个描述字。

void FD_ZERO(fd_set *set);	//clear all bits in set
void FD_SET(int fd, fd_set *set);	//turn on the bit for fd in set
void FD_CLR(int fd, fd_set *set);	//turn off the bit for fd in set
int FD_ISSET(int fd, fd_set * set);	//is the bit for fd on in set?
我们分配一个fd_set数据类型的描述字集,并用这些宏设置或测试该集合中的每一位。

timeout:这个参数有3种可能:

1).永远等待下去。仅在有一个描述字准备好I/O时才返回。为此,我们把参数设置为NULL.

2).等待一段时间:在有一个描述字返回准备好I/O时才返回,但是不超过由该参数所指向的timeval结构中指定的秒数和微秒数。

3).根本不等待:检查描述字后立即返回,这称为论询(polling).为此,该参数必须指向一个timeval结构,而且其中的定时器值必须为0.

该函数返回时,结果指示哪些描述字已就绪。该函数返回后,使用FD_ISSET宏来测试fd_set数据类型中的描述字。描述字中任何与未就绪的描述字对应的位返回时均清0.为此,每次重新调用select时,我们都得再次把所有描述字集中所关心的位均置为0.

使用select时最常见的两个编程错误:忘了对最大描述字加1;忘了描述字集是值-结果参数。第二个错误导致调用select时,描述字集中我们认为是1的位却被置为0.


早先的版本在于:当套接口上发生某些事件时,客户可能阻塞与fgets调用。新版本改为阻塞于select调用,等待要么标准输入可读,要么套接口可读。

void str_cli(FILE *fp, int sockfd) {
	int maxfdp1;
	fd_set rset;
	char send[MAXLINE], recv[MAXLINE];
	FD_ZERO(&rset);
	for(;;) {
		FD_SET(fileno(fp), &rset);
		FD_SET(sockfd, &rset);
		maxfdp1 = max(fileno(fp), sockfd) + 1;
		/*
		 * fileno函数把标准I/O文件指针转换为对应的描述字。select只工作在描述字上。
		 */
		select(maxfdp1, &rset, NULL, NULL, NULL);
		if(FD_ISSET(sockfd, &rset)) {
			if(0 == readline(sockfd, recv, MAXLINE))
				perror("xxxx");
			fputs(recv, stdout);
		}
		if(FD_ISSET(fileno(fp), &rset)) {
			if(fgets(send, MAXLINE, fp) == NULL)
				return;
			write(sockfd, send, strlen(send));
		}
	}
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值