日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

C++笔记:select多路复用机制

發布時間:2023/11/30 c/c++ 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C++笔记:select多路复用机制 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

轉載:http://blog.csdn.net/qdx411324962/article/details/42499535

函數作用:

系統提供select函數來實現多路復用輸入/輸出模型。select系統調用是用來讓我們的程序監視多個文件句柄的狀態變化的。程序會停在select這里等待,直到被監視的文件句柄有一個或多個發生了狀態改變。關于文件句柄,其實就是一個整數,我們最熟悉的句柄是0、1、2三個,0是標準輸入,1是標準輸出,2是標準錯誤輸出。0、1、2是整數表示的,對應的FILE *結構的表示就是stdin、stdout、stderr。

函數原型:

  • int?select(int?maxfd,fd_set?*rdset,fd_set?*wrset,?\??
  • ???????????fd_set?*exset,struct?timeval?*timeout);??
  • 參數說明:

    參數maxfd是需要監視的最大的文件描述符值+1;rdset,wrset,exset分別對應于需要檢測的可讀文件描述符的集合,可寫文件描述符的集 合及異常文件描述符的集合。struct timeval結構用于描述一段時間長度,如果在這個時間內,需要監視的描述符沒有事件發生則函數返回,返回值為0。

    下面的宏提供了處理這三種描述詞組的方式:
    FD_CLR(inr fd,fd_set* set);用來清除描述詞組set中相關fd 的位
    FD_ISSET(int fd,fd_set *set);用來測試描述詞組set中相關fd 的位是否為真
    FD_SET(int fd,fd_set*set);用來設置描述詞組set中相關fd的位
    FD_ZERO(fd_set *set);用來清除描述詞組set的全部位

    參數timeout為結構timeval,用來設置select()的等待時間,其結構定義如下:

  • struct?timeval??
  • {??
  • ????time_t?tv_sec;//second??
  • ????time_t?tv_usec;//minisecond??
  • };??
  • 如果參數timeout設為:

    NULL,則表示select()沒有timeout,select將一直被阻塞,直到某個文件描述符上發生了事件。

    0:僅檢測描述符集合的狀態,然后立即返回,并不等待外部事件的發生。

    特定的時間值:如果在指定的時間段里沒有事件發生,select將超時返回。

    函數返回值:

    執行成功則返回文件描述詞狀態已改變的個數,如果返回0代表在描述詞狀態改變前已超過timeout時間,沒有返回;當有錯誤發生時則返回-1,錯誤原因存于errno,此時參數readfds,writefds,exceptfds和timeout的值變成不可預測。錯誤值可能為:
    EBADF 文件描述詞為無效的或該文件已關閉
    EINTR 此調用被信號所中斷
    EINVAL 參數n 為負值。
    ENOMEM 核心內存不足

    常見的程序片段如下:

    fs_set readset;
    FD_ZERO(&readset);
    FD_SET(fd,&readset);
    select(fd+1,&readset,NULL,NULL,NULL);
    if(FD_ISSET(fd,readset){……}

    理解select模型:

    理解select模型的關鍵在于理解fd_set,為說明方便,取fd_set長度為1字節,fd_set中的每一bit可以對應一個文件描述符fd。則1字節長的fd_set最大可以對應8個fd。

    (1)執行fd_set set; FD_ZERO(&set);則set用位表示是0000,0000。

    (2)若fd=5,執行FD_SET(fd,&set);后set變為0001,0000(第5位置為1)

    (3)若再加入fd=2,fd=1,則set變為0001,0011

    (4)執行select(6,&set,0,0,0)阻塞等待

    (5)若fd=1,fd=2上都發生可讀事件,則select返回,此時set變為0000,0011。注意:沒有事件發生的fd=5被清空。

     基于上面的討論,可以輕松得出select模型的特點:

      (1)可監控的文件描述符個數取決與sizeof(fd_set)的值。我這邊服務 器上sizeof(fd_set)=512,每bit表示一個文件描述符,則我服務器上支持的最大文件描述符是512*8=4096。據說可調,另有說雖 然可調,但調整上限受于編譯內核時的變量值。本人對調整fd_set的大小不太感興趣,參考http://www.cppblog.com?/CppExplore/archive/2008/03/21/45061.html中的模型2(1)可以有效突破select可監控的文件描述符上 限。

      (2)將fd加入select監控集的同時,還要再使用一個數據結構array保存放到select監控集中的fd,一是用于再select 返回后,array作為源數據和fd_set進行FD_ISSET判斷。二是select返回后會把以前加入的但并無事件發生的fd清空,則每次開始 select前都要重新從array取得fd逐一加入(FD_ZERO最先),掃描array的同時取得fd最大值maxfd,用于select的第一個 參數。

      (3)可見select模型必須在select前循環array(加fd,取maxfd),select返回后循環array(FD_ISSET判斷是否有時間發生)。

    下面給一個偽碼說明基本select模型的服務器模型:

  • array[slect_len];??
  •  nSock=0;??
  •  array[nSock++]=listen_fd;(之前listen?port已綁定并listen)??
  •  maxfd=listen_fd;??
  • ??
  •  while(1){??
  • ??
  •   FD_ZERO(&set);??
  • ??
  •   foreach?(fd?in?array)??
  •   {??
  • ????  fd大于maxfd,則maxfd=fd??
  • ????  FD_SET(fd,&set)??
  •   }??
  • ??
  •   res=select(maxfd+1,&set,0,0,0);??
  • ??
  •   if(FD_ISSET(listen_fd,&set))??
  •   {??
  • ????  newfd=accept(listen_fd);??
  • ????  array[nsock++]=newfd;??
  • ????  if(--res<=0)?continue;??
  •   }??
  • ??
  •   foreach?下標1開始?(fd?in?array)??
  •   {??
  • ????  if(FD_ISSET(fd,&tyle="COLOR:?#ff0000">set))??
  • ????  執行讀等相關操作??
  • ????  如果錯誤或者關閉,則要刪除該fd,將array中相應位置和最后一個元素互換就好,nsock減一??
  • ????  if(--res<=0)?continue;??
  •   }??
  • ??
  •  }??
  • 檢測鍵盤有無輸入,完整的程序如下:

  • #include<sys/time.h>??
  • #include<sys/types.h>??
  • #include<unistd.h>??
  • #include<string.h>??
  • #include<stdlib.h>??
  • #include<stdio.h>??
  • int?main()??
  • {??
  • ????????char?buf[10]="";??
  • ????????fd_set?rdfds;??
  • ????????struct?timeval?tv;??
  • ????????int?ret;??
  • ????????FD_ZERO(&rdfds);??
  • ????????FD_SET(0,&rdfds);???//文件描述符0表示stdin鍵盤輸入??
  • ????????tv.tv_sec?=?3;??
  • ????????tv.tv_usec?=?500;??
  • ????????ret?=?select(1,&rdfds,NULL,NULL,&tv);??
  • ????????if(ret<0)??
  • ??????????????printf("\n?selcet");??
  • ????????else?if(ret?==?0)??
  • ??????????????printf("\n?timeout");??
  • ????????else??
  • ??????????????printf("\n?ret?=?%d",ret);??
  • ??
  • ????????if(FD_ISSET(1,&rdfds))??//如果有輸入,從stdin中獲取輸入字符??
  • ????????{??
  • ??????????????printf("\n?reading");??
  • ??????????????fread(buf,9,1,stdin);??
  • ?????????}??
  • ?????????write(1,buf,strlen(buf));??
  • ?????????printf("\n?%d?\n",strlen(buf));??
  • ?????????return?0;??
  • }??
  • //執行結果ret?=?1.??
  • 利用Select模型,設計的web服務器:

  • #include?<stdio.h>??
  • #include?<stdlib.h>??
  • #include?<unistd.h>??
  • #include?<errno.h>??
  • #include?<string.h>??
  • #include?<sys/types.h>??
  • #include?<sys/socket.h>??
  • #include?<netinet/in.h>??
  • #include?<arpa/inet.h>??
  • ??
  • #define?MYPORT?88960????//?the?port?users?will?be?connecting?to??
  • ??
  • #define?BACKLOG?10?????//?how?many?pending?connections?queue?will?hold??
  • ??
  • #define?BUF_SIZE?200??
  • ??
  • int?fd_A[BACKLOG];????//?accepted?connection?fd??
  • int?conn_amount;????//?current?connection?amount??
  • ??
  • void?showclient()??
  • {??
  • ????int?i;??
  • ????printf("client?amount:?%d\n",?conn_amount);??
  • ????for?(i?=?0;?i?<?BACKLOG;?i++)?{??
  • ????????printf("[%d]:%d??",?i,?fd_A[i]);??
  • ????}??
  • ????printf("\n\n");??
  • }??
  • ??
  • int?main(void)??
  • {??
  • ????int?sock_fd,?new_fd;??//?listen?on?sock_fd,?new?connection?on?new_fd??
  • ????struct?sockaddr_in?server_addr;????//?server?address?information??
  • ????struct?sockaddr_in?client_addr;?//?connector's?address?information??
  • ????socklen_t?sin_size;??
  • ????int?yes?=?1;??
  • ????char?buf[BUF_SIZE];??
  • ????int?ret;??
  • ????int?i;??
  • ??
  • ????if?((sock_fd?=?socket(AF_INET,?SOCK_STREAM,?0))?==?-1)?{??
  • ????????perror("socket");??
  • ????????exit(1);??
  • ????}??
  • ??
  • ????if?(setsockopt(sock_fd,?SOL_SOCKET,?SO_REUSEADDR,?&yes,?sizeof(int))?==?-1)?{??
  • ????????perror("setsockopt");??
  • ????????exit(1);??
  • ????}??
  • ??????
  • ????server_addr.sin_family?=?AF_INET;?????????//?host?byte?order??
  • ????server_addr.sin_port?=?htons(MYPORT);?????//?short,?network?byte?order??
  • ????server_addr.sin_addr.s_addr?=?INADDR_ANY;?//?automatically?fill?with?my?IP??
  • ????memset(server_addr.sin_zero,?'\0',?sizeof(server_addr.sin_zero));??
  • ??
  • ????if?(bind(sock_fd,?(struct?sockaddr?*)&server_addr,?sizeof(server_addr))?==?-1)?{??
  • ????????perror("bind");??
  • ????????exit(1);??
  • ????}??
  • ??
  • ????if?(listen(sock_fd,?BACKLOG)?==?-1)?{??
  • ????????perror("listen");??
  • ????????exit(1);??
  • ????}??
  • ??
  • ????printf("listen?port?%d\n",?MYPORT);??
  • ??
  • ????fd_set?fdsr;??
  • ????int?maxsock;??
  • ????struct?timeval?tv;??
  • ??
  • ????conn_amount?=?0;??
  • ????sin_size?=?sizeof(client_addr);??
  • ????maxsock?=?sock_fd;??
  • ????while?(1)?{??
  • ????????//?initialize?file?descriptor?set??
  • ????????FD_ZERO(&fdsr);??
  • ????????FD_SET(sock_fd,?&fdsr);??
  • ??
  • ????????//?timeout?setting??
  • ????????tv.tv_sec?=?30;??
  • ????????tv.tv_usec?=?0;??
  • ??
  • ????????//?add?active?connection?to?fd?set??
  • ????????for?(i?=?0;?i?<?BACKLOG;?i++)?{??
  • ????????????if?(fd_A[i]?!=?0)?{??
  • ????????????????FD_SET(fd_A[i],?&fdsr);??
  • ????????????}??
  • ????????}??
  • ??
  • ????????ret?=?select(maxsock?+?1,?&fdsr,?NULL,?NULL,?&tv);??
  • ????????if?(ret?<?0)?{??
  • ????????????perror("select");??
  • ????????????break;??
  • ????????}?else?if?(ret?==?0)?{??
  • ????????????printf("timeout\n");??
  • ????????????continue;??
  • ????????}??
  • ??
  • ????????//?check?every?fd?in?the?set??
  • ????????for?(i?=?0;?i?<?conn_amount;?i++)?{??
  • ????????????if?(FD_ISSET(fd_A[i],?&fdsr))?{??
  • ????????????????ret?=?recv(fd_A[i],?buf,?sizeof(buf),?0);??
  • ??????????????????
  • ????????????????char?str[]?=?"Good,very?nice!\n";??
  • ??????????????????
  • ????????????????send(fd_A[i],str,sizeof(str)?+?1,?0);??
  • ??????????????????
  • ??????????????????
  • ????????????????if?(ret?<=?0)?{????????//?client?close??
  • ????????????????????printf("client[%d]?close\n",?i);??
  • ????????????????????close(fd_A[i]);??
  • ????????????????????FD_CLR(fd_A[i],?&fdsr);??
  • ????????????????????fd_A[i]?=?0;??
  • ????????????????}?else?{????????//?receive?data??
  • ????????????????????if?(ret?<?BUF_SIZE)??
  • ????????????????????????memset(&buf[ret],?'\0',?1);??
  • ????????????????????printf("client[%d]?send:%s\n",?i,?buf);??
  • ????????????????}??
  • ????????????}??
  • ????????}??
  • ??
  • ????????//?check?whether?a?new?connection?comes??
  • ????????if?(FD_ISSET(sock_fd,?&fdsr))?{??
  • ????????????new_fd?=?accept(sock_fd,?(struct?sockaddr?*)&client_addr,?&sin_size);??
  • ????????????if?(new_fd?<=?0)?{??
  • ????????????????perror("accept");??
  • ????????????????continue;??
  • ????????????}??
  • ??
  • ????????????//?add?to?fd?queue??
  • ????????????if?(conn_amount?<?BACKLOG)?{??
  • ????????????????fd_A[conn_amount++]?=?new_fd;??
  • ????????????????printf("new?connection?client[%d]?%s:%d\n",?conn_amount,??
  • ????????????????????????inet_ntoa(client_addr.sin_addr),?ntohs(client_addr.sin_port));??
  • ????????????????if?(new_fd?>?maxsock)??
  • ????????????????????maxsock?=?new_fd;??
  • ????????????}??
  • ????????????else?{??
  • ????????????????printf("max?connections?arrive,?exit\n");??
  • ????????????????send(new_fd,?"bye",?4,?0);??
  • ????????????????close(new_fd);??
  • ????????????????break;??
  • ????????????}??
  • ????????}??
  • ????????showclient();??
  • ????}??
  • ??
  • ????//?close?other?connections??
  • ????for?(i?=?0;?i?<?BACKLOG;?i++)?{??
  • ????????if?(fd_A[i]?!=?0)?{??
  • ????????????close(fd_A[i]);??
  • ????????}??
  • ????}??
  • ??
  • ????exit(0);??
  • }??
  • 補充部分:

    1 基本原理

    注:select 原理圖,摘自?IBM iSeries 信息中心。

    1 數據結構與函數原型

    1.1 select

    • 函數原型 int select(int nfds,fd_set *readset,fd_set *writeset,fd_set* exceptset,struct timeval *timeout);
    • 頭文件
      • select位于: #include <sys/select.h>
      • struct timeval位于: #include <sys/time.h>
    • 返回值

      返回對應位仍然為1的fd的總數。

    • 參數
      • nfds:第一個參數是:最大的文件描述符值+1;
      • readset:可讀描述符集合;
      • writeset:可寫描述符集合;
      • exceptset:異常描述符;
      • timeout:select 的監聽時長,如果這短時間內所監聽的 socket 沒有事件發生。

    1.2 fd_set

    1.2.1 清空描述符集合

    FD_ZERO(fd_set *)

    1.2.2 向描述符集合添加指定描述符

    FD_SET(int, fd_set *)

    1.2.3 從描述符集合刪除指定描述符

    FD_CLR(int, fd_set *)

    1.2.4 檢測指定描述符是否在描述符集合中

    FD_ISSET(int, fd_set *)

    1.2.5 描述符最大數量

    #define FD_SETSIZE 1024

    1.3 描述符集合

    可讀描述符集合中可讀的描述符,為1,其他為0;可寫也類似。異常描述符集合中有異常等待處理的描述符的值為1,其他為0。

    1.4 ioctl

    • 函數原型:

      int ioctl(int handle, int cmd,[int *argdx, int argcx]);
    • 頭文件:

      #include <sys/ioctl.h>
    • 返回值:

      • 0 - 成功
      • 1 - 失敗

    2 示例

    程序各部分的解釋在注釋中。

    #include <sys/socket.h> #include <string.h> #include <sys/time.h> #include <netinet/in.h> #include <sys/ioctl.h> #include <stdlib.h> #include <errno.h> #include <stdio.h> #include <unistd.h>#define TRUE 1 #define FALSE 0int main(int argc, char *argv[]) {int i, len, rc, on = TRUE;int listen_sd, new_sd = 0, max_sd;int desc_ready;char buffer[80];int close_conn, end_server = FALSE;struct sockaddr_in server_addr;struct timeval timeout;struct fd_set master_set, working_set;// Listenlisten_sd = socket(AF_INET, SOCK_STREAM, 0);if (listen_sd < 0){perror("socket() failed");exit(-1);}// Set socket optionsrc = setsockopt(listen_sd, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on));if (rc < 0){perror("setsockopt() failed");close(listen_sd);exit(-1);}// Set IO controlrc = ioctl(listen_sd, FIONBIO, (char *) &on);if (rc < 0){perror("ioctl() failed");close(listen_sd);exit(-1);}// Bindmemset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_addr.s_addr = htonl(INADDR_ANY);server_addr.sin_port = htons(atoi(argv[1]));rc = bind(listen_sd, (struct sockaddr *) &server_addr, sizeof(server_addr));if (rc < 0){perror("bind() failed\n");close(listen_sd);exit(-1);}// Listenrc = listen(listen_sd, 32);if (rc < 0){perror("listen() failed\n");close(listen_sd);exit(-1);}// Intialize sd setFD_ZERO(&master_set);max_sd = listen_sd;FD_SET(listen_sd, &master_set);timeout.tv_sec = 3 * 60;timeout.tv_usec = 0;// Startdo{// Copy master_set into working_setmemcpy(&working_set, &master_set, sizeof(master_set));printf("Waiting on select()...\n");rc = select(max_sd + 1, &working_set, NULL, NULL, &timeout);if (rc < 0){perror(" select() failed\n");break;}if (rc == 0){printf(" select() timed out. End program.\n");break;}desc_ready = rc; // number of sds ready in working_set// Check each sd in working_setfor (i = 0; i <= max_sd && desc_ready > 0; ++i){// Check to see if this sd is readyif (FD_ISSET(i, &working_set)){--desc_ready;// Check to see if this is the listening sdif (i == listen_sd){printf(" Listeing socket is readable\n");do{// Acceptnew_sd = accept(listen_sd, NULL, NULL);// Nothing to be acceptedif (new_sd < 0){// All have been acceptedif (errno != EWOULDBLOCK){perror(" accept() failed\n");end_server = TRUE;}break;}// Insert new_sd into master_setprintf(" New incoming connection - %d\n", new_sd);FD_SET(new_sd, &master_set);if (new_sd > max_sd){max_sd = new_sd;}}while (new_sd != -1);}// This is not the listening sdelse{close_conn = FALSE;printf(" Descriptor %d is avaliable\n", i);do{rc = recv(i, buffer, sizeof(buffer), 0);// Receive data on sd "i", until failure occursif (rc < 0){// Normal failureif (errno != EWOULDBLOCK){perror(" recv() failed\n");close_conn = TRUE;}break;}// The connection has been closed by the clientif (rc == 0){printf(" Connection closed\n");close_conn = TRUE;break;}/* Receiving data succeeded and echo it backthe to client */len = rc;printf(" %d bytes received\n", len);rc = send(i, buffer, len, 0);if (rc < 0){perror(" send() failed");close_conn = TRUE;break;}}while (TRUE);// If unknown failure occuredif (close_conn){// Close the sd and remove it from master_setclose(i);FD_CLR(i, &master_set);// If this is the max sdif (i == max_sd){// Find the max sd in master_set nowwhile (FD_ISSET(max_sd, &master_set) == FALSE){--max_sd;}} // End of if (i == max_sd)} // End of if (close_conn)}}}}while (end_server == FALSE);/* Close each sd in master_set */for (i = 0; i < max_sd; ++i){if (FD_ISSET(i, &master_set)){close(i);}}return 0; }

    總結

    以上是生活随笔為你收集整理的C++笔记:select多路复用机制的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。