zoukankan      html  css  js  c++  java
  • io多路复用-select()

    参照《Unix网络编程》相关章节内容,实现了一个简单的单线程IO多路复用服务器与客户端。

    普通迭代服务器,由于执行recvfrom则会发生阻塞,直到客户端发送数据并正确接收后才能够返回,一个服务器进程只能服务于一个客户端,解决这种问题可采用多线程方式(参见虚拟机隐藏进程检测工具实现)和IO多路复用select和poll,select()机制的优势在于可以同时等待多个描述符就绪。

    与IO复用密切相关的另一种IO模型是在多线程中使用阻塞式IO。

    简要描述select机制:

    fd_set rset

    void FD_ZERO(fd_set *fdset)

    void FD_SET(int fd, fd_set *fdset)

    void FD_CLR(int fd, fd_set *fdset)

    void FD_ISSET(int fd, fd_set *fdset)

    其中rset类型为fd_set,可理解为一个位图,用于标识哪些描述符正在被监听。FD_ZERO用于初始化rset,FD_SET用于设置新的用于被监听的描述符,FD_CLR用于清空rset,FD_ISSET用户判断具体哪个描述符就绪。

    由于tcp服务器中具有监听套接字与已连接套接字两个概念,在服务器端实现中主要过程如下:

    1. 初始化rset,转2;
    2. 添加监听套接字到rset中,转3;
    3. select阻塞等待是否存在描述符就绪,转4;
    4. 当客户端连接后,rset中设置的监听套接字就绪,添加已连接套接字到rset,转5;
    5. 判断哪个描述符就绪(一般为新的已连接套接字),进行套接字读写操作,转1;

    在《Unix网络编程》中,上述步骤5结束后直接转2,但在实际测试与资料查阅中,由于内核会修改描述符内容,使得需要重新初始化rset才能有效。

    代码测试:

    服务端:

      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 #include <sys/select.h>
      4 #include <unistd.h>
      5 #include <sys/socket.h>
      6 #include <netinet/in.h>
      7 #include <linux/netlink.h>
      8 #include <string.h>
      9 #include <arpa/inet.h>
     10 
     11 #define IP_ADDR 127.0.0.1
     12 #define IP_PORT 8081
     13 
     14 #define LISTEN_NUM 10
     15 
     16 #define MAXLINE 1024
     17 
     18 int main(void)
     19 {
     20     struct sockaddr_in clitaddr, servaddr;
     22     unsigned int clilen;
     23     int listenfd, connfd, sockfd, ret;
     24 
     25     int maxindex, i, n;
     26 
     27     int maxfd;
     28     fd_set allset;
     29 
     30     int client[FD_SETSIZE];
     31     int nready;
     32 
     33     char buf[MAXLINE];
     34 
     35     int reuse = 1;
     36     
     37     listenfd = socket(AF_INET,SOCK_STREAM,0);
     38 
     39     if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1) {
     40         return -1;
     41     }
     42 
     43     bzero(&servaddr,sizeof(servaddr));
     44     servaddr.sin_family = AF_INET;
     45     servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
     46     servaddr.sin_port = htons(IP_PORT);
     47 
     48     ret = bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr));
     49     if(ret == -1) {
     50         printf("bind socket error
    ");
     51         exit(0);
     52     }
     53 
     54     ret = listen(listenfd,LISTEN_NUM);
     55     if(ret == -1) {
     56         printf("listen socket error
    ");
     57         exit(0);
     58     }
     59 
     60     maxfd = listenfd;
     61     maxindex = -1;
     62     for(i = 0; i < FD_SETSIZE; i++) {
     63         client[i] = -1;
     64     }
     65     //FD_ZERO(&allset);
     66     //FD_SET(listenfd, &allset);
     67 
     68     for( ; ; ) {
     69         //reset = allset;
     70         FD_ZERO(&allset);
     71         FD_SET(listenfd, &allset);
     72         for (i = 0; i <= maxindex; i++) {
     73             FD_SET(client[i], &allset);
     74         }
     75 
     76         nready = select(maxfd + 1, &allset, NULL, NULL, NULL);
     77 
     78         if(FD_ISSET(listenfd, &allset)) {
     79             clilen = sizeof(clitaddr);
     80             connfd = accept(listenfd, (struct sockaddr*)&clitaddr, &clilen);
     81             //connfd = accept(listenfd, (struct sockaddr*)&clitaddr, sizeof(clitaddr));
     82 
     83             //printf("clitaddr ip : %d,port : %d
    ", inet_ntoa(clitaddr.sin_addr), clitaddr.sin_port);
     84             fprintf(stdout, "accept clitaddr %s:%d
    ", inet_ntoa(clitaddr.sin_addr), clitaddr.sin_port);
     85             for(i = 0; i < FD_SETSIZE; i++) {
     86                 if(client[i] < 0){
     87                     client[i] = connfd;
     88                     break;
     89                 }
     90             }
     91 
     92             if(i == FD_SETSIZE) {
     93                 printf("too many clients
    ");
     94                 exit(0);
     95             }
     96 
     97             FD_SET(connfd, &allset);
     98             if(connfd > maxfd) {
     99                 maxfd = connfd;
    100             }
    101             if(i > maxindex) {
    102                 maxindex = i;
    103             }
    104             if(--nready <= 0)
    105                 continue;
    106         }
    107 
    108         for(i = 0; i <= maxindex; i++) {
    109             if((sockfd = client[i]) < 0)
    110                 continue;
    111             if(FD_ISSET(sockfd, &allset)) {
    112                 if((n = recv(sockfd, buf, MAXLINE, 0)) == 0) {
    113                     close(sockfd);
    114                     FD_CLR(sockfd, &allset);
    115                     client[i] = -1;
    116                 }
    117                 else {
    118                     printf("server recv buf is %s
    ", buf);
    119                     send(sockfd, buf, strlen(buf), 0);
    120                 }
    121                 if(--nready <= 0)
    122                     break;
    123             }
    124         }
    125     }
    126 
    127     return 0;
    128 }

    客户端:

     1 #include <netinet/in.h>
     2 #include <sys/socket.h>
     3 #include <stdio.h>
     4 #include <string.h>
     5 #include <stdlib.h>
     6 #include <sys/select.h>
     7 #include <time.h>
     8 #include <unistd.h>
     9 #include <sys/types.h>
    10 #include <errno.h>
    11 #include <math.h>
    12 
    13 #define SERVER_ADDR "127.0.0.1"
    14 #define SERVER_PORT 8081
    15 
    16 #define MAXLINE 1024
    17 
    18 void str_cli(FILE *fp, int sockfd)
    19 {
    20     int maxfdp, stdineof;
    21     fd_set rset;
    22     char buf[MAXLINE];
    23     int n;
    24 
    25     stdineof = 0;
    26     FD_ZERO(&rset);
    27 
    28     for( ; ; ) {
    29         if(stdineof == 0)
    30             FD_SET(fileno(fp), &rset);
    31         FD_SET(sockfd, &rset);
    32         maxfdp = (fileno(fp) > sockfd) ? (fileno(fp) + 1) : (sockfd + 1);
    33         //maxfdp = max(fileno(fp), sockfd) + 1;
    34         select(maxfdp, &rset, NULL, NULL, NULL);
    35 
    36         if(FD_ISSET(sockfd, &rset)) {
    38             if ((n = recv(sockfd, buf, MAXLINE, 0)) == 0) {
    39                 if (stdineof = 1) {
    40                     return ;
    41                 }
    42                 else {
    43                     printf("server prematurely
    ");
    44                     exit(0);
    45                 }
    46             }
    47             printf("client:sockfd ready
    ");
    48             //write(fileno(stdout), buf, n);
    49             write(fileno(stdout), buf, strlen(buf) + 1);
    50         }
    51 
    52         if (FD_ISSET(fileno(fp), &rset)) {
    53             if ((n = read(fileno(fp), buf, MAXLINE)) == 0) {
    54                 stdineof = 1;
    55                 shutdown(sockfd, SHUT_WR);
    56                 FD_CLR(fileno(fp), &rset);
    57                 continue;
    58             }
    59             //buf[n] = '';
    60             printf("client buf is %s",buf);
    61             send(sockfd, buf, strlen(buf), 0);
    62         }
    63     }
    64     return ;
    65 }
    66 
    67 int main(void)
    68 {
    69     int sockfd, ret;
    70     struct sockaddr_in servaddr;
    71 
    72     sockfd = socket(AF_INET, SOCK_STREAM, 0);
    73     if(sockfd < 0) {
    74         printf("create socket error
    ");
    75         exit(0);
    76     }
    77 
    78     bzero(&servaddr, sizeof(servaddr));
    79     servaddr.sin_family = AF_INET;
    80     servaddr.sin_port = htons(SERVER_PORT);
    81     servaddr.sin_addr.s_addr = inet_addr(SERVER_ADDR);
    82 
    83     ret = connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr));
    84     if(ret < 0) {
    85         printf("connect error
    ");
    86         exit(0);
    87     }
    88 
    89     str_cli(stdin, sockfd);
    90     exit(0);
    91 }

    执行结果:

    服务器启动后处于监听状态,可以启动多个客户端连入服务器请求并响应。实验结果略。

  • 相关阅读:
    XML 加密、解密
    word 转 pdf
    PowerDesigner(PowerDesigner15.1.0.2850)下载、安装以及破解
    SQL 2005 18456
    SQL 2005端口的修改
    SQL——统计查询
    由于启动用户实例的进程时出错,导致无法生成 SQL Server 的用户实例 解决办法
    ASP.NET MVC中Model View Controller的开发顺序
    使用Windows身份验证的Intranet网站安全管理 Windows Authentication
    ASP.NET web.config中数据库连接字符串connectionStrings节的配置
  • 原文地址:https://www.cnblogs.com/scu-cjx/p/7723992.html
Copyright © 2011-2022 走看看