IO的多路复用和信号驱动
2015-06-13 23:54
483 查看
Linux为多路复用IO提供了较多的接口,有select(),pselect(),poll()的方式,继承自BSD和System V 两大派系。
select模型比较简单,“轮询”检测fd_set的状态,然后再采取相应的措施。
信号驱动模型有必要仔细研究一下,一般有如下步骤:
设置安装函数,信号是驱动信号是SIGIO(最好使用sigaction的方式,方便设置flag为SA_RESTART,因为client中读取终端的syscall可能会被中断,有必要重启。当然,使用signal()的方式然后再对errno进行判断是否为ETNTR,自行重启也是一种方法。但是signal()的可移植性问题,我强烈不建议使用)
设置fd的属主。F_SETOWN,要接受信号的进程,fcntl().
设置fd的异步标志。小细节,用'|'添加,要把之前的状态保留,也就是先F_GETFL再F_SETFL。(注意:在打开文件open()时设置标识O_ASYNC无实质效果)
On Linux, specifying the O_ASYNC
flag when calling open() has no effect. To enable signal-driven I/O, we must
instead set this flag using the fcntl() F_SETFL operation (Section 5.3).————《the linux programming interface》 4.3.1
sigaction()安装
具体进下文client例程。
写了一个聊天程序的demo,把这两种技术都使用了。服务端采取多路复用的IO方式,代替多进(线)程的模型,客服端采取的是信号驱动,如下:
容易产生bug的地方都写注释里边了。
serv.c
client:
select模型比较简单,“轮询”检测fd_set的状态,然后再采取相应的措施。
信号驱动模型有必要仔细研究一下,一般有如下步骤:
设置安装函数,信号是驱动信号是SIGIO(最好使用sigaction的方式,方便设置flag为SA_RESTART,因为client中读取终端的syscall可能会被中断,有必要重启。当然,使用signal()的方式然后再对errno进行判断是否为ETNTR,自行重启也是一种方法。但是signal()的可移植性问题,我强烈不建议使用)
设置fd的属主。F_SETOWN,要接受信号的进程,fcntl().
设置fd的异步标志。小细节,用'|'添加,要把之前的状态保留,也就是先F_GETFL再F_SETFL。(注意:在打开文件open()时设置标识O_ASYNC无实质效果)
On Linux, specifying the O_ASYNC
flag when calling open() has no effect. To enable signal-driven I/O, we must
instead set this flag using the fcntl() F_SETFL operation (Section 5.3).————《the linux programming interface》 4.3.1
sigaction()安装
具体进下文client例程。
写了一个聊天程序的demo,把这两种技术都使用了。服务端采取多路复用的IO方式,代替多进(线)程的模型,客服端采取的是信号驱动,如下:
容易产生bug的地方都写注释里边了。
serv.c
#include <sys/select.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <fcntl.h> #include <stdlib.h> #include <string.h> #include <stdio.h> #include <signal.h> void endServ(int sig) { printf("Server ended!\n"); exit(-1); } int main() { // signal struct sigaction act; sigemptyset(&act.sa_mask); act.sa_handler = endServ; act.sa_flags = 0; sigaction(SIGINT,&act,0); printf("This server is started,enter CTRL+C to end. \n"); int servfd = socket(AF_INET,SOCK_STREAM,0); if(servfd == -1) { printf("something wrong with socket():%m\n"); exit(-1); } struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(9999); inet_pton(AF_INET,"127.0.0.1",&addr.sin_addr); if (bind(servfd,(struct sockaddr*)&addr,sizeof(addr)) == -1) { printf("Some thing wrong with bind():%m\n"); exit(-1); } printf("Bind success!\n"); listen(servfd,20); int numbers=0;//how many clients has accepted fd_set fs; FD_ZERO(&fs); int client_fd[100]; int i; for(i=0;i<100;++i) { client_fd[i] = -1; } int maxfd=0; char buf[1024]; for(;;) { maxfd =0; FD_ZERO(&fs); FD_SET(servfd,&fs); maxfd = maxfd>servfd?maxfd:servfd; for(i=0;i<numbers;++i) { if(client_fd[i] != -1) { FD_SET(client_fd[i],&fs); maxfd = maxfd>client_fd[i]?maxfd:client_fd[i]; } } int res = select(maxfd+1,&fs,0,0,0); if(res == -1) { printf("Something wrong with select():%m\n"); exit(-1); } if(FD_ISSET(servfd,&fs) && numbers < 100) { printf("New client!\n"); client_fd[numbers] = accept(servfd,0,0); numbers++; } for(i=0;i<numbers;++i) { bzero(buf,sizeof(buf)); //judge if client_fd[i] equal -1 is necessary //if a client quited,next time the program will //have a segment default //also it should be in the front. if(client_fd[i] != -1 && FD_ISSET(client_fd[i],&fs)) { res = recv(client_fd[i],buf,sizeof(buf),0); if(res == 0) { printf("A client quit\n"); close(client_fd[i]); FD_CLR(client_fd[i],&fs); client_fd[i] = -1; } else if(res == -1) { printf("Something wrong with net.\n"); exit(-1); } else { int j; for(j=0;j<numbers;++j) { if(client_fd[j] != -1) send(client_fd[j],buf,sizeof(buf),0); } } } } } }
client:
#include <signal.h> #include <unistd.h> #include <sys/socket.h> #include <fcntl.h> #include <arpa/inet.h> #include <netinet/in.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <errno.h> static int fd; void show(int sig) { char buf[1024] = {0}; int n = read(fd,buf,sizeof(buf)); buf = 0; write(1,"MSG:",strlen("MSG:")); write(1,buf,strlen(buf)); } int main() { struct sigaction act; sigemptyset(&act.sa_mask); act.sa_handler = show; //This is necessary,in last loop read() counld be interrupt; act.sa_flags = SA_RESTART; sigaction(SIGIO,&act,0); fd = socket(AF_INET,SOCK_STREAM,0); if(fd == -1) { printf("Cannot get socketfd!:%m\n"); exit(-1); } struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(9999); inet_pton(AF_INET,"127.0.0.1",&addr.sin_addr); if(connect(fd,(struct sockaddr*)&addr,sizeof(addr)) != -1) printf("connect success!\n"); else exit(-1); int flag = fcntl(fd,F_GETFL); flag |= O_ASYNC; fcntl(fd,F_SETFL,flag); fcntl(fd,F_SETOWN,getpid()); char buffer[1024]={0}; for(;;) { int n = read(0,buffer,sizeof(buffer)); if(n==-1) break; send(fd,buffer,n,0); } write(1,"Closed.",strlen("Closed.")); }
相关文章推荐
- 2015061309 - 推荐李公子博客
- linux 进程基础(四)
- Zend framework2 - 联合查询分页
- LA3938:"Ray, Pass me the dishes!"(线段树)
- java final 关键字的详解!
- 有感于STL的内存管理
- oracle pl sql 抛出例外
- 谈谈软件定义存储
- c++学习
- 最新的swift 没有了fromraw方法,该如何获取初始值为x的枚举值
- 双向链表(1) - 基本介绍以及插入节点
- 2015061308 - 两个jsp页面之间数据传递的方式
- Android popupwindow 示例程序一
- RSA加密算法加密与解密过程解析
- mysql 存储过程中limit(#1064 You have an error in your SQL syntax)
- You don't have permission to access / on this server for debian_8
- java获取文件名的三种方法
- php基础知识总结
- Android 自定义dialog
- cocos中Widget 的用法