I/O复用

背景知识

如果TCP客户同时处理两个输入: 标准输入和TCP套接字. 那么如果客户阻塞于标准输入期间(例如fgets()), 套接字收到的FIN或者RST信息就不会及时得到处理. 所以这里需要使用I/O复用, 是由select和poll这两个函数支持的.

为了更好地理解I/O复用, 这里总结一下UNIX下的5种I/O模型的基本区别:

阻塞式I/O : 默认情况下, 所有的套接字都是阻塞的. 一些慢系统调用也是阻塞的.

非阻塞式I/O : 当所请求的I/O操作非得把本进程投入睡眠才能完成时, 不要把本进程投入睡眠, 而是返回一个错误.

I/O复用: 用select的优势在于我们可以等待多个描述符

信号驱动式I/O: 让内核在描述符就绪时发送SIGIO信号通知我们, 这种模式的优势在于等待数据报到达期间进程是不被阻塞的.

异步I/O: 告知内核启动某个操作, 并让内核在整个操作完成后通知我们.

为了实现I/O复用, 这里使用select函数, 该函数允许进程指示内核等待多个事件中的任何一个发生, 并只在一个或多个事件发生或者经历过一段指定的时间再唤醒它.

  1. #include <sys/select.h>
  2. #include <sys/time.h>
  3. int select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, const struct timeval *timeout);

select函数的返回: 若有就绪描述符则为其数目, 若超时则为0, 若出错则为-1

使用select的服务器端程序

  1. #include "unp.h"
  2. #include <time.h>
  3. int
  4. main(int argc, char **argv)
  5. {
  6. int i, maxi, maxfd, listenfd, connfd, sockfd;
  7. int nready, client[FD_SETSIZE];
  8. ssize_t n;
  9. fd_set rset, allset;
  10. char buf[MAXLINE];
  11. socklen_t clilen;
  12. struct sockaddr_in cliaddr, servaddr;
  13. listenfd = Socket(AF_INET, SOCK_STREAM,0);
  14. bzero(&servaddr, sizeof(servaddr));
  15. servaddr.sin_family=AF_INET;
  16. servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
  17. servaddr.sin_port=htons(1234);
  18. Bind(listenfd, (SA*)&servaddr, sizeof(servaddr));
  19. Listen(listenfd, LISTENQ);
  20. maxfd=listenfd;
  21. maxi=-1;
  22. for(i=0;i<FD_SETSIZE;i++)
  23. client[i]=-1;
  24. FD_ZERO(&allset);
  25. FD_SET(listenfd, &allset);
  26. for(;;){
  27. rset=allset;
  28. nready=Select(maxfd+1, &rset, NULL, NULL, NULL);
  29. if(FD_ISSET(listenfd, &rset)){ // new client connection
  30. clilen=sizeof(cliaddr);
  31. connfd=Accept(listenfd, (SA*) &cliaddr, &clilen);
  32. for(i=0;i<FD_SETSIZE;i++){
  33. if(client[i]<0){
  34. client[i]=connfd;
  35. break;
  36. }
  37. }
  38. if(i==FD_SETSIZE)
  39. err_quit("too many clients");
  40. FD_SET(connfd, &allset);
  41. if(connfd>maxfd)
  42. maxfd=connfd;
  43. if(i>maxi)
  44. maxi=i;
  45. if(--nready<=0)
  46. continue;
  47. }
  48. for(i=0;i<=maxi;i++){ //check all clients for data
  49. if( (sockfd=client[i])<0)
  50. continue;
  51. if(FD_ISSET(sockfd, &rset)){
  52. if((n=Read(sockfd, buf, MAXLINE))==0){
  53. //connection closed by client
  54. Close(sockfd);
  55. FD_CLR(sockfd, &allset);
  56. client[i]=-1;
  57. }else
  58. Writen(sockfd, buf, n);
  59. if(--nready<=0)
  60. break; //no more readable descriptors
  61. }
  62. }
  63. }
  64. return 0;
  65. }

使用select函数的客户端程序

  1. #include "unp.h"
  2. void cli_echo(FILE *fp, int sockfd)
  3. {
  4. int maxfdp1, stdineof;
  5. fd_set rset;
  6. char buf[1000];
  7. int n;
  8. stdineof=0;
  9. FD_ZERO(&rset);
  10. for(;;){
  11. if(stdineof==0)
  12. FD_SET(fileno(fp),&rset);
  13. FD_SET(sockfd,&rset);
  14. maxfdp1=max(fileno(fp), sockfd)+1;
  15. Select(maxfdp1, &rset, NULL, NULL, NULL);
  16. if(FD_ISSET(sockfd, &rset)) {//socket is readable
  17. if( (n=Read(sockfd, buf, 1000))==0){
  18. if(stdineof==1)
  19. return;//normal termination
  20. else
  21. err_quit("cli_echo: server terminated prematurely");
  22. }
  23. Write(fileno(stdout),buf,n);
  24. }
  25. if(FD_ISSET(fileno(fp), &rset)){//input is readable
  26. if( (n=Read(fileno(fp),buf,1000))==0){
  27. stdineof=1;
  28. Shutdown(sockfd, SHUT_WR);
  29. FD_CLR(fileno(fp), &rset);
  30. continue;
  31. }
  32. Writen(sockfd, buf, n);
  33. }
  34. }
  35. }
  36. int
  37. main(int argc, char **argv)
  38. {
  39. int sockfd;
  40. struct sockaddr_in servaddr;
  41. if(argc!=2)
  42. err_quit("usage: tcpcli <IPaddress>");
  43. sockfd=Socket(AF_INET, SOCK_STREAM, 0);
  44. bzero(&servaddr, sizeof(servaddr));
  45. servaddr.sin_family=AF_INET;
  46. servaddr.sin_port=htons(1234);
  47. Inet_pton(AF_INET,argv[1], &servaddr.sin_addr);
  48. Connect(sockfd, (SA*) &servaddr, sizeof(servaddr));
  49. cli_echo(stdin,sockfd);
  50. exit(0);
  51. }

