欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

select函數(shù)實現(xiàn)高性能IO多路訪問的關(guān)鍵示例深入解析

 更新時間:2023年09月25日 09:40:09   作者:白茶加冰  
這篇文章主要為大家介紹了select函數(shù)實現(xiàn)高性能IO多路訪問的關(guān)鍵示例深入解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

一.select函數(shù)原型

select 函數(shù)的原型如下:

#include <sys/select.h>
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

該函數(shù)接受五個參數(shù):

  • nfds:需要監(jiān)聽的文件描述符的最大編號加 1。在監(jiān)聽范圍內(nèi)的文件描述符包括從 0 到 nfds-1。這個參數(shù)的值應該是監(jiān)聽的所有文件描述符中的最大值加 1。

  • readfds:一個指向 fd_set 結(jié)構(gòu)體的指針,用于指定需要監(jiān)聽可讀事件的文件描述符。

  • writefds:一個指向 fd_set 結(jié)構(gòu)體的指針,用于指定需要監(jiān)聽可寫事件的文件描述符。

  • exceptfds:一個指向 fd_set 結(jié)構(gòu)體的指針,用于指定需要監(jiān)聽異常事件的文件描述符。

  • timeout:一個指向 struct timeval 結(jié)構(gòu)體的指針,用于設置 select 的超時時間。如果設置為 NULL,則 select 將會阻塞直到有事件發(fā)生。如果設置為一個指向 struct timeval 結(jié)構(gòu)體的指針,則 select 會等待指定的時間,超時后返回。

fd_set 是一個文件描述符集合數(shù)據(jù)類型,用于存儲需要監(jiān)聽事件的文件描述符。

struct timeval 是一個時間結(jié)構(gòu)體,用于指定超時時間。它包含了兩個成員:tv_sec 表示秒數(shù),tv_usec 表示微秒數(shù)。

select 函數(shù)返回就緒文件描述符的總數(shù),如果出錯則返回 -1。通過檢查 readfds、writefds 和 exceptfds 來確定哪些文件描述符已經(jīng)就緒。

注意:在使用 select 函數(shù)之前,需要使用 FD_ZERO 和 FD_SET 宏來初始化和設置文件描述符集合。

二.select相關(guān)宏函數(shù)

在使用 select 函數(shù)進行文件描述符集合操作時,常用的宏函數(shù)有以下幾個:

  • FD_ZERO:用于將文件描述符集合清空,將集合中所有位都設置為 0。
void FD_ZERO(fd_set *set);
  • FD_SET:用于將指定的文件描述符加入到集合中。
void FD_SET(int fd, fd_set *set);
  • FD_CLR:用于將指定的文件描述符從集合中移除。
void FD_CLR(int fd, fd_set *set);
  • FD_ISSET:用于檢查指定的文件描述符是否在集合中已經(jīng)設置。
int FD_ISSET(int fd, fd_set *set);

這些宏函數(shù)可以用于操作文件描述符集合,例如初始化集合、添加文件描述符、移除文件描述符以及檢查集合中是否已經(jīng)設置了特定的文件描述符。

需要注意的是,fd_set 是一個位圖類型的結(jié)構(gòu),用于存儲文件描述符集合的狀態(tài)。宏函數(shù)配合使用可以方便地操作這個位圖,以設置和判斷文件描述符的狀態(tài)。

另外,需要包含 <sys/select.h> 頭文件來使用這些宏函數(shù)。

三.select特點

select 函數(shù)是一種多路 I/O 復用機制,具有以下特點:

  • 多路復用:select 允許在一個事件循環(huán)中同時監(jiān)聽多個文件描述符(比如套接字、管道等),并在這些文件描述符就緒時通知應用程序。

  • 平臺兼容性:select 是一種標準的系統(tǒng)調(diào)用,幾乎在所有的 POSIX 操作系統(tǒng)上都有支持,因此具有良好的跨平臺性。

  • 阻塞和非阻塞模式:select 支持兩種調(diào)用方式。在阻塞模式下,調(diào)用 select 會一直等待,直到至少一個文件描述符變得可讀或可寫。而在非阻塞模式下,調(diào)用 select 會立即返回,不管是否有文件描述符就緒。

  • 超時功能:select 允許指定一個超時時間,在等待文件描述符就緒時設定一個最長等待時間。一旦超過設定的超時時間,select 將返回,這樣可以避免無限期地阻塞程序。

  • 可讀性檢查:select 對于讀操作可用于檢查一個文件描述符是否有數(shù)據(jù)可以讀取。

  • 可寫性檢查:select 對于寫操作可用于檢查一個文件描述符是否可以寫入數(shù)據(jù)而不會造成阻塞。

  • 異常條件檢查:select 還可以檢查是否有異常條件發(fā)生,例如套接字的帶外數(shù)據(jù)。

