【ネットワーク】(八)I/O多重化--Select(二)


第7篇の文章の中でいくつかのIO模型を紹介して、select関数を使ってクライアントのコードを改善して、この文章はそれを使ってサービス側のコードを改善します!
select関数で実装されたサーバプログラムは、同時サーバと呼ばれ、タスクをキューに並べて処理しているため、これらのイベントを並列に処理できません.
1、selectイベント発生条件
0.1読み取り可能なイベント
  • セットのインタフェースバッファにはデータが読み取り可能である.
  • 接続のリード半分が閉じる、すなわち、FINセグメントが受信されると、読み取り可能であり、読み取り動作は0
  • に戻る.
  • リスニングセットインタフェースであれば、完了したキューが空でない場合、読み取り可能である.新しい接続要求
  • がある.
  • インタフェースでgetsockopt関数でSO_を指定できるエラーが発生しました.ERRORオプションは、
  • を取得するために使用される
    0.2書き込み可能なイベント
  • セットインタフェース送信バッファは、データ
  • を収容する空間を有する.
  • 接続の書き込みの半分が閉じる、すなわちRSTセグメントが受信された後に書き込み可能であり、write動作を再び呼び出すことでSIGPIPE信号の生成
  • をもたらす.
  • インタフェースでgetsockopt関数でSO_を指定できるエラーが発生しました.ERRORオプションは、
  • を取得するために使用される
    0.3例外イベント
  • ソケットインタフェースは帯域外データ
  • が存在する.
    2、select同時サーバー実現
    select関数を使用して、すべてのイベントを単一プロセスで処理し、同時サーバを実現します.
    クライアント・プログラムは「第7編」と同じで、コンパイル・コマンドは次のとおりです.
    gcc -Wall -g -std=gnu99 client.c -o client

    サービス・エンド・フル・プログラム
    コンパイルコマンド:gcc-Wall-g-std=c 99 server.c -o server
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    
    #define handle_error(msg)   \
                        do{perror(msg);exit(EXIT_FAILURE);}while(0)
    
    
    ssize_t readn(int fd, void *buf, size_t count)
    {
      if((fd < 0) || (buf == NULL) || (count < 0))
            return -1;
      size_t nleft = count;   //     
      ssize_t nread = 0;      //     
      char *pbuf = (char*)buf;
      while(nleft > 0)
      {
        if((nread = read(fd, pbuf, nleft)) < 0)
        {
            if(errno == EINTR)
                continue;
            return -1;
        }
        else if (nread == 0)
            return count - nleft;
        pbuf += nread;
        nleft -= nread;
      }
      return count;
    }
    
    ssize_t writen(int fd, const void *buf, size_t count)
    {
        if((fd < 0) || (buf == NULL) || (count < 0))
              return -1;
        size_t nleft = count;   //     
        ssize_t nwritten = 0;      //      
        char *pbuf = (char*)buf;
        while (nleft > 0)
        {
            if((nwritten = write(fd, pbuf, nleft)) < 0)
            {
                if(errno == EINTR)
                    continue;
                return -1;
            }
            else if(nwritten == 0)
                continue;
            pbuf += nwritten;
            nleft -= nwritten;
        }
        return count;
    }
    
    //  recv                ,            
    ssize_t recv_peek(int sockfd, void *buf, size_t len)
    {
        while(true)
        {
            int iret = recv(sockfd, buf, len, MSG_PEEK);
            if(iret == -1 && errno == EINTR)    //           ,       
                continue;
            return iret;
        }
    }
    
    //         
    , ,
    ssize_t recvline(int sockfd, void *buf, size_t maxlen) { int iret = 0; int nread = 0; // char *pbuf = (char*)buf; int nleft = maxlen; // while(true) { iret = recv_peek(sockfd, pbuf, nleft); if(iret < 0) return iret; // else if(iret == 0) return iret; // nread = iret; if(nread > nleft) // exit(EXIT_FAILURE); for(int i = 0; i < nread; i++) { if(pbuf[i] == '
    '
    ) { iret = readn(sockfd, pbuf, i+1); //
    if(iret != i+1) exit(EXIT_FAILURE); // i+1 , return iret; //
    } } //
    , ,
    nleft -= nread; iret = readn(sockfd, pbuf, nread); if(iret != nread) exit(EXIT_FAILURE); pbuf += nread; } return -1; } int main(void) { int sk_fd = socket(AF_INET, SOCK_STREAM , IPPROTO_TCP); if(sk_fd < 0) handle_error("socket"); // REUSEADDR, TIME_WAIT , int on = 1; if(setsockopt(sk_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) { close(sk_fd); handle_error("setsockopt"); } struct sockaddr_in sr_addr; memset(&sr_addr,0,sizeof(sr_addr)); sr_addr.sin_family = AF_INET; sr_addr.sin_port = htons(5188); sr_addr.sin_addr.s_addr = htonl(INADDR_ANY); //sr_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); //inet_aton("127.0.0.1",&sr_addr.sin_addr); if(bind(sk_fd, (struct sockaddr*)&sr_addr, sizeof(sr_addr)) < 0) { close(sk_fd); handle_error("bind"); } // if(listen(sk_fd, SOMAXCONN) < 0) // SOMAXCONN { close(sk_fd); handle_error("listen"); } // select fd_set readset; fd_set allset; FD_ZERO(&readset); FD_ZERO(&allset); FD_SET(sk_fd, &allset); // , int maxfd = sk_fd; // int maxfd_last = maxfd; // maxfd , int nready = 0; int maxindex = 0; //client_sk int maxindex_last = 0; //maxindex int client_sk[FD_SETSIZE]; // for(int i = 0; i < FD_SETSIZE; i++) { client_sk[i] = -1; } while (true) { readset = allset; nready = select(maxfd + 1, &readset, NULL, NULL, NULL); if(nready == -1) { if(errno == EINTR) // continue; handle_error("select"); } if(nready == 0) //time out continue; if(FD_ISSET(sk_fd, &readset)) // { // accept struct sockaddr_in cl_addr; socklen_t cl_length = sizeof(cl_addr); memset(&cl_addr,0,sizeof(cl_addr)); int ac_sk = accept(sk_fd, (struct sockaddr *)&cl_addr, &cl_length); if(ac_sk < 0) { if(errno == EINTR) continue; handle_error("accept"); } // FD_SET(ac_sk, &allset); if(ac_sk > maxfd) { maxfd_last = maxfd; // maxfd = ac_sk; // } int i; for(i = 0; i < FD_SETSIZE; i++) { if(client_sk[i] < 0) { client_sk[i] = ac_sk; if(maxindex < i) { maxindex_last = maxindex; // maxindex = i; // } break; } } if(i == FD_SETSIZE) //client_sk , { fprintf(stderr, "Client too many!"); exit(EXIT_FAILURE); } printf("Connect ip = %s\tport = %d
    "
    ,inet_ntoa(cl_addr.sin_addr),ntohs(cl_addr.sin_port)); if(--nready <= 0) // continue; } // for(int i = 0; i <= maxindex; i++) { int conn_sk = client_sk[i]; if(FD_ISSET(conn_sk, &readset)) { // char recvbuf[1024]; memset(recvbuf,0,sizeof(recvbuf)); int iret = recvline(conn_sk,recvbuf,sizeof(recvbuf)); // if(iret == -1) handle_error("read"); else if(iret == 0) { // , if(conn_sk == maxfd) { maxfd = maxfd_last; // } if(i == maxindex) { maxindex = maxindex_last; // } client_sk[i] = -1; FD_CLR(conn_sk, &allset); printf("Client was closed!
    "
    ); close(conn_sk); } fputs(recvbuf,stdout); writen(conn_sk, recvbuf, strlen(recvbuf)); // if(--nready <= 0) // break; } } } for(int i = 0; i <= maxindex; i++) { close(client_sk[i]); } close(sk_fd); return 0; }