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

C語言詳解select函數(shù)的使用

 更新時(shí)間:2022年05月04日 10:28:00   作者:liufeng2023  
C語言中select函數(shù)的使用?一般用connect、accept、recv或recvfrom這類函數(shù),程序阻塞,直至該套接字上接受到數(shù)據(jù)后程序才能繼續(xù)運(yùn)行。但是使用select函數(shù)可以實(shí)現(xiàn)非阻塞方式的程序

select

select API介紹

主旨思想:

  • 首先要構(gòu)造一個(gè)關(guān)于文件描述符的列表,將要監(jiān)聽的文件描述符添加到該列表中。
  • 調(diào)用一個(gè)系統(tǒng)函數(shù),監(jiān)聽該列表中的文件描述符,直到這些描述符中的一個(gè)或者多個(gè)進(jìn)行I/O操作時(shí),該函數(shù)才返回。

a. 這個(gè)函數(shù)是阻塞

b. 函數(shù)對文件描述符的檢測的操作是由內(nèi)核完成的

  • 在返回時(shí),它會告訴進(jìn)程有多少(哪些)描述符要進(jìn)行I/O操作。

// sizeof(fd_set) = 128字節(jié)     1024位, 每一個(gè)標(biāo)志位對應(yīng)一個(gè)文件描述符
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/select.h>
int select(int nfds, fd_set *readfds, fd_set *writefds,
            fd_set *exceptfds, struct timeval *timeout);
    - 參數(shù):
        - nfds : 委托內(nèi)核檢測的最大文件描述符的值 + 1
        - readfds : 要檢測的文件描述符的讀的集合(有數(shù)據(jù)發(fā)送過來),委托內(nèi)核檢測哪些文件描述符的讀的屬性
                - 一般檢測讀操作
                - 對應(yīng)的是對方發(fā)送過來的數(shù)據(jù),因?yàn)樽x是被動(dòng)的接收數(shù)據(jù),檢測的就是讀緩沖區(qū)    
                - 是一個(gè)傳入傳出參數(shù)(內(nèi)核進(jìn)行對文件描述符標(biāo)志位檢測,檢測完后再返回回來;)
                 檢測過程: 文件描述符在用戶態(tài),1表示文件描述符需要檢測,0表示不需要檢測;在內(nèi)核中處理時(shí):只檢測文件描述符為                        1的文件描述符,如果數(shù)據(jù)發(fā)生變化,置為1,不變化置為0,然后返回給用戶態(tài)。
        - writefds : 要檢測的文件描述符的寫的集合,委托內(nèi)核檢測哪些文件描述符的寫的屬性
                - 委托內(nèi)核檢測寫緩沖區(qū)是不是還可以寫數(shù)據(jù)(不滿的就可以寫)
                要檢測哪個(gè)文件描述符,就將那個(gè)標(biāo)志位置為1;
                緩沖區(qū)滿了將對應(yīng)文件描述符標(biāo)志位置為0,有空余的數(shù)據(jù)可以寫, 置為1。
        - exceptfds : 檢測發(fā)生異常的文件描述符的集合
        - timeout : 設(shè)置的超時(shí)時(shí)間
            struct timeval {
                long tv_sec;     /* seconds */
                long tv_usec;     /* microseconds */
            };
            - NULL : 永久阻塞,直到檢測到了文件描述符有變化
            - tv_sec = 0 tv_usec = 0, 不阻塞
            - tv_sec > 0 tv_usec > 0, 阻塞對應(yīng)的時(shí)間
        - 返回值
            - -1 : 失敗
            - 0 : select函數(shù)中設(shè)置了超時(shí)時(shí)間,超時(shí)時(shí)間到了,沒有檢測到,返回0
            - >0(n) : 檢測的集合中有n個(gè)文件描述符發(fā)生了變化
                
// 將參數(shù)文件描述符fd對應(yīng)的標(biāo)志位設(shè)置為0
void FD_CLR(int fd, fd_set *set);

// 判斷fd對應(yīng)的標(biāo)志位是0還是1, 返回值 : fd對應(yīng)的標(biāo)志位的值,0,返回0, 1,返回1
int FD_ISSET(int fd, fd_set *set);

// 將參數(shù)文件描述符fd 對應(yīng)的標(biāo)志位,設(shè)置為1
void FD_SET(int fd, fd_set *set);

// fd_set一共有1024 bit位, 全部初始化為0
void FD_ZERO(fd_set *set);

先創(chuàng)建檢測讀的文件描述符集合

fd_set reads;

將3,4,100,101四個(gè)文件描述符置為1,表示需要對這些文件描述符進(jìn)行檢測;

接下來調(diào)用select函數(shù):

select(101+1, &reads, NULL, NULL, NULL);	//第一個(gè)參數(shù),需要檢測的文件描述符+1	--(+1才能遍歷到101,因?yàn)槭菑?開始的)

