1.阻塞IO
CPU占用率低,等待资源时将任务挂起,不占用CPU资源,等到拿到资源后继续向下执行
向管道中写入数据
write.c
#include "../head.h"
int main()
{int fd;mkfifo("/tmp/myfifo",0777);char tmpbuff[100]={0};fd=open("/tmp/myfifo",O_WRONLY);if(fd==-1){return -1;}while(1){memset(tmpbuff,0,sizeof(tmpbuff));fgets(tmpbuff,sizeof(tmpbuff),stdin);tmpbuff[strlen(tmpbuff)-1]='\0';write(fd,tmpbuff,strlen(tmpbuff));}close(fd);return 0;
}
read.c: 先从终端写入数据,再从管道读入数据,管道必须等终端写入才可以读,第二次循环,终端也必须等第一次循环管道读入数据后才能继续从终端写,互相阻塞
#include "head.h"int main(void)
{int fd = 0;char tmpbuff[4096] = {0};mkfifo("/tmp/myfifo", 0777);fd = open("/tmp/myfifo", O_RDONLY);if (-1 == fd){perror("fail to open");return -1;}while (1){memset(tmpbuff, 0, sizeof(tmpbuff));read(fd, tmpbuff, sizeof(tmpbuff));printf("FIFO:%s\n", tmpbuff);memset(tmpbuff, 0, sizeof(tmpbuff));gets(tmpbuff);printf("STDIN:%s\n", tmpbuff);}close(fd);return 0;
}
2.非阻塞IO
能够让任务不阻塞,效率低,因为没有数据时,CPU一直空转
write.c和第一个相同
read.c:将管道和stdin流都设置为非阻塞属性
#include "../head.h"
int main()
{int fd;ssize_t nsize=0;int flags=0;char tmpbuff[200]={0};char *pret=NULL;mkfifo("/tmp/myfifo",0777);fd=open("/tmp/myfifo",O_RDONLY);if(fd==-1){return -1;}/*获得fd文件描述符的属性*/flags=fcntl(fd,F_GETFL);/*在现有属性中加入非阻塞属性*/flags|=O_NONBLOCK;/*将新属性设置回文件描述符中*/fcntl(fd,F_SETFL,flags);/*设置stdin的属性为非阻塞属性*/flags=fcntl(0,F_GETFL);flags|=O_NONBLOCK;fcntl(0,F_SETFL,flags);while(1){/*从管道中读数据*/memset(tmpbuff,0,sizeof(tmpbuff));nsize=read(fd,tmpbuff,sizeof(tmpbuff));if(nsize>0){printf("FIFO:%s\n",tmpbuff);}/*从终端读数据*/memset(tmpbuff,0,sizeof(tmpbuff));pret=gets(tmpbuff);if(pret!=NULL){printf("STDIN:%s\n",tmpbuff);}}close(fd);return 0;
}
3.异步IO
将一个文件描述符设定为异步IO,当IO有事件发生时,内核会向用户层发送SIGIO信号提醒用户层处理事件
write.c和第一个相同
read.c:将管道设定为异步IO,当管道中有数据写入时发送信号,执行处理函数
#include "../head.h"
int fd;
void handler(int signo)
{char tmpbuff[256]={0};memset(tmpbuff,0,sizeof(tmpbuff));read(fd,tmpbuff,sizeof(tmpbuff));printf("FIFO:%s\n",tmpbuff);}
int main()
{ssize_t nsize=0;int flags=0;char tmpbuff[200]={0};char *pret=NULL;/*当文件描述符有信号发生时(管道写入数据),发送信号通知,执行处理函数*/signal(SIGIO,handler);mkfifo("/tmp/myfifo",0777);fd=open("/tmp/myfifo",O_RDONLY);if(fd==-1){return -1;}/*获得fd文件描述符的属性*/flags=fcntl(fd,F_GETFL);/*将fd设置为异步IO(文件描述符发生可以读的事件,会发送信号通知)*/flags|=O_ASYNC;/*将新属性设置回文件描述符中*/fcntl(fd,F_SETFL,flags);//通知给当前进程fcntl(fd,F_SETOWN,getpid());while(1){memset(tmpbuff,0,sizeof(tmpbuff));pret=gets(tmpbuff);if(pret!=NULL){printf("STDIN:%s\n",tmpbuff);}}close(fd);return 0;
}
4.多路复用IO
1.select
监听文件描述符集合,将所有要监听的事件加入集合中,使用select监听所有事件,当集合中有事件发生, select不再阻塞,同时select会将产生事件的文件描述符留在集合中,而把没有产生事件的文件描述符从集合中踢出,所以留在集合中的文件描述即为产生事件的文件描述符,对其处理即可
函数接口:
1.int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
功能:
监听文件描述符是否有事件发生
参数:
nfds:最大文件描述符的值 + 1
readfds:读文件描述符集合
writefds:写文件描述符集合
exceptfds:异常文件描述符集合
timeout:超时时间
返回值:
成功返回产生事件的文件描述符个数
失败返回-1
timeout时间到达仍然没有产生的事件返回0
void FD_CLR(int fd, fd_set *set);
功能:将fd从集合中清除
int FD_ISSET(int fd, fd_set *set);
功能:判断fd是否仍在文件描述符集合中
void FD_SET(int fd, fd_set *set);
功能:将fd加入文件描述符集合中
void FD_ZERO(fd_set *set);
功能:将文件描述符集合清0
write:与第一个相同
read.c:当管道或者终端某一个有时间发生时,留下发生事件的那个文件描述符,其余删除,检测哪个文件描述符还在文件描述符集合中,就处理哪个
#include "../head.h"
int fd;int main()
{ssize_t nsize=0;int flags=0;char tmpbuff[200]={0};char *pret=NULL;fd_set rdfds;fd_set tmpfds;int maxfd;int nready=0;mkfifo("/tmp/myfifo",0777);fd=open("/tmp/myfifo",O_RDONLY);if(fd==-1){return -1;}/* 将文件描述符集合清0 */FD_ZERO(&rdfds);FD_SET(fd,&rdfds);/*将stdin加入文件描述符集合中*/FD_SET(0,&rdfds);maxfd=fd;while(1){tmpfds=rdfds;//监听发生事件的文件描述符,将其余文件描述符从文件描述符集合中清除nready=select(maxfd+1,&tmpfds,NULL,NULL,NULL);if(-1==nready){perror("failed to select");return -1;}/*判断fd是否仍在文件描述符集合中*/if(FD_ISSET(fd,&tmpfds)){memset(tmpbuff,0,sizeof(tmpbuff));read(fd,tmpbuff,sizeof(tmpbuff));printf("FIFO:%s\n",tmpbuff);} /*判断stdin是否仍在文件描述符集合中*/if(FD_ISSET(0,&tmpfds)){memset(tmpbuff,0,sizeof(tmpbuff));pret=gets(tmpbuff);if(pret!=NULL){printf("STDIN:%s\n",tmpbuff);}}}close(fd);return 0;
}
TCP多路复用应用
client向server发送数据,再接收server的回复
client.c
#include "../head.h"
int CreateTcpConnection()
{int sockfd;int ret=0;struct sockaddr_in sendaddr;//创建套接字sockfd=socket(AF_INET,SOCK_STREAM,0);if(sockfd==-1){return -1;}sendaddr.sin_family=AF_INET;sendaddr.sin_port=htons(50000);sendaddr.sin_addr.s_addr=inet_addr("192.168.0.187");//发送连接ret=connect(sockfd,(struct sockaddr *)&sendaddr,sizeof(sendaddr));if(ret==-1){return -1;}return sockfd;
}
int HandleConnection(int sockfd)
{char tmpbuff[1024]={0};static int cnt=0;ssize_t nsize=0;//发送数据sprintf(tmpbuff,"hello world --%d",cnt);nsize=send(sockfd,tmpbuff,strlen(tmpbuff),0);if(nsize==-1){return -1;}cnt++;//接收数据memset(tmpbuff,0,sizeof(tmpbuff));nsize=recv(sockfd,tmpbuff,sizeof(tmpbuff),0);if(nsize==-1){return -1;}else if(nsize==0){return 0;}printf("RECV:%s\n",tmpbuff);return nsize;
}
int main()
{int sockfd;int ret=0;sockfd=CreateTcpConnection();if(sockfd==-1){printf("连接服务器出错\n");return -1;}while(1){ret=HandleConnection(sockfd);if(ret==-1){printf("连接出错\n");break;}else if(ret==0){printf("关闭连接\n");break;}sleep(1);}//关闭套接字close(sockfd);return 0;
}
server.c:监听是否有新的连接请求,如果sockfd产生事件,处理新的请求,并将新的文件描述符加入集合,下一次一起监听,遍历所有已连接客户端,要是某个客户端有事件发生,就执行它的处理函数:接收来自客户端的数据,并回复客户端新拼接的数据
#include "../head.h"
int CreateTcpConnection()
{int sockfd;int ret=0;struct sockaddr_in recvaddr;sockfd=socket(AF_INET,SOCK_STREAM,0);if(sockfd==-1){return -1;}recvaddr.sin_family=AF_INET;recvaddr.sin_port=htons(50000);recvaddr.sin_addr.s_addr=inet_addr("192.168.0.187");ret=bind(sockfd,(struct sockaddr *)&recvaddr,sizeof(recvaddr));if(ret==-1){return -1;}ret=listen(sockfd,20);if(ret==-1){return -1;}return sockfd;}
int HandleConnection(int confd)
{char tmpbuff[1024]={0};ssize_t nsize=0;//接收数据并打印nsize=recv(confd,tmpbuff,sizeof(tmpbuff),0);if(nsize==-1){return -1;}else if(nsize==0){return 0;}printf("RECV:%s\n",tmpbuff);
//返回数据sprintf(tmpbuff,"%s--echo",tmpbuff);nsize=send(confd,tmpbuff,strlen(tmpbuff),0);if(nsize==-1){return -1;}return nsize;
}
int main()
{int sockfd;int i=0;int maxfd=0;fd_set rdfds;fd_set tmpfds;int nready=0;int ret=0;int confd;//创建套接字sockfd=CreateTcpConnection();if (-1 == sockfd){printf("创建监听套接字失败\n");return -1;}//将sockfd加入监听集合中FD_ZERO(&rdfds);FD_SET(sockfd,&rdfds);maxfd=sockfd;while(1){//开始监听tmpfds=rdfds;nready=select(maxfd+1,&tmpfds,NULL,NULL,NULL);if(nready==-1){perror("failed to select");return -1;}// 如果sockfd产生事件,处理新的请求,并将新的文件描述符加入集合,下一次一起监听if(FD_ISSET(sockfd,&tmpfds)){confd=accept(sockfd,NULL,NULL);if(confd==-1){FD_CLR(sockfd,&rdfds);close(sockfd);continue;}maxfd=maxfd>confd?maxfd:confd;FD_SET(confd,&rdfds);}//遍历所有已经连接的客户端中是否有事件发生for(i=sockfd+1;i<=maxfd;i++){if(FD_ISSET(i,&tmpfds)){ret=HandleConnection(i);if(ret==-1){printf("连接异常\n");FD_CLR(i,&rdfds);close(i);continue;}else if(ret==0){printf("关闭连接\n");FD_CLR(i,&rdfds);close(i);continue;}}}}close(sockfd);return 0;
}
运行结果:一个服务器连接两个客户端(可以连接更多的客户端)