C++ Select()

C++ Select()是一種系統調用函數,能在一組文件描述符中強制時間限制內等待一個或多個條件成立,可用於I/O多路復用。下面從不同方面對C++ Select()做詳細的闡述。

一、C Select函數

C Select函數的原型為:int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout),其中:

• nfds為文件描述符集合中數值最大的描述符加1

• readfds,writefds,exceptfds為文件描述符集合

• timeout表示阻塞時間

C Select函數用於檢查文件描述符集合中是否有可讀、可寫、異常等事件,可用於單進程內多個文件描述符的I/O事件監聽。

以下為C++ Select函數使用示例:


#include<stdio.h>
#include<sys/time.h>
#include<sys/types.h>
#include<unistd.h>

int main() {  
    fd_set rfds;  
    struct timeval tv;  
    int retval;  
    char buf[256];  

/* 等待stdin(標準輸入)上的輸入 */  
    FD_ZERO(&rfds);  
    FD_SET(STDIN_FILENO, &rfds);  

/* 等待5秒鐘 */  
    tv.tv_sec = 5;  
    tv.tv_usec = 0;  

    retval = select(1, &rfds, NULL, NULL, &tv);  

/* 由於我們只等待stdin上的輸入,所以不需要傳遞參數除STDIN_FILENO外 */
    if (retval == -1)  
        perror("select()");  
    else if (retval) {  
        fgets(buf, sizeof buf, stdin);  
        printf("輸入: %s\n", buf);  
    }  
    else  
        printf("沒有輸入數據在五秒內.\n");  
    return 0;  
}

二、C Selection和S Selection

C Selection和S Selection都是Select函數的改進版,其擴展了參數類型,可以通過hash表實現高效的查找,提升了系統利用率。其中,C Selection適用於用戶空間,S Selection適用於內核空間,可以用於高速網路和操作系統的性能優化。

C++ Select函數的相關參數和C Selection、S Selection相比,基本一致。在使用S Selection時,需要在建立socket時進行一個參數的設置,同時需要用到poll函數。

下面是借用《UNIX環境高級編程》一書中關於S Selection的代碼示例:


#include<sys/time.h>
#include<sys/types.h>

/* Selserv.cpp */
int
main(int argc, char *argv[])
{
    int    i, maxi, maxfd, listenfd, connfd, sockfd;
    int    nready, client[FD_SETSIZE];
    ssize_t    n;
    fd_set rset, allset;
    char   buf[MAXLINE];
    char   str[INET_ADDRSTRLEN];
    socklen_t clilen;
    struct sockaddr_in cliaddr, servaddr;

    if ( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        err_sys("socket error");

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family      = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port        = htons(SERV_PORT);

    if (bind(listenfd, (SA *) &servaddr, sizeof(servaddr)) < 0)
        err_sys("bind error");

    if (listen(listenfd, LISTENQ) < 0)
        err_sys("listen error");
    else
        printf("<時間%ld>  監聽埠 %d on %s\n",
               time(NULL), SERV_PORT, inet_ntop(AF_INET, &servaddr.sin_addr, str, sizeof(str)));

    maxfd = listenfd;        /* 定義初始最大描述符值 */
    maxi = -1;                /* 有效客戶連接下標 */
    for (i = 0; i < FD_SETSIZE; i++)
        client[i] = -1;        /* 初始化 */

    FD_ZERO(&allset);
    FD_SET(listenfd, &allset); /* 添加監聽描述符 */

    for ( ; ; ) {
        rset = allset;        /* 每次循環時都重新設置select的可讀文件描述符集 */
        if ( (nready = select(maxfd+1, &rset, NULL, NULL, NULL)) < 0)
            err_sys("select error");

        if (FD_ISSET(listenfd, &rset)) { /* 新的客戶連接請求 */
            clilen = sizeof(cliaddr);
            if ( (connfd = accept(listenfd, (SA *) &cliaddr, &clilen)) < 0)
                err_sys("accept error");

            printf("<時間%ld>:新的客戶連接來自%s: %d\n",
                   time(NULL), inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)), ntohs(cliaddr.sin_port));

            for (i = 0; i < FD_SETSIZE; i++)
                if (client[i] < 0) {
                    client[i] = connfd;    /* 保存新的描述符 */
                    break;
                }
            if (i == FD_SETSIZE)
                err_quit("too many clients");

            FD_SET(connfd, &allset);    /* 添加新的文件描述符到讀集合中 */
            if (connfd > maxfd)
                maxfd = connfd;            /* for select */

            if (i > maxi)
                maxi = i;                /* 數組client中最大元素的下標 */
            if (--nready <= 0)
                continue;            /* 沒有更多就緒事件時,繼續回到select()調用*/

        }

        for (i = 0; i <= maxi; i++) {    /* 檢測客戶端socket */
            if ( (sockfd = client[i]) < 0)
                continue;
            if (FD_ISSET(sockfd, &rset)) {
                if ( (n = Read(sockfd, buf, MAXLINE)) == 0) {
                    /* 當客戶關閉連接時,伺服器也關閉對應的連接,並將相應的client數組元素設置為-1,表示空閑 */
                    close(sockfd);
                    FD_CLR(sockfd, &allset);
                    client[i] = -1;
                } else {
                    Writen(sockfd, buf, n);
                }
                if (--nready <= 0)
                    break;                /* 沒有更多就緒事件時,繼續回到select()調用*/
            }
        }
    }
}

