第6章 I/O多路复用
前一章节客户端同时处理两个输入:标准输入和TCP套接字,然而问题在于客户端阻塞于fgets调用期,服务器进程被杀死后,服务器tcp虽然可以正确发送一个fin,但进程正阻塞于标准输入,它无法看到eof,直到从套接字读为止.这样的进程需要一种预先告诉内核的能力,一旦内核发现进程指定的一个或者多个I/O就绪,它就通知进程,这就叫做I/O复用
#include <sys/select.h>#include<sys/time.h>int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);
struct timeval{long tv_sec;long tv_usec;};
void FD_ZERO(fd_set*fdset);void FD_SET(int fd,fd_set*fdset);void FD_CLR(int fd,fd_set *fdset);int FD_ISSET(int fd,fd_set *fdset);
fd_set rset;FD_ZERO(&rset);FD_SET(1,&rset);FD_SET(4,&rset);FD_SET(5,&rset);

#include "unp.h"voidstr_cli(FILE *fp, int sockfd){int maxfdp1, stdineof;fd_set rset;char buf[MAXLINE];int n;stdineof = 0;FD_ZERO(&rset);for ( ; ; ) {if (stdineof == 0)FD_SET(fileno(fp), &rset);FD_SET(sockfd, &rset);maxfdp1 = max(fileno(fp), sockfd) + 1;Select(maxfdp1, &rset, NULL, NULL, NULL);- if (FD_ISSET(sockfd, &rset)) { /* socket is readable */
if ( (n = Read(sockfd, buf, MAXLINE)) == 0) {if (stdineof == 1)return; /* normal termination */elseerr_quit("str_cli: server terminated prematurely");}- Write(fileno(stdout), buf, n);
}- if (FD_ISSET(fileno(fp), &rset)) {
if ( (n = Read(fileno(fp), buf, MAXLINE)) == 0) {stdineof = 1;Shutdown(sockfd, SHUT_WR);FD_CLR(fileno(fp), &rset);continue;}Writen(sockfd, buf, n);}}}int main(int argc,char *argv[]){if(argc<3)err_quit("please input IP Address : Port");int connfd=Socket(AF_INET,SOCK_STREAM,0);struct sockaddr_in servaddr;servaddr.sin_family=AF_INET;servaddr.sin_port=htons(atoi(argv[2]));Inet_pton(AF_INET,argv[1],&servaddr.sin_addr);Connect(connfd,(SA*)&servaddr,sizeof(servaddr));str_cli(stdin,connfd);return 0;}
#include "unp.h"static int create_sock(char *ip){int listenfd;listenfd=Socket(AF_INET,SOCK_STREAM,0);struct sockaddr_in servaddr;servaddr.sin_family=AF_INET;servaddr.sin_port=htons(atoi(ip));Inet_pton(AF_INET,"0.0.0.0",&servaddr.sin_addr);Bind(listenfd,(SA*)&servaddr,sizeof(servaddr));Listen(listenfd,20);return listenfd;}static void deal_cli(int listenfd){int connfd,i;fd_set allset,rset;//allset保存的整个描述符集。但并不会直接select.每次select都会使得清0.很不方便。于是当要select的时候把它付给rsetFD_ZERO(&allset);FD_ZERO(&rset);char buf[1024];//从客户端读取的缓冲区int ret=0;int maxfd=listenfd;//select第一个参数int allfd[MAXLINE];//连接数组for(i=0;i<MAXLINE;++i){allfd[i]=-1;}int nready; //select I/O已经就绪int max=0;//表示已完成连接数组中最大的队列FD_SET(listenfd,&allset);//将监听连接加入allset集合中for(;;){rset=allset;//更新nready=Select(maxfd+1,&rset,NULL,NULL,NULL);if(FD_ISSET(listenfd,&rset)){connfd=Accept(listenfd,NULL,NULL); //从已连接套接字拿取一个出来for(i=0;i<MAXLINE;++i){ //遍历整个已连接数组。找到alifd[i]等于-1(表示该位可用)if(allfd[i]<0){allfd[i]=connfd;break;}}FD_SET(connfd,&allset);if(maxfd<connfd) //每次都需要比较maxfd跟新来的描述符值大小maxfd=connfd;if(max<i)//还要更新最后可连接数组最后的那个已连接索引max=i;if(--nready<=0) //如果该连接已经处理完成。那就continue;}for(i=0;i<max+1;++i){ //遍历以此处理所有已完成连接if(allfd[i]!=-1)//跳过if(FD_ISSET(allfd[i],&rset)){ret=read(allfd[i],buf,1024);if(ret==0){close(allfd[i]);FD_CLR(allfd[i],&allset);allfd[i]=-1;}else if(ret<0){perror("error");}elseWriten(allfd[i],buf,ret);}if(--nready<=0) //如果处理最后一个连接。继续返回到select中去continue;}}}- }
- int main(int argc,char *argv[])
{if(argc<2){err_quit("please input ip aiddress\n");}int listenfd=create_sock(argv[1]);//创建一个套接字deal_cli(listenfd);//开始处理细节return 0;}
#include <poll.h>- int poll(struct pollfd *fds, nfds_t nfds, int timeout);
struct pollfd {int fd; /* file descriptor */short events; /* requested events */short revents; /* returned events */};
/* include fig01 */#include "unp.h"//#include <limits.h> /* for OPEN_MAX */intmain(int argc, char **argv){int i, maxi, listenfd, connfd, sockfd;int nready;ssize_t n;char buf[MAXLINE];socklen_t clilen;long OPEN_MAX = sysconf(_SC_OPEN_MAX);struct pollfd client[OPEN_MAX];struct sockaddr_in cliaddr, servaddr;listenfd = Socket(AF_INET, SOCK_STREAM, 0);bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_port = htons(SERV_PORT);Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));Listen(listenfd, LISTENQ);client[0].fd = listenfd;client[0].events = POLLRDNORM;for (i = 1; i < OPEN_MAX; i++)client[i].fd = -1; /* -1 indicates available entry */maxi = 0; /* max index into client[] array *//* end fig01 *//* include fig02 */for ( ; ; ) {nready = Poll(client, maxi+1, INFTIM);if (client[0].revents & POLLRDNORM) { /* new client connection */clilen = sizeof(cliaddr);connfd = Accept(listenfd, (SA *) &cliaddr, &clilen);#ifdef NOTDEFprintf("new client: %s\n", Sock_ntop((SA *) &cliaddr, clilen));#endiffor (i = 1; i < OPEN_MAX; i++)if (client[i].fd < 0) {client[i].fd = connfd; /* save descriptor */break;}if (i == OPEN_MAX)err_quit("too many clients");client[i].events = POLLRDNORM;if (i > maxi)maxi = i; /* max index in client[] array */if (--nready <= 0)continue; /* no more readable descriptors */}for (i = 1; i <= maxi; i++) { /* check all clients for data */if ( (sockfd = client[i].fd) < 0)continue;if (client[i].revents & (POLLRDNORM | POLLERR)) {if ( (n = read(sockfd, buf, MAXLINE)) < 0) {if (errno == ECONNRESET) {/*4connection reset by client */#ifdef NOTDEFprintf("client[%d] aborted connection\n", i);#endifClose(sockfd);client[i].fd = -1;} elseerr_sys("read error");} else if (n == 0) {/*4connection closed by client */#ifdef NOTDEFprintf("client[%d] closed connection\n", i);#endifClose(sockfd);client[i].fd = -1;} elseWriten(sockfd, buf, n);if (--nready <= 0)break; /* no more readable descriptors */}}}}/* end fig02 */
第6章 I/O多路复用的更多相关文章
- LPC43xx SGPIO Experimentation
LPC4350 SGPIO Experimentation The NXP LPC43xx microcontrollers have an interesting, programmable ser ...
- 《Linux/UNIX系统编程手册》第63章 IO多路复用、信号驱动IO以及epoll
关键词:fasync_helper.kill_async.sigsuspend.sigaction.fcntl.F_SETOWN_EX.F_SETSIG.select().poll().poll_wa ...
- Netty源码分析第1章(Netty启动流程)---->第4节: 注册多路复用
Netty源码分析第一章:Netty启动流程 第四节:注册多路复用 回顾下以上的小节, 我们知道了channel的的创建和初始化过程, 那么channel是如何注册到selector中的呢?我们继 ...
- 第15章 高并发服务器编程(2)_I/O多路复用
3. I/O多路复用:select函数 3.1 I/O多路复用简介 (1)通信领域的时分多路复用 (2)I/O多路复用(I/O multiplexing) ①同一线程,通过“拨开关”方式,来同时处理多 ...
- 【面试】一篇文章帮你彻底搞清楚“I/O多路复用”和“异步I/O”的前世今生
曾经的VIP服务 在网络的初期,网民很少,服务器完全无压力,那时的技术也没有现在先进,通常用一个线程来全程跟踪处理一个请求.因为这样最简单. 其实代码实现大家都知道,就是服务器上有个ServerSoc ...
- 深入理解计算机操作系统——12章:多进程,IO多路复用
三种并行的应用程序: 1. 基于进程的并发编程: 2. 基于IO多路复用的并发: 3. 基于线程的并发编程: 12.1 基于进程的并发编程 进程的优劣: (1)进程间共享文件表,但不共享用户地址空间, ...
- 一篇文章帮你彻底搞清楚“I/O多路复用”和“异步I/O”的前世今生
在网络的初期,网民很少,服务器完全无压力,那时的技术也没有现在先进,通常用一个线程来全程跟踪处理一个请求.因为这样最简单. 其实代码实现大家都知道,就是服务器上有个ServerSocket在某个端口监 ...
- Python(七)Socket编程、IO多路复用、SocketServer
本章内容: Socket IO多路复用(select) SocketServer 模块(ThreadingTCPServer源码剖析) Socket socket通常也称作"套接字" ...
- 【翻译】XV6-DRAFT as of September 3,2014 第0章 操作系统接口
操作系统接口 操作系统的任务是让多个程序共享计算机(资源),并且提供一系列基于计算机硬件的但更有用的服务.操作系统管理并且把底层的硬件抽象出来,举例来说,一个文字处理软件(例如word)不需要关心计算 ...
随机推荐
- 救援(BFS)
题目描述: 在你的帮助下,Oliver终于追到小X了,可有一天,坏人把小X抓走了.这正是Oliver英雄救美的时候.所以,Oliver又找到哆啦A梦,借了一个机器,机器显示出一幅方格地图,它告诉Oli ...
- Qt的由来和发展
一.Qt的由来 Haavard Nord 和Eirik Chambe-Eng于1991年开始开发"Qt",1994年3月4日创立公司,早名为Quasar Technologies, ...
- nginx的url重写
1.1 简介 url重写由ngx_http_rewrite_module模块提供,默认会安装,但该模块功能的实现需要pcre.URL重写技术不仅要求掌握几个指令的语法.熟悉简单的正则表达式,还需要尽量 ...
- vue_music:封装scroll.vue组件
在项目中经常用到滚动,结合Better-scroll封装了sroll.vue组件参考链接:https://ustbhuangyi.github.io...http://www.imooc.com/ar ...
- 3.layhm框架的流程与Boot类启动
思路 在项目根目录里新建好对应的目录 cmd里在项目根目录里,composer init初使化,一路回车 把要自动加载的文件和目录定在composer.json文件的autoload里,file是自动 ...
- 字符串:HDU5371-Hotaru's problem(manacher 的应用)
Hotaru's problem Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others) Pr ...
- Linux学习-备份策略
每部主机的任务都不相同,重要的数据也不相同,重要性也不一样,因此,每个人的备份思考角度都不一样! 备份分为两大部分,一个是每日备份经常性变动的重要数据, 一个则是每周备份就不常变动的信息.这个时候我就 ...
- P2598 [ZJOI2009]狼和羊的故事(最小割)
P2598 [ZJOI2009]狼和羊的故事 题目描述 “狼爱上羊啊爱的疯狂,谁让他们真爱了一场:狼爱上羊啊并不荒唐,他们说有爱就有方向......” Orez听到这首歌,心想:狼和羊如此和谐,为什么 ...
- joyoi tyvj1313 [NOIP2010初赛]烽火传递
单调队列优化dp #include <iostream> #include <cstdio> using namespace std; int dp[1000005], n, ...
- 浅谈抓取网页数据(奉上Demo)
Demo源码 背景 曾经在公司做过一个比价系统,就是抓取其它网站上商品的价格并和自己公司的商品进行对应,然后展示出来,给pm提供一个定价的参考.后来,有同事的朋友在找工作的时候,猎头让其做一个抓取去哪 ...