在這里插入圖片描述

將fd_set從內(nèi)核態(tài)拷貝到用戶態(tài)(由內(nèi)核幫我們檢測),同時(shí)假設(shè)A,B發(fā)送了數(shù)據(jù);

A,B對應(yīng)的文件描述符為3和4;

在這里插入圖片描述

結(jié)果: 3和4有數(shù)據(jù),置為1,;100和101沒有數(shù)據(jù),將其1置為0。

修改完之后,再將fd_set從內(nèi)核態(tài)拷到用戶態(tài);

在這里插入圖片描述

用戶可以遍歷這個(gè)集合,找到哪個(gè)文件描述符為1,即3和4,就說明有數(shù)據(jù)了。

select 代碼

//客戶端
#include <stdio.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main() {
    // 創(chuàng)建socket
    int fd = socket(PF_INET, SOCK_STREAM, 0);
    if(fd == -1) {
        perror("socket");
        return -1;
    }
    struct sockaddr_in seraddr;
    inet_pton(AF_INET, "127.0.0.1", &seraddr.sin_addr.s_addr);
    seraddr.sin_family = AF_INET;
    seraddr.sin_port = htons(9999);
    // 連接服務(wù)器
    int ret = connect(fd, (struct sockaddr *)&seraddr, sizeof(seraddr));
    if(ret == -1){
        perror("connect");
        return -1;
    }
    int num = 0;
    while(1) {
        char sendBuf[1024] = {0};
        sprintf(sendBuf, "send data %d", num++);
        write(fd, sendBuf, strlen(sendBuf) + 1);
        // 接收
        int len = read(fd, sendBuf, sizeof(sendBuf));
        if(len == -1) {
            perror("read");
            return -1;
        }else if(len > 0) {
            printf("read buf = %s\n", sendBuf);
        } else {
            printf("服務(wù)器已經(jīng)斷開連接...\n");
            break;
        }
        // sleep(1);
        usleep(1000);
    }
    close(fd);
    return 0;
}
//服務(wù)端
#include <stdio.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/select.h>
int main() {
    // 創(chuàng)建socket
    int lfd = socket(PF_INET, SOCK_STREAM, 0);
    struct sockaddr_in saddr;
    saddr.sin_port = htons(9999);
    saddr.sin_family = AF_INET;
    saddr.sin_addr.s_addr = INADDR_ANY;
    // 綁定
    bind(lfd, (struct sockaddr *)&saddr, sizeof(saddr));
    // 監(jiān)聽
    listen(lfd, 8);
    // 創(chuàng)建一個(gè)fd_set的集合,存放的是需要檢測的文件描述符
    fd_set rdset, tmp;	//fd_set底層可以表示1024個(gè)文件描述符
    FD_ZERO(&rdset);	//初始化
    FD_SET(lfd, &rdset);	//添加需要監(jiān)聽的文件描述符
    int maxfd = lfd;	//定義最大文件描述符,作為參數(shù)傳入select函數(shù)中
    while(1) {
        tmp = rdset;	//rdset這個(gè)不能變,因?yàn)閮?nèi)核再檢測時(shí),如果沒有數(shù)據(jù),就會將其變?yōu)?,因此,我們需要復(fù)制一份。
        // 調(diào)用select系統(tǒng)函數(shù),讓內(nèi)核幫檢測哪些文件描述符有數(shù)據(jù)
        int ret = select(maxfd + 1, &tmp, NULL, NULL, NULL);
        if(ret == -1) {
            perror("select");
            exit(-1);
        } else if(ret == 0) {	//這里不可能為0,因?yàn)樵O(shè)置了永久阻塞NULL,直到檢測到文件描述符有數(shù)據(jù)變化
            continue;	
        } else if(ret > 0) {	//ret只會返回文件描述符發(fā)生變化的個(gè)數(shù),不知道具體哪個(gè)發(fā)生了變化,需要遍歷查找
            // 說明檢測到了有文件描述符的對應(yīng)的緩沖區(qū)的數(shù)據(jù)發(fā)生了改變
            if(FD_ISSET(lfd, &tmp)) {	//lfd為監(jiān)聽文件描述符
                // 表示有新的客戶端連接進(jìn)來了
                struct sockaddr_in cliaddr;
                int len = sizeof(cliaddr);
                int cfd = accept(lfd, (struct sockaddr *)&cliaddr, &len);
                // 將新的文件描述符加入到集合中,下一次select檢測時(shí),需要檢測這些通信的文件描述符有沒有數(shù)據(jù)
                FD_SET(cfd, &rdset);
                // 更新最大的文件描述符
                maxfd = maxfd > cfd ? maxfd : cfd;
            }
            //檢測剩余文件描述符有沒有數(shù)據(jù)變化,從lfd+1開始即可
            for(int i = lfd + 1; i <= maxfd; i++) {
                if(FD_ISSET(i, &tmp)) {
                    // 說明這個(gè)文件描述符對應(yīng)的客戶端發(fā)來了數(shù)據(jù)
                    char buf[1024] = {0};
                    int len = read(i, buf, sizeof(buf));
                    if(len == -1) {
                        perror("read");
                        exit(-1);
                    } else if(len == 0) {	//說明客戶端斷開連接
                        printf("client closed...\n");
                        close(i);	//關(guān)閉文件描述符
                        FD_CLR(i, &rdset);	//fd_set中不在監(jiān)測這個(gè)文件描述符
                    } else if(len > 0) {
                        printf("read buf = %s\n", buf);
                        write(i, buf, strlen(buf) + 1);
                    }
                }
            }
        }
    }
    close(lfd);
    return 0;
}