三、C Select語句

C Select語句是select函數的一種可移植、跨平台的I/O多路復用機制,可以在一組文件描述符上等待多個I/O事件,多線程同時處理。C Select語句適用於網路編程中,方便實現高並發的伺服器。

以下是一個使用C Select語句進行I/O復用的示例:


/* 實現一個TCP伺服器,監聽10086埠,並在有客戶端連接時返回當前系統時間 */
#include<sys/socket.h>
#include<netinet/in.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/select.h>
#include<time.h>

const int MAXLINE = 1024, LISTENQ = 5, SERV_PORT = 10086;

int main() {
    int i, maxi, maxfd, listenfd, connfd, sockfd;
    int nready, client[FD_SETSIZE];
    ssize_t n;
    char buf[MAXLINE];
    char str[INET_ADDRSTRLEN];
    socklen_t clilen;
    struct sockaddr_in cliaddr, servaddr;
    fd_set rset, allset;

    if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        perror("error: socket");
        return -1;
    }

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(SERV_PORT);

    if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1) {
        perror("error: bind");
        return -1;
    }

    if (listen(listenfd, LISTENQ) == -1) {
        perror("error: listen");
        return -1;
    }

    maxfd = listenfd;
    maxi = -1;
    for (i = 0; i < FD_SETSIZE; i++)
        client[i] = -1;
    FD_ZERO(&allset);
    FD_SET(listenfd, &allset);

    for (;;) {
        rset = allset;
        if ((nready = select(maxfd + 1, &rset, NULL, NULL, NULL)) == -1) {
            perror("error: select");
            return -1;
        }

        if (FD_ISSET(listenfd, &rset)) {
            clilen = sizeof(cliaddr);
            connfd = accept(listenfd, (struct sockaddr*)&cliaddr, &clilen);
            printf("accept client ip=%s port=%d\n",
                inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
                ntohs(cliaddr.sin_port));


            for (i = 0; i < FD_SETSIZE; i++)
                if (client[i] < 0) {
                    client[i] = connfd;
                    break;
                }

            if(i == FD_SETSIZE) {
                perror("too many clients");
                return -1;
            }

            FD_SET(connfd, &allset);

            if (connfd > maxfd)
                maxfd = connfd;
            if (i > maxi)
                maxi = i;

            if (--nready == 0)
                continue;
        }
        for (i = 0; i <= maxi; i++) {
            if ((sockfd = client[i]) < 0)
                continue;
            if (FD_ISSET(sockfd, &rset)) {
                if ((n = read(sockfd, buf, MAXLINE)) == 0) {
                    close(sockfd);
                    FD_CLR(sockfd, &allset);
                    client[i] = -1;
                }
                else {
                    time_t t = time(NULL);
                    char buffer[1024] = {0};
                    strftime(buffer, 1024, "%Y-%m-%d %H:%M:%S\n", localtime(&t));
                    write(sockfd, buffer, strlen(buffer));
                }
                if (--nready == 0)
                    break;
            }
        }
    }
    return 0;
}