然而,值得注意的是,select 在高并發(fā)情況下可能性能較差,因為在每一次調(diào)用 select 時,都需要遍歷所有的文件描述符。在這種情況下,使用更高效的 I/O 復用機制,如 poll 或 epoll,可能是更好的選擇。

四.select機制

select 是一種多路 I/O 復用機制,用于同時監(jiān)聽多個文件描述符的可讀、可寫和異常事件。它的機制如下:

  • 調(diào)用 select 函數(shù)時,將需要監(jiān)聽的文件描述符通過參數(shù)傳遞給 select 函數(shù),同時設置超時時間(可選)。

  • select 函數(shù)會阻塞程序運行,直到以下三種情況之一發(fā)生:

    • 有一個或多個文件描述符可以讀?。ū恢糜诳勺x狀態(tài))。
    • 有一個或多個文件描述符可以寫入(被置于可寫狀態(tài))。
    • 有一個或多個文件描述符發(fā)生了異常。
  • 當有一個或多個文件描述符就緒時,select 函數(shù)返回,程序可以繼續(xù)往下執(zhí)行。此時,通過檢查文件描述符集合的狀態(tài)(readfds、writefds 和 exceptfds),可以確定哪些文件描述符已經(jīng)就緒。

  • 根據(jù)文件描述符的狀態(tài),進行相應的讀取、寫入或處理異常的操作。

  • 重復以上步驟,循環(huán)使用 select 函數(shù)來監(jiān)聽文件描述符,以實現(xiàn)事件驅(qū)動的編程模式。

select 的特點是可以同時監(jiān)聽多個文件描述符,在有事件發(fā)生時返回,并指示哪些文件描述符已經(jīng)就緒,使得程序能夠及時處理相關(guān)操作。它適用于需要同時處理多個 I/O 事件的情況,并且具有良好的跨平臺性。然而,在高并發(fā)情況下性能可能較差,因為每次調(diào)用 select 都需要遍歷所有的文件描述符。在這種情況下,可以考慮使用其他更高效的 I/O 復用機制,如 poll 或 epoll。

五.select編程步驟

使用 select 函數(shù)進行 I/O 復用時,通常包含以下步驟:

  • 準備文件描述符集合:創(chuàng)建并初始化需要監(jiān)聽的文件描述符集合,例如使用 fd_set 類型的變量,并使用 FD_ZERO 宏將其清空,然后使用 FD_SET 宏將需要監(jiān)聽的文件描述符添加到集合中。

  • 設置超時時間:可選擇是否設置超時時間。如果需要設置超時時間,在 struct timeval 結(jié)構(gòu)體中設置合適的值,然后將該結(jié)構(gòu)體的指針作為 select 函數(shù)的最后一個參數(shù)傳遞給函數(shù)。

  • 調(diào)用 select 函數(shù):將文件描述符集合作為參數(shù)傳遞給 select 函數(shù),并傳遞其他必要的參數(shù),如最大文件描述符編號加 1(nfds)以及超時時間(可選)。調(diào)用 select 函數(shù)后,程序?qū)⒃谠摵瘮?shù)處阻塞等待。

  • 檢查就緒文件描述符:當 select 函數(shù)返回時,需要檢查文件描述符集合的狀態(tài),以確定哪些文件描述符已經(jīng)就緒??梢允褂?nbsp;FD_ISSET 宏來檢查特定的文件描述符是否已經(jīng)設置。

  • 處理就緒文件描述符:根據(jù)文件描述符的狀態(tài)(可讀、可寫或異常),進行相應的處理操作。例如,如果一個文件描述符可讀,可以使用 read 函數(shù)讀取數(shù)據(jù);如果一個文件描述符可寫,可以使用 write 函數(shù)寫入數(shù)據(jù);如果一個文件描述符發(fā)生異常,可以進行相應的錯誤處理。

  • 重復步驟 3-5:根據(jù)需要,可以使用循環(huán)來多次調(diào)用 select 函數(shù)以處理更多的事件。這樣可以實現(xiàn)事件驅(qū)動的編程模式,不斷監(jiān)聽和處理多個文件描述符的事件。

