zoukankan      html  css  js  c++  java
  • 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. }








  • 相关阅读:
    未能从程序集 C:Program Files (x86)MSBuild14.0inMicrosoft.Data.Entity.Build.Tasks.dll 加载任务“EntityClean”
    asp.net mvc 4 项目升级到 asp.net mvc5
    SQL Server查看所有表大小,所占空间
    0x80072f8a未指定的错误
    vs2012 aps.net4.0/4.5尚未在web服务器上注册
    vsphere 出现“在主机的当前连接状况下不允许执行该操作”
    sql server 发布时提示'dbo.sysmergepublications'无效的解决办法
    sql server更改机器名后更改数据库机器名
    Ajax向后台传入File类型参数
    上传下载Azure Blob里的Excel文件。
  • 原文地址:https://www.cnblogs.com/gremount/p/5785566.html
Copyright © 2011-2022 走看看