一、定义和概念
定义:单进程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力
逻辑控制流在时间上的重叠叫做 并发
多路IO结局问题的开销比进线程都小
二、IO模型
1)阻塞IO
2)非阻塞IO EAGAIN 忙等待(CPU被占用)
3)信号驱动 IO
4)并行模型, 进程,线程
5)多路IO
三、多路IO
1.select(特征:轮询)
1>创建fd集合(可以理解为数组,大小一般为1024)
2>加入文件描述符
3>调用select(),等待时间到来
4>找到对应的fd,进行读写操作
5>标志位清0
函数原型:int select(int nfds, fd_set *readfds, fd_set *wirtefds, fd_set *exceptfds, struct timeval *timeout);
nfs:最大文件描述符(也可以写1024)
fd_set *readfds:监测读
fd_set *wirtefds:监测写(一般写NULL)
fd_set *exceptfds:监测异常(一般写NULL)
struct timeval *timeout:超时控制(一般写NULL)
为了配合select函数执行,有如下宏函数:
void FD_CLR(int fd, fd_set *set);
功能:将指定的set集合中编号为fd的描述符号删除。
int FD_ISSET(int fd, fd_set *set);
功能:判断值为fd的描述符是否在set集合中,
如果在则返回真,否则返回假。
void FD_SET(int fd, fd_set *set);
功能:将指定的fd描述符,添加到set集合中。
void FD_ZERO(fd_set *set);
功能:将指定的set集合中所有描述符删除。
2.epoll(方便更快的查找)
1>创建fd集合(二叉树)
2>加入关心的文件描述符
3>epoll_wait
4>epoll把准备就绪的fd,放入rev集合中(这里的集合可以理解为数组)
struct epoll_event rev[10];
epoll_ctl 有很多结构体,epoll支持边缘触发
四、select poll epoll的区别
- select
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
void FD_CLR(int fd, fd_set *set);
int FD_ISSET(int fd, fd_set *set);
void FD_SET(int fd, fd_set *set);
void FD_ZERO(fd_set *set);
- epoll
int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout)
3.epoll显著提高性能的前提是:监听大量描述符,并且每次触发事件的描述符文件非常少。
epoll的另外区别是:①epoll创建了描述符,记得close;②支持水平触发和边沿触发。
五、例题和代码
1.多路复用---非阻塞型
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char **argv)
{
int ret = mkfifo("fifo", 0666);
if (-1 == ret)
{
if (EEXIST != errno)
{
perror("mkfifo");
return 1;
}
}
int fd = open("fifo", O_RDONLY);
if (-1 == fd)
{
perror("open fifo");
return 1;
}
int flag = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, flag|O_NONBLOCK);
flag = fcntl(fileno(stdin), F_GETFL);
fcntl(0, F_SETFL, flag|O_NONBLOCK);
while (1)
{
char buf[128] = {0};
if(read(fd, buf, sizeof(buf))>0) //阻塞
{
printf("w:%s\n", buf);
}
if(fgets(buf, sizeof(buf), stdin) > 0)//阻塞
{
printf("fifo:%s", buf);
fflush(stdout);
}
}
close(fd);
// remove("fifo");
// system("pause");
return 0;
}
2.select---读文件
#include <asm-generic/errno-base.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char **argv)
{
int ret = mkfifo("fifo", 0666);
if(-1 == ret)
{
if(EEXIST != errno)
{
perror("mkfifo");
return 1;
}
}
int fd = open("fifo", O_RDONLY);
if(-1 == fd)
{
perror("open fifo");
return 1;
}
fd_set rd_set, tmpset;
FD_ZERO(&rd_set);
FD_ZERO(&tmpset);
FD_SET(fd, &tmpset);
FD_SET(0, &tmpset);
while(1)
{
rd_set = tmpset;
char buf[128] = {0};
select(fd + 1, &rd_set, NULL, NULL, NULL);
int i = 0;
for(i = 0; i < fd + 1; ++i)
{
if(FD_ISSET(i, &rd_set) && 0 == i)
{
fgets(buf, sizeof(buf), stdin);
printf("terminal:%s", buf);
}
if(FD_ISSET(i, &rd_set) && fd == i)
{
read(fd, buf, sizeof(buf));
printf("fifo:%s\n", buf);
}
}
}
close(fd);
return 0;
}
3.epoll---读文件
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/epoll.h>
int add_fd(int epfd, int fd)
{
struct epoll_event ev;
ev.events = EPOLLIN;
ev.data.fd = fd;
int ret = epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev);
if(-1 == ret)
{
perror("add fd");
return 1;
}
return 0;
}
int main(int argc, char **argv)
{
int ret = mkfifo("fifo", 0666);
if (-1 == ret)
{
if (EEXIST != errno)
{
perror("mkfifo");
return 1;
}
}
int fd = open("fifo", O_RDONLY);
if (-1 == fd)
{
perror("open fifo");
return 1;
}
//1.create fd set
int epfd = epoll_create(2);
if(-1 == epfd)
{
perror("epoll_create");
return 1;
}
//2.add fd to set
struct epoll_event rev[2];
add_fd(epfd, 0);
add_fd(epfd, fd);
while (1)
{
char buf[128] = {0};
//3 wait event
int ep_ret = epoll_wait(epfd, rev, 2, -1);
int i;
for(i = 0; i < ep_ret; ++i)
{
if(0 == rev[i].data.fd)
{
fgets(buf, sizeof(buf), stdin);
printf("terminal:%s", buf);
}
else
{
read(fd, buf, sizeof(buf));
printf("fifo:%s\n", buf);
}
}
}
close(fd);
return 0;
}
4.tcp-select 服务器端
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h> /* See NOTES */
#include <time.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
typedef struct sockaddr *(SA);
int main(int argc, char **argv)
{
int listfd = socket(AF_INET, SOCK_STREAM, 0);//监听套接字
if(-1 == listfd)
{
perror("socket");
return 1;
}
struct sockaddr_in ser, cli;// man 7 ip
bzero(&ser, sizeof(ser));
bzero(&cli, sizeof(cli));
ser.sin_family = AF_INET;
ser.sin_port = htons(50001);
ser.sin_addr.s_addr = inet_addr("192.168.0.119");
int ret = bind(listfd, (SA)&ser, sizeof(ser));
if(-1 == ret)
{
perror("bind");
return 1;
}
listen(listfd, 3);
fd_set rd_set, tmp_set;
FD_ZERO(&rd_set);
FD_ZERO(&tmp_set);
FD_SET(listfd, &tmp_set);
int max_fd = listfd;
socklen_t len = sizeof(cli);
while(1)
{
rd_set = tmp_set;
select(max_fd + 1, &rd_set, NULL, NULL, NULL);
int i;
for(i = listfd; i < max_fd + 1; ++i)
{
if(FD_ISSET(i, &rd_set) && i == listfd) //i是否准备好,装到rd_set中
{
int conn = accept(listfd, (SA)&cli, &len);
if(-1 == conn)
{
perror("conn");
continue;
}
FD_SET(conn, &tmp_set);
if(conn > max_fd)
{
max_fd = conn;
}
}
else if (FD_ISSET(i, &rd_set) && i != listfd)
{
int conn = i;
char buf[256] = {0};
ret = recv(conn, buf, sizeof(buf), 0);
if(ret <= 0)
{
FD_CLR(conn, &tmp_set);
close(conn);
printf("cli offline\n");
continue;
}
getpeername(conn, (SA)&cli, &len);
printf("cli ip:%s port :%d %s\n", inet_ntoa(cli.sin_addr),
ntohs(cli.sin_port), buf);
time_t tm;
time(&tm);
struct tm *info = localtime(&tm);
sprintf(buf, "%s %d:%d:%d\n",buf, info->tm_hour
, info->tm_min, info->tm_sec);
send(conn, buf, strlen(buf), 0);
}
}
}
// close(conn);
close(listfd);
return 0;
}
5.tcp-epoll 服务器端
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h> /* See NOTES */
#include <time.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/epoll.h>
typedef struct sockaddr *(SA);
int add_fd(int epfd, int fd)
{
struct epoll_event ev;
ev.events = EPOLLIN;
ev.data.fd = fd;
int ret = epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev);
if(-1 == ret)
{
perror("add fd");
return 1;
}
return 0;
}
int del_fd(int epfd, int fd)
{
struct epoll_event ev;
ev.events = EPOLLIN;
ev.data.fd = fd;
int ret = epoll_ctl(epfd, EPOLL_CTL_DEL, fd, &ev);
if(-1 == ret)
{
perror("add fd");
return 1;
}
return 0;
}
int main(int argc, char **argv)
{
int listfd = socket(AF_INET, SOCK_STREAM, 0);//监听套接字
if(-1 == listfd)
{
perror("socket");
return 1;
}
struct sockaddr_in ser, cli;// man 7 ip
bzero(&ser, sizeof(ser));
bzero(&cli, sizeof(cli));
ser.sin_family = AF_INET;
ser.sin_port = htons(50001);
ser.sin_addr.s_addr = inet_addr("192.168.0.119");
int ret = bind(listfd, (SA)&ser, sizeof(ser));
if(-1 == ret)
{
perror("bind");
return 1;
}
listen(listfd, 3);
int epfd = epoll_create(10);
if(-1 == epfd)
{
perror("epoll_create");
return 1;
}
struct epoll_event rev[10];
add_fd(epfd, listfd);
socklen_t len = sizeof(cli);
while(1)
{
int ep_ret = epoll_wait(epfd, rev, 10, -1);
int i;
for(i = 0; i < ep_ret; ++i)
{
if(listfd == rev[i].data.fd)
{
int conn = accept(listfd, (SA)&cli, &len);
if(-1 == conn)
{
perror("conn");
continue;
}
add_fd(epfd, conn);
}
else
{
int conn = rev[i].data.fd;
char buf[256] = {0};
getpeername(conn, (SA)&cli, &len);
ret = recv(conn, buf, sizeof(buf), 0);
if(ret <= 0)
{
del_fd(epfd, conn);
close(conn);
printf("cli ip:%s port:%d offline\n", inet_ntoa(cli.sin_addr),
ntohs(cli.sin_port));
continue;
}
printf("cli ip:%s port :%d %s\n", inet_ntoa(cli.sin_addr),
ntohs(cli.sin_port), buf);
time_t tm;
time(&tm);
struct tm *info = localtime(&tm);
sprintf(buf, "%s %d:%d:%d\n",buf, info->tm_hour
, info->tm_min, info->tm_sec);
send(conn, buf, strlen(buf), 0);
}
}
}
// close(conn);
close(listfd);
return 0;
}