需要注意的是,在每次循環(huán)中調(diào)用 select 函數(shù)之前需要重新設置文件描述符集合和超時時間,以反映當前需要監(jiān)聽的文件描述符集合和超時時間的變化。

六.自定義數(shù)組提高效率

要提高 select 函數(shù)的效率,可以考慮使用自定義的數(shù)組來替代文件描述符集合,以便更有效地管理和操作需要監(jiān)聽的文件描述符。

以下是使用自定義數(shù)組提高 select 函數(shù)效率的一般步驟:

  • 創(chuàng)建自定義數(shù)組:根據(jù)需要監(jiān)聽的文件描述符數(shù)量,創(chuàng)建一個數(shù)組來存儲這些文件描述符的信息??梢允褂谜麛?shù)型數(shù)組、結(jié)構(gòu)體數(shù)組等來表示每個文件描述符的狀態(tài)和其他相關(guān)信息。
  • 初始化自定義數(shù)組:在程序開始時,初始化自定義數(shù)組,設置每個文件描述符的初始狀態(tài)和其他必要的信息。
  • 更新自定義數(shù)組:在程序中需要做出更改時,通過修改自定義數(shù)組中的相應元素來反映文件描述符的狀態(tài)變化。
  • 使用自定義數(shù)組代替 select 函數(shù)的文件描述符集合:在調(diào)用 select 函數(shù)之前,將自定義數(shù)組轉(zhuǎn)換為對應的文件描述符集合,例如使用 FD_ZERO 和 FD_SET 宏來設置相應的文件描述符集合。
  • 調(diào)用 select 函數(shù):使用轉(zhuǎn)換后的文件描述符集合作為參數(shù)調(diào)用 select 函數(shù),并傳遞其他必要的參數(shù),如最大文件描述符編號加 1(nfds)以及超時時間(可選)。
  • 檢查就緒文件描述符:當 select 函數(shù)返回時,檢查文件描述符集合的狀態(tài)以確定哪些文件描述符已經(jīng)就緒??梢酝ㄟ^遍歷自定義數(shù)組,根據(jù)其中的狀態(tài)信息來確定哪些文件描述符已經(jīng)就緒。
  • 處理就緒文件描述符:根據(jù)文件描述符的狀態(tài),進行相應的處理操作,如讀取、寫入或處理異常等。
  • 重復步驟 3-7:根據(jù)需要,可以使用循環(huán)來多次調(diào)用 select 函數(shù)以處理更多的事件。

使用自定義數(shù)組可以更靈活地管理文件描述符,并且可以根據(jù)程序需求進行優(yōu)化,以提高效率。但請注意,當需要監(jiān)聽的文件描述符數(shù)量過大時,自定義數(shù)組的使用可能會導致額外的內(nèi)存開銷。因此,在性能和內(nèi)存消耗之間需要進行權(quán)衡。

七.編程實戰(zhàn)

練習
利用select實現(xiàn)一個高并發(fā)服務器,當服務連接成功后,客戶端發(fā)送小寫字母的字符串,服務器端發(fā)送其大寫形式。要求自定義一個數(shù)組,用于提高程序效率。

