linux的select函数

我们先来看一段阻塞的read代码:

#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[]){
	int fd;
	char buf[11];
	int ret;

	fd=0;

	while(1){
		//set the buf to 0
		memset((void *)buf,0,11);
		ret=read(fd,(void *)buf,10);
		printf("ret=%d\n",ret);

		if(ret!=-1){
			buf[10]='\0';
			printf("  buf=%s\n",buf);
		}
	}
	return 0;
}

我们先用memset初始化数组:

void *memset(void *s, int c, size_t n);

DESCRIPTION
The memset() function fills the first n bytes of the memory area pointed to by s with the constant byte c.

也就是说,我们把buf全部设置为0。

然后使用read函数:

 ssize_t read(int fd, void *buf, size_t count);

read() attempts to read up to count bytes from file descriptor fd into the buffer starting at buf.

On success, the number of bytes read is returned (zero indicates end of file), and the file position is advanced by this number. It is not an error if this number is smaller than
the number of bytes requested; this may happen for example because fewer bytes are actually available right now (maybe because we were close to end-of-file, or because we are
reading from a pipe, or from a terminal), or because read() was interrupted by a signal. On error, -1 is returned, and errno is set appropriately. In this case it is left
unspecified whether the file position (if any) changes.

read会从文件描述符fd中读取内容,并读取count个字节,并将内容读到buf中。

它会返回读到的字节数。如果是0,则是读到末尾了。如果是-1,那就是出错了。

在没有出错的情况下,我们打印buf的内容(读取到的内容)。

'\0'标志着字符串的结尾。

我们运行该程序:

I love linux programming
ret=10
  buf=I love lin
ret=10
  buf=ux program
ret=5
  buf=ming

我们发现,程序阻塞了,它一直在等待输入。这是因为它在read处阻塞了。

但这是不好的,因为如果这是一个网络程序,如果客户端一直没有消息发过来的话,难道就要一直阻塞吗?这是很浪费资源的。

于是,我们要用到select


int select(int nfds, fd_set *readfds, fd_set *writefds,
                  fd_set *exceptfds, struct timeval *timeout);

DESCRIPTION
select() and pselect() allow a program to monitor multiple file descriptors, waiting until one or more of the file descriptors become “ready” for some class of I/O operation
(e.g., input possible). A file descriptor is considered ready if it is possible to perform the corresponding I/O operation (e.g., read(2)) without blocking.

select会轮询监听多个文件描述符,如果有文件描述符准备好了io操作,那么这个文件描述符就会被激活。

Three independent sets of file descriptors are watched. Those listed in readfds will be watched to see if characters become available for reading (more precisely, to see if a
read will not block; in particular, a file descriptor is also ready on end-of-file), those in writefds will be watched to see if a write will not block, and those in exceptfds
will be watched for exceptions. On exit, the sets are modified in place to indicate which file descriptors actually changed status. Each of the three file descriptor sets may be
specified as NULL if no file descriptors are to be watched for the corresponding class of events.

fd_set *readfds, fd_set *writefds,fd_set *exceptfds是三个传入传出参数。它们分别监听读事件、写事件和异常事件。fd_set *readfds表示监控有读数据到达文件描述符集合,我们将会使用这个参数,因为我们要监听读事件。

如果设置为NULL就表示不需要监控文件描述符的该事件。

nfds is the highest-numbered file descriptor in any of the three sets, plus 1.

nfds为要监控的文件描述符的最大数+1。因为文件描述符的下标是从0开始的。

The timeout argument specifies the minimum interval that select() should block waiting for a file descriptor to become ready


           struct timeval {
               long    tv_sec;         /* seconds */
               long    tv_usec;        /* microseconds */
           };

timeout就是要阻塞的时间,它由秒和微秒组成。

RETURN VALUE
On success, select() and pselect() return the number of file descriptors contained in the three returned descriptor sets (that is, the total number of bits that are set in
readfds, writefds, exceptfds) which may be zero if the timeout expires before anything interesting happens

select返回激活的文件描述符的总数。如果timeout时间到了,就会返回0。

另外:

Four macros are provided to manipulate the sets. FD_ZERO() clears a set. FD_SET() and FD_CLR() respectively add and remove a given file descriptor from a set. FD_ISSET() tests
to see if a file descriptor is part of the set; this is useful after select() returns.

FD_ZERO()清空一个集合。

FD_SET()从一个集合中增加给定的文件描述符。

FD_CLR()从一个集合中移除给定的文件描述符。

FD_ISSET()测试一个文件描述符是否在一个集合中。


有了以上的知识,我们来看代码:

#include <stdio.h>
#include <string.h>
#include <sys/types.h>

int main(int argc, char *argv[]) {
	int fd;
	char buf[11];
	int ret, sret;

	fd = 0;

	fd_set readfds;
	struct timeval timeout;

	while (1) {

		FD_ZERO(&readfds);
		FD_SET(fd, &readfds);

		timeout.tv_sec = 5;
		timeout.tv_usec = 0;

		sret = select(1, &readfds, NULL, NULL, &timeout);

		if (sret == 0) {
			printf("sret=%d\n", sret);
			printf("   timeout\n");
		} else {
			printf("sret=%d\n", sret);
			//set the buf to 0
			memset((void *) buf, 0, 11);
			ret = read(fd, (void *) buf, 10);
			printf("ret=%d\n", ret);

			if (ret != -1) {
				buf[10] = '\0';
				printf("  buf=%s\n", buf);
			}

		}
	}
	return 0;
}

这时候,如果客户端没有发消息过来,服务器端只会等5秒,5秒之后可以处理其他的事。之后如果发消息来了,还是能够收到,因为服务端一直在循环,重要的是,select并不阻塞。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值