編譯運(yùn)行

再打開一個(gè)客戶端:

服務(wù)端可以看出有新客戶端進(jìn)來了:

又連進(jìn)來了新的客戶端:

select和poll缺點(diǎn)

到此這篇關(guān)于C語言詳解select函數(shù)的使用的文章就介紹到這了,更多相關(guān)C語言select函數(shù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C++詳細(xì)分析引用的使用及其底層原理

    C++詳細(xì)分析引用的使用及其底層原理

    引用是C++一個(gè)很重要的特性,顧名思義是某一個(gè)變量或?qū)ο蟮膭e名,對引用的操作與對其所綁定的變量或?qū)ο蟮牟僮魍耆葍r(jià),這篇文章主要給大家總結(jié)介紹了C++中引用的相關(guān)知識點(diǎn),需要的朋友可以參考下
    2022-04-04
  • C++中的繼承問題(繼承基本概念、菱形虛擬繼承的對象模型)

    C++中的繼承問題(繼承基本概念、菱形虛擬繼承的對象模型)

    這篇文章主要介紹了C++中的繼承問題(繼承基本概念、菱形虛擬繼承的對象模型),具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-02-02
  • 淺析C語言調(diào)試器GDB和LLDB的使用方法

    淺析C語言調(diào)試器GDB和LLDB的使用方法

    這篇文章主要介紹了C語言調(diào)試器GDB和LLDB的使用方法,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-12-12
  • LintCode 堆化詳解及實(shí)例代碼

    LintCode 堆化詳解及實(shí)例代碼

    這篇文章主要介紹了LintCode 堆化詳解及實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下
    2017-04-04
  • 用C++的odeint庫求解微分方程

    用C++的odeint庫求解微分方程

    求解微分方程的數(shù)值解一般使用MATLAB等數(shù)值計(jì)算軟件,其實(shí)C++也可以求解微分方程,需要用到odeint庫,它是boost庫的一部分。官方教程和示例比較晦澀,本文力求用較短的篇幅介紹它的基本用法,需要的朋友可以參考下面文章的具體內(nèi)容
    2021-09-09
  • 詳解Matlab如何繪制圓角半透明圖例

    詳解Matlab如何繪制圓角半透明圖例

    目前MATLAB的legend圖例是不支持圓角和半透明的,所以本文將自制實(shí)現(xiàn)圓角半透明圖例。文中的示例代碼講解詳細(xì),需要的可以參考一下
    2022-05-05
  • c語言實(shí)現(xiàn)通訊錄管理系統(tǒng)詳細(xì)實(shí)例

    c語言實(shí)現(xiàn)通訊錄管理系統(tǒng)詳細(xì)實(shí)例

    這篇文章主要給大家介紹了關(guān)于c語言實(shí)現(xiàn)通訊錄管理系統(tǒng)的相關(guān)資料,通訊錄管理系統(tǒng)是一種常見的應(yīng)用程序,可以用來管理聯(lián)系人的信息,包括姓名、電話號碼、地址等,需要的朋友可以參考下
    2023-07-07
  • C++實(shí)現(xiàn)LeetCode(137.單獨(dú)的數(shù)字之二)

    C++實(shí)現(xiàn)LeetCode(137.單獨(dú)的數(shù)字之二)

    這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(137.單獨(dú)的數(shù)字之二),本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-07-07
  • 詳解vs2022創(chuàng)建及調(diào)用.lib的方法

    詳解vs2022創(chuàng)建及調(diào)用.lib的方法

    這篇文章主要介紹了vs2022創(chuàng)建及調(diào)用.lib的方法,調(diào)用Lib的原則就是可以讓編譯器找到頭文件和庫文件的目錄,并正確引入,本文給大家詳細(xì)講解需要的朋友可以參考下
    2022-11-11
  • C++實(shí)現(xiàn)二叉樹基本操作詳解

    C++實(shí)現(xiàn)二叉樹基本操作詳解

    這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)二叉樹基本操作,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-12-12

最新評論