来自为知笔记(Wiz)

时间: 2024-10-08 09:04:17

I/O复用的相关文章

Java复用类

Java复用类 Java复用类一般有两种方法. 一,组合:在新的类中产生现有类的对象.由于新的类是由现有类的对象所组成,所以这种方法成为组合. import java.util.*; class WaterSource{ private String s; WaterSource(){ System.out.println("WaterSource()"); s="constructed"; } public String toString(){ return s;

Java 继承、多态与类的复用

摘要: 本文结合Java的类的复用对面向对象两大特征继承和多态进行了全面的介绍. 首先,我们介绍了继承的实质和意义,并探讨了继承,组合和代理在类的复用方面的异同.紧接着,我们依据继承引入了多态.介绍了它的实现机制和详细应用.此外,为了更好地理解继承和多态.我们对final关键字进行了全面的介绍. 在此基础上.我们介绍了Java中类的载入及初始化顺序.最后.我们对面向对象设计中三个十分重要的概念–重载.覆盖与隐藏进行了详细的说明. 要点: 继承 组合,继承,代理 多态 final 关键字 类载入及

Java多态——代码复用性

Java中,多态的意为相同的行为,不同的实现. 其中,多态又分为静态多态和动态多态.两者的区别在于:前者在编译器就可以确定运行期的最终结果,即编译时就可以确定调用哪个方法:而后者在编译期则不能确定运行效果,只有运行后,依据所绑定对象的的不同,才能确定运行效果,即运行中系统才能确定方法所指的对象.静态多态主要体现在方法的重载和单独使用方法重写上,而动态多态体现为动态绑定和方法的重写上. 引入动态绑定的概念之前,需要先了解转型技术.转型即数据类型的转换,通常发生在赋值符号左右两边数据类型不同的时候.

面向对象设计原则之六:组合/聚集复用原则

组合/聚合复用原则(Composite/Aggregate Reuse Principle CARP).组合和聚合都是对象建模中关联(Association)关系的一种.聚合表示整体与部分的关系,表示"含有",整体由部分组合而成,部分可以脱离整体作为一个独立的个体存在.组合则是一种更强的聚合,部分组成整体,而且不可分割,部分不能脱离整体而单独存在.在合成关系中,部分和整体的生命周期一样,组合的新的对象完全支配其组成部分,包括他们的创建和销毁.一个合成关系中成分对象是不能与另外一个合成关

Android学习笔记-构建一个可复用的自定义BaseAdapter

转载自http://www.runoob.com/w3cnote/android-tutorial-customer-baseadapter.html   作者:coder-pig 本节引言: 如题,本节给大家带来的是构建一个可复用的自定义BaseAdapter,我们每每涉及到ListView GridView等其他的Adapter控件,都需要自己另外写一个BaseAdapter类,这样显得非常麻烦, 又比如,我们想在一个界面显示两个ListView的话,我们也是需要些两个BaseAdapter

java 编程思想笔记(三)——类的复用

一:复用功能介绍 复用代码,除了对代码复制并加以改变是不够的,除此之外,还能够使用类而不破坏现有程序代码. java 中代码复用可以分为以下三类:组合,继承,代理. 二:组合 新的类中使用现有类的对象,新的类由现有类的对象组成. 三:继承 通过extends 语法来实现声明. 四:代理 java 对代理没有提供直接支持,但通过组合和继承完成了间接实现,代理是组合和继承的中庸之道.代理=继承+组合 具体理解可以参考相关代码! 五:基类方法被子类重载 子类重载父类的方法,不会屏蔽其在基类中的任何版本

关于技术上的重复与代码复用

项目遇到分布式与高并发的问题,我的疑惑是:那么多公司能应对高并发与分布式,为什么我们处理起来还是这么难? 我的分析: 很多公司指哪些公司:是不是生活中经常用到的:百度,淘宝等大公司?它们的技术水平更高,也经过了摸索. 别的公司做了,不代表做起来很容易.它们遇到的问题不会一一分享出来,代码.部署.运维也没有完全的.详细的开源出来 这应该是一个综合性的技术问题,既需要代码方面,还需要数据库设计.管理以及机器部署 如果要解决这些问题,应该怎么做? 继续分解这些问题,找到难点,系统的搜索.学习: 寻找分

ListView复用和优化详解

我们每一个Android开发人员对ListView的使用肯定是很熟悉的,然而多少人能真正的懂ListView的缓存机制呢,说白了就是ListView为了提高效率,而内部实现的一种优化,牺牲一点内存.而这种优化就需要复用ItemView(也就是item对应的View).那么下面楼主来对ListView和RecyclerView的item复用问题做一个深入的讲解先来一张大家学习的时候都遇到过的图这里写图片描述看不懂也没啥事,可以接着往下看,先有一个直观的认识首先来解答几个问题为什么会存在Item复用

Android中复用问题哲理性解析

Android中列表的复用机制提高了APP的运行效率,但随之而来的复用的问题总是让程序员们头痛,一个 bug找头天也找不到.我就把自己解决这方面的经验贡献出来供大家参考: 问题1:什么是复用 复用其实指的是复用View,而绑定View的数据是变化的. 问题2:复用出现的场景 在Adapter中,如果绑定View的数据的时候如果有if判断,往往很多人忘记了加else,这是大多数复用问题出现的根源. 实际场景:     比如每个item可能有或没有图片picarrList,之前我只加了if判断,如果