server.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/select.h>
#include <sys/time.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
    //1.socket創(chuàng)建套接字
    int socked = socket(AF_INET, SOCK_STREAM, 0);
    if (socked < 0)
    {
        perror("socket is err");
        return -1;
    }
    //2.bind綁定服務器ip地址和端口號
    struct sockaddr_in saddr, caddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(atoi(argv[1]));
    saddr.sin_addr.s_addr = inet_addr("0.0.0.0");
    int len = sizeof(caddr);
    int ret = bind(socked, (struct sockaddr *)(&saddr), sizeof(saddr));
    if (ret < 0)
    {
        perror("bind is err");
        return -1;
    }
    //3.listen設置同時最大鏈接數(shù)
    ret = listen(socked, 5);
    if (ret < 0)
    {
        perror("listen is err");
        return -1;
    }
    //4.select前置工作
    fd_set readfds, tempfds; //創(chuàng)建表
    FD_ZERO(&readfds);       //清空表
    FD_SET(socked, &readfds); //添加文件描述符
    FD_SET(0, &readfds);
    int maxfd = socked, size = 0, maxi = -1, client[1024] = {0};
    char buf[1024] = {0};
    //初始化數(shù)組為-1
    for (int i = 0; i < 1024; ++i)
        client[i] = -1;
    while (1)
    {
        tempfds = readfds;
        int ret = select(maxfd + 1, &tempfds, NULL, NULL, NULL);
        if (ret < 0)
        {
            perror("select is err");
            break;
        }
        if (FD_ISSET(socked, &tempfds))
        {
            int fd = accept(socked, (struct sockaddr *)(&caddr), &len);
            printf("port:%d   ip:  %s\n", ntohs(caddr.sin_port), inet_ntoa(caddr.sin_addr));
            //文件描述符加入數(shù)組中
            int i;
            for (i = 0; i < 1024; ++i)
            {
                if (client[i] < 0)
                {
                    client[i] = fd;
                    break;
                }
            }
            if (i == 1024)
            {
                printf("too many clients\n");
                exit(-1);
            }
            if (i > maxi)
                maxi = i;
            FD_SET(fd, &readfds);
            if (maxfd < fd)
                maxfd = fd;
            if (--ret == 0)
                continue;
        }
        for (int i = 0; i <= maxi; ++i)
        {
            if (client[i] < 0)
                continue;
            if (FD_ISSET(client[i], &tempfds))
            {
                int flage = recv(client[i], buf, sizeof(buf), 0);
                if (flage < 0)
                {
                    perror("recv is err");
                }
                else if (flage == 0)
                {
                    printf("ip:%s is close\n", inet_ntoa(caddr.sin_addr));
                    close(i);
                    FD_CLR(i, &readfds);
                    client[i] = -1;
                }
                else
                {
                    size = strlen(buf);
                    for (int i = 0; i < size; ++i)
                    {
                        if (buf[i] >= 'a' && buf[i] <= 'z')
                            buf[i] = buf[i] + ('A' - 'a');
                        else
                            buf[i] = buf[i] + ('a' - 'A');
                    }
                    printf("%s\n", buf);
                    send(client[i], buf, sizeof(buf), 0);
                }
                if (--ret == 0)
                    break;
            }
        }
    }
    close(socked);
    return 0;
}

client.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
    //1.socket建立文件描述符
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd < 0)
    {
        perror("socket is err");
    }
    //2.connect連接服務器
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(atoi(argv[2]));
    saddr.sin_addr.s_addr = inet_addr(argv[1]);
    int flage = connect(fd, (struct sockaddr *)(&saddr), sizeof(saddr));
    if (flage < 0)
    {
        perror("connect is err");
    }
    //3.服務器端不斷發(fā)送數(shù)據(jù),接受服務器轉(zhuǎn)化后的數(shù)據(jù)
    char buf[1024] = {0};
    while (1)
    {
        //memset(buf,0,sizeof(buf));
        fgets(buf, sizeof(buf), stdin);
        if (strncmp(buf,"quit#",5)==0)
        {
            break;
        }
        if (buf[strlen(buf) - 1] == '\n')
            buf[strlen(buf) - 1] = '\0';
        send(fd, buf, sizeof(buf), 0);
        flage = recv(fd, buf, sizeof(buf), 0);
        if (flage < 0)
        {
            perror("recv is err");
        }
        else
        {
            fprintf(stdout, "%s\n", buf);
        }
    }
    close(fd);
    return 0;
}

以上就是select函數(shù)實現(xiàn)高性能IO多路訪問的關(guān)鍵示例深入解析的詳細內(nèi)容,更多關(guān)于select函數(shù)IO多路訪問的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論