《Linux網路開發必學教程》4_嚐鮮 seclect 多路複用

TianSong發表於2022-05-03
問題:如何增強服務端能力,同時支援多個客戶端?

Linux 的設計哲學:一切皆檔案

Linux 中的檔案是什麼?
  • 俠義:

    • 檔案系統中物理意義上的檔案(邏輯上關聯的資料集合)
  • 廣義:

    • 裝置,通道,記憶體,。。。
    • Linux 管理的一切物件
理解檔案描述符
  • 檔案描述符是一個非負整數,本質是一個控制程式碼
  • 一切對使用者(程式設計師)透明的資源標識都可以看作控制程式碼
  • 使用者使用檔案描述符(控制程式碼)與核心互動
  • 核心通過檔案描述符操作對應資源的資料結構
一切皆檔案的意義
  • 統一各種裝置的操作方式(open,read,write,close)
  • 如:

    • IO 裝置(命令列,顯示器)
    • 網路裝置(網路卡)
    • ...
程式設計實驗:以檔案方式操作命令列
#include <stdio.h>
#include <unistd.h>

int main()
{
    int iofd = 0;
    char s[] = "D.T.SoftWare\n";
    int len = 0;

    write(iofd, s, sizeof(s));

    len = read(0, s, 5);

    s[len] = 0;

    printf("%s\n", s);

    return 0;
}

輸出:

book@100ask:~/Desktop$ ./a.out 
D.T.SoftWare
12345
12345
book@100ask:~/Desktop$ 

...

book@100ask:~/Desktop$ ./a.out 
D.T.SoftWare
12345678
12345                              // 注意這裡為什麼只輸出了 12345
book@100ask:~/Desktop$ 678         // 注意這裡為什麼輸出 678
678: command not found

答:在 a.out 應用程式中輸入了"12345678",但應用程式只讀取了 5 個字元,即"12345"。當應用程式結束,a.out所在終端取得所有權,得到 "678", 並嘗試將其當作命令解析
事件相關函式的分類
  • 阻塞式函式

    • 函式呼叫後需要等待某個事件發生後才會返回
  • 非阻塞式函式

    • 函式呼叫後能夠及時返回(僅標記等待的事件)
    • 事件發生後以回撥方式傳遞
阻塞 VS 輪詢
  • 輪詢指依序訪問每一個相關裝置是否需要服務的方式
  • 輪詢可用於解決阻塞函式導致程式無法繼續執行的問題

image.png

神奇的 select() 函式
  • select() 用於監視指定的檔案符是否產生事件
  • 可通過輪詢的方式檢測目標檔案(事件產生則標記發生變化)
  • 根據事件型別做出具體處理(如:讀資料)
int select(int maxfd,                    // maxfd = n(最大的檔案描述符 ) + 1,標記監聽的描述符範圍 [0 - maxfd-1]
    fd_set *readset,                     // 檢查可讀性
    fd_set *writeset,                    // 檢查可寫性
    fd_set *exceptset,                   // 檢查異常
    const struct timeval *timeout);      // 等待 IO 的時長
select() 函式的使用步驟

image.png

select() 相關資料型別及操作

fd_set 的每一位標識一個檔案描述符,當某一位為 1,則表示監聽
image.png

* FD_SERO(fd_set *fdset);         // 將 fd_set 變數的所有位設定為0
* FD_SET(int fd, fd_set*fdset);   // 在 fd_set 中指定要監聽的 fd
* FD_CLR(int fd, fd_set*fdset);   // 在 fd_set 中剔除 fd, 不再監聽
* FD_ISSET(int fd, fd_set*fdset); // 在 fd_set 產看是否包含 fd
程式設計實驗:select() 初體驗
#include <sys/select.h>
#include <sys/time.h>
#include <stdio.h>
#include <unistd.h>

int main()
{
    int iofd = 0;
    char s[] = "D.T.SoftWare";
    int len = 0;
    fd_set reads = {0};
    fd_set temps = {0};
    struct timeval timeout = {0};

    FD_ZERO(&reads);
    FD_SET(iofd, &reads);

    while (1) {
        int r = -1;

        temps = reads; // NOTICE !!!

        timeout.tv_sec  = 0;
        timeout.tv_usec = 50000;

        r = select(1, &temps, 0, 0, &timeout);

        if (r > 0) {
            len = read(iofd, s, sizeof(s)-1);

            s[len] = 0;

            printf("Input: %s\n", s);
        }
        else if (r == 0) {
            static int count = 0;

            usleep(10000);

            count++;

            if (count > 100) {
                printf("do something slse\n");

                count = 0; 
            }
        }
        else {
            break;
        }
    }

    return 0;
}

輸出:

book@100ask:~/Desktop$ ./a.out
hello word
Input: hello word

do something slse
...
...

思考:使用 select() 函式可以擴充套件服務端功能嗎?如果可以,具體怎麼實現?

相關文章