1、多路IO复用的概念
这里有一篇通俗易懂的文件介绍了多路IO的概念http://www.linuxidc.com/Linux/2013-03/80704.htm。当我们需要操作多个文件时,比如我们需要读多个套接字里面的内容,但我们并不知道什么时候套接字里会有数据,如果一直在某一个套接字上阻塞,这时候就不能处理其他套接字,这样会使实时性大打折扣。但是我们就想了,为什么不用非阻塞的方式,使用轮询的方式?轮询的方式确实可以实现,但效率低下,因为我们在需要不停的去“查看”里面有没有数据,即使里面什么也没有。于是我们就想有没有一种方法,在套接字里面有数据时就通知我们,让我们及时去处理,而没有数据时,我们可以不用去理,转而处理其他的事件,这就是多路IO由来。linux提供了以下两个函数来实现多路IO。
2、select函数
头文件:
#include<sys/time.h>
#include <sys/types.h>
#include <unistd.h>
函数原型:int select(int n, fd_set *readfds,
fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
还要搭配以下宏函数来使用
FD_CLR(int fd, fd_set *set);
FD_ISSET(int fd, fd_set *set);
FD_SET(int fd , fd_set *set);
FD_ZERO(fd_set *set);
参数说明:
n : 所有的文件描述符中的最大值+1。
readfds:一个结构体指针,里面包含我们需要监测的发生“读”变化文件描述符
writefds:一个结构体指针,里面包含我们需要监测的发生“写”变化文件描述符
exceptfds:一个结构体指针,里面包含我们需要监测的发生“异常”变化文件描述符
timeout:设置时间的结构体
struct timeval{
long tv_sec; //秒数
long tv_usec;//毫秒数
}
FD_CLR(int fd, fd_set *set);:清除文件描述符集合中的一个fd
FD_SET(int fd , fd_set *set); :向文件描述符集合中添加一个fd
FD_ISSET(int fd, fd_set *set); :通过这个宏的返回值来判断哪一个文件出于”就绪”状态,可以来操作此文件。
FD_ZERO(fd_set *set); :清空文件描述符集合
应用举例:
/* 以下代码是读标准输入设备中的数据 */
#include <stdio.h>
#include <sys/time.h>
#include <unistd.h>
#define TIMEOUT 5
#define BUF_LEN 1024
int main(int argc, char const *argv[])
{
struct timeval tv; //设置时间的结构体
fd_set readfds; //文件描述符集合
int ret ;
FD_ZERO(&readfds); //清空文件描述符集合
FD_SET(STDIN_FILENO, &readfds);// 将文件描述符(标准输入:STDIN_FILENO)添加到集合中去
/* 通过结构体来设置select 等待时间 */
tv.tv_sec = 5; //秒
tv.tv_usec = 0; //微秒
ret = select(STDIN_FILENO+1, &readfds, NULL, NULL, &tv);
if(-1 == ret )
{
perror("select");
return 1;
}
else if (ret == 0) //超出时间5s
{
printf("%d seconds elapsed.Nothing input!\n",TIMEOUT);
return 0;
}
char buf[BUF_LEN+1];
int len;
if( FD_ISSET(STDIN_FILENO, &readfds) )//如果监测到有东西输入
{
len = read(STDIN_FILENO, buf, BUF_LEN);
if(-1 == len)
{
perror("read");
return 1;
}
else if (len != 0)
{
buf[len] = '\0';
printf("read:%s\n", buf);
/* code */
}
return 0;
}
fprintf(stderr, "This should not happen!\n");
return 0;
}
3、poll函数
头文件:
#include<sys/poll.h>
函数原型:
int poll(struct pollfd *fds, unsigned int nfds, int timeout);
参数说明:
fds : 一个包含多个文件描述pollfd结构体的数组指针
struct pollfd{
int fd; //文件描述符
short events; //要监测的事件
short revents; //需要返回观察的事件
}
其中:events的可选项为
POLLIN : 没有数据可读
POLLRDNORM: 有正常的数据可读
POLLRDBAND :有优先的数据可读
POLLPRI :有高优先级的数据可读
POLLOU : 写操作不会阻塞
POLLWRNORM:写正常数据不会阻塞
POLLBAND : 写优先数据不会阻塞
POLLMSG : 有一个SIGPOLL消息可用
nfds : fds数组中有多少个结构体,fds也就是数组的大小
timeout : 设置时间,同select一样,不过这里的单位是毫秒
应用举例:
//以下程序使用poll函数实现监测标准输入,同select.c文件实现的功能是一样
#include <sys/poll.h>
#include <stdio.h>
#include <unistd.h>
#define TIMEOUT 5000 //5000ms
int main(int argc, char const *argv[])
{
struct pollfd fds[1];
int ret;
fds[0].fd = STDIN_FILENO; //设置文件描述符
fds[0].events = POLLIN|POLLPRI; //设置读写方式:没有可读事件或者有高优先级数据可读
ret = poll(fds, 1 , TIMEOUT);
if(-1 == ret)
{
perror("poll");
return 1;
}
else if (0 == ret) //超时
{
printf("\n%d seconds elapsed.\n",TIMEOUT);
return 0;
/* code */
}
char buf[100];
int len;
if(fds[0].revents & POLLIN)
{
len = read(STDIN_FILENO, buf, 100);
if(-1 == len)
{
perror("read");
return 1;
}
else if (len != 0)
{
buf[len] = '\0';
printf("read:%s\n", buf);
return 0;
}
}
printf("Noting\n");
return 0;
}
注:由于本人也是刚接触这个,里面不免会有一些错误,请批判接受。上面只是这两个函数最基本的应用举例,对于更复杂的应用,请自行查阅相关资料。