四、C Select詳解與模型

C Select是一種基於事件驅動的編程範式,其主要用於網路編程中的I/O多路復用與高並發伺服器。其基本工作原理為:首先需要定義一組文件描述符,通過select函數等待其事件到達,當其中任意一個文件描述符事件發生變化時,select函數返回;在返回後,通過遍歷文件描述符集合獲取到其中發生變化的文件描述符並進行對應處理。

C Select模型與並發伺服器中的運作模型是有關聯的:在並發伺服器中,通過創建多線程來支持多並發客戶端連接,當每個線程需要處理多個不同的客戶端請求時,其I/O事件規律拓撲結構應採用Multiplexer或Reactor這兩種I/O通信模型,其中,Multiplexer模型使用了Select程序,而Reactor模型使用的是相對更為高級的Epoll機制。

以下為一個簡單的使用C Select函數的示例:


#include<stdio.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/time.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<netinet/ip.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>

void server(int port) {
int fd = socket(AF_INET, SOCK_STREAM, 0); // 創建套接字
if (fd == -1) {
perror("socket");
return;
}
int optval = 1;
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval); // 配置選項
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(port);
if (bind(fd, (struct sockaddr*)&addr, sizeof addr) == -1) { // 綁定埠

原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/278898.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
小藍的頭像小藍
上一篇 2024-12-20 15:02
下一篇 2024-12-20 15:02

相關推薦

  • 使用SQL實現select 聚合查詢結果前加序號

    select語句是資料庫中最基礎的命令之一,用於從一個或多個表中檢索數據。常見的聚合函數有:count、sum、avg等。有時候我們需要在查詢結果的前面加上序號,可以使用以下兩種方…

    編程 2025-04-29
  • 深入理解SQL SELECT AS

    一、AS的基本用法 1、AS的含義 在SQL語句中,SELECT語句除了可以通過列名選擇指定的列,還可以對查詢結果進行自定義,對列進行別名定義。這個別名就是AS的作用。 SELEC…

    編程 2025-04-25
  • 深入了解select模型

    一、select模型特點 select是傳統的IO多路復用模型,與其他IO多路復用模型(如epoll,kqueue)不同的是,select函數能夠同時監聽多個socket句柄的可讀…

    編程 2025-04-23
  • Select Join的作用與應用

    一、Select Join簡介 Select Join是SQL中的命令語句,常用於連接多個數據表以顯示相關數據。該操作能夠通過使用共同的列連接多個表,從而將這些表的行組合在一起,從…

    編程 2025-04-23
  • SQL SELECT AS詳解

    在 SQL 中,使用 SELECT 語句根據給定的條件從資料庫中選取數據。AS 關鍵字用於為列或表格名稱指定別名,提供更準確、更有意義的列名和表名。 一、AS關鍵字的基礎使用 AS…

    編程 2025-04-12
  • Select1和Select*的區別

    一、Select語句 在資料庫操作中,Select語句是最常用的語句之一,它用來從表中獲取數據,再根據數據的不同屬性進行分類、計算等操作。在Select語句中,一個常見的問題是:在…

    編程 2025-03-12
  • User-select詳解:控制文字選中

    一、user-select怎麼樣 user-select屬性控制是否允許用戶選擇文本,以及如何選擇文本。該屬性在CSS3中出現。 如果user-select的值被設置為none,則…

    編程 2025-03-12
  • jQuery select change事件詳解

    一、基本用法 1、首先需要在網頁中引入jQuery庫 <script src=”https://code.jquery.com/jquery-3.6.0.min.js”&gt…

    編程 2025-02-17
  • 詳解el-select的默認值

    一、el-select 默認值 el-select是Element UI組件庫中的一種下拉選擇器。在使用el-select組件時,可以通過設置默認值來使頁面初始顯示選定的選項。el…

    編程 2025-02-15
  • 深入解析select*fromwhere

    一、select from where如何提升搜索速度 當我們需要從一張表中查詢數據時,我們可以通過使用select語句來實現。使用select語句時,我們需要指定要查詢的列名和查…

    編程 2025-02-05

發表回復

登錄後才能評論