国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

epoll() 多路復(fù)用 和 兩種工作模式

這篇具有很好參考價(jià)值的文章主要介紹了epoll() 多路復(fù)用 和 兩種工作模式。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

一、epoll概述

epollLinux內(nèi)核中的一個(gè)事件驅(qū)動(dòng)I/O機(jī)制,用于處理多個(gè)文件描述符上的事件。它是一個(gè)高效且強(qiáng)大的I/O多路復(fù)用工具,可以用于處理大量文件描述符的I/O操作。epoll的主要優(yōu)點(diǎn)是它只占用較少的資源,并且比傳統(tǒng)的selectpoll更易于使用。

epoll的工作原理是通過(guò)一個(gè)事件表來(lái)跟蹤所有需要監(jiān)控的文件描述符。當(dāng)某個(gè)文件描述符上有事件發(fā)生時(shí),epoll會(huì)通知程序去處理這些事件。這種方式可以確保程序在等待某個(gè)文件描述符上有事件發(fā)生時(shí)只占用較少的資源,而不是像selectpoll那樣整個(gè)程序都阻塞。

----來(lái)自CodeGeex

二、epoll

1.epoll API 介紹

typedef union epoll_data {
	void *ptr;
	int fd;
	uint32_t u32;
	uint64_t u64;
} epoll_data_t;

struct epoll_event {
	uint32_t events; /* Epoll events */
	epoll_data_t data; /* User data variable */
};

常見(jiàn)的Epoll檢測(cè)事件:
	- EPOLLIN
	- EPOLLOUT
	- EPOLLERR
	
// 對(duì)epoll實(shí)例進(jìn)行管理:添加文件描述符信息,刪除信息,修改信息
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
	- 參數(shù):
		- epfd : epoll實(shí)例對(duì)應(yīng)的文件描述符
		- op : 要進(jìn)行什么操作
				EPOLL_CTL_ADD: 添加
				EPOLL_CTL_MOD: 修改
				EPOLL_CTL_DEL: 刪除
		- fd : 要檢測(cè)的文件描述符
		- event : 檢測(cè)文件描述符什么事情

// 檢測(cè)函數(shù)----檢測(cè)epoll樹(shù)中是否有就緒的文件描述符
// 創(chuàng)建了epfd,設(shè)置好某個(gè)fd上需要檢測(cè)事件并將該fd綁定到epfd上去后,就可以調(diào)用epoll_wait
// 檢測(cè)事件了
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
	- 參數(shù):
		- epfd : epoll實(shí)例對(duì)應(yīng)的文件描述符
		- events : 傳出參數(shù),保存了發(fā)送了變化的文件描述符的信息
		- maxevents : 第二個(gè)參數(shù)結(jié)構(gòu)體數(shù)組的大小
		- timeout : 阻塞時(shí)間
			- 0 : 不阻塞
			- -1 : 阻塞,直到檢測(cè)到fd數(shù)據(jù)發(fā)生變化,解除阻塞
			- > 0 : 阻塞的時(shí)長(zhǎng)(毫秒)
	- 返回值:
		- 成功,返回發(fā)送變化的文件描述符的個(gè)數(shù) > 0
		- 失敗 -1

// 創(chuàng)建epoll實(shí)例,通過(guò)一棵紅黑樹(shù)管理待檢測(cè)集合
// 參數(shù) size 從 Linux 2.6.8 以后就不再使用,但是必須設(shè)置一個(gè)大于 0 的值。epoll_create 函數(shù)調(diào)用成功返回一個(gè)非負(fù)值的 epollfd,調(diào)用失敗返回 -1。
int epoll_create(int size);
>>epoll_wait 缺點(diǎn):
    ① epoll_wait 調(diào)用之后,需要將所有fd的event參數(shù)重新設(shè)置一遍,
      如果fd比較多的話,會(huì)比較消耗性能。----來(lái)自CodeGeeX

>>epoll_wait 優(yōu)點(diǎn):
    ① epoll_wait 調(diào)用之后,直接在event參數(shù)中拿到所有有事件就緒的fd,直接處理即可。
    ② 一般在fd數(shù)量比較多,但某段時(shí)間內(nèi),就緒事件fd數(shù)量較少的情況下,epoll_wait才會(huì)
    體現(xiàn)出它的優(yōu)勢(shì),也就是說(shuō)socket連接數(shù)量較大時(shí)而活躍連接較少時(shí)epoll模型更高效。

epoll() 多路復(fù)用 和 兩種工作模式,epoll,多路復(fù)用,兩種工作模式,ET模式,LT模式

// epoll 的使用
// 操作步驟
// 在服務(wù)器使用 epoll 進(jìn)行 IO 多路轉(zhuǎn)接的操作步驟如下:
    1.創(chuàng)建監(jiān)聽(tīng)的套接字
    int lfd = socket(AF_INET, SOCK_STREAM, 0);

    2.設(shè)置端口復(fù)用(可選)
    int opt = 1;
    setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    3.使用本地的IP與端口和監(jiān)聽(tīng)的套接字進(jìn)行綁定
    int ret = bind(lfd, (struct sockaddr*)&saddr, sizeof(saddr));

    4.給監(jiān)聽(tīng)的套接字設(shè)置監(jiān)聽(tīng)
    listen(lfd, 128);

    5.創(chuàng)建 epoll 實(shí)例
    int epfd = epoll_create(100);

    6.將用于監(jiān)聽(tīng)的套接字添加到 epoll 實(shí)例中
    struct epoll_event ev;
    ev.events = EPOLLIN; //檢測(cè)lfd讀緩沖區(qū)是否有數(shù)據(jù)
    ev.data.fd = lfd;
    int ret = epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &ev);

    接著創(chuàng)建一個(gè)數(shù)組,用于存儲(chǔ)epoll_wait()返回的文件描述符
    struct epoll_event evs[1024];

    7.檢測(cè)添加到epoll實(shí)例中的文件描述符是否已經(jīng)就緒,并將這些已就緒的文件描述符進(jìn)行處理
    int num = epoll_wait(epfd, evs, size, -1);

    ① 如果監(jiān)聽(tīng)的是文件描述符,和新客戶端建立連接,將得到的文件描述符添加到epoll實(shí)例中
    int cfd = accept(curfd,NULL,NULL);
    ev.events = EPOLLIN;
    ev.data.fd = cfd;

    新得到的文件描述符添加到epoll模型中,下一輪循環(huán)的時(shí)候就可以被檢測(cè)了
    epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &ev);

    ② 如果是通信的文件描述符,和對(duì)應(yīng)的客戶端通信,如果連接已斷開(kāi),將該文件描述符從epoll實(shí)例中刪除
    int len = recv(curfd,buf,sizeof(buf),0);
    if(len == 0) {
        // 將這個(gè)文件描述符從epoll實(shí)例中刪除
        epoll_ctl(epfd, EPOLL_CTL_DEL, curfd, NULL);
        close(curfd);
    }else if(len > 0) {
        send(curfd,buf,len,0);
    }

    8.重復(fù)第 7 步的操作

往期文章推薦:

IO多路轉(zhuǎn)接(復(fù)用)多線程 select 并發(fā)_呵呵噠( ̄▽ ̄)"的博客-CSDN博客https://blog.csdn.net/weixin_41987016/article/details/132497986?spm=1001.2014.3001.5501

epoll() 多路復(fù)用 和 兩種工作模式,epoll,多路復(fù)用,兩種工作模式,ET模式,LT模式?

select/poll低效的原因之一是將“添加/維護(hù)待檢測(cè)任務(wù)”和“阻塞進(jìn)程/線程”兩個(gè)步驟合二為一。每次調(diào)用select都需要這兩步操作,然而大多數(shù)應(yīng)用場(chǎng)景中,需要監(jiān)視的socket個(gè)數(shù)相對(duì)固定,并不需要每次都修改。epoll將這兩個(gè)操作分開(kāi),先用epoll_ctl()維護(hù)等待隊(duì)列,再調(diào)用epoll_wait()阻塞進(jìn)程(解耦)。通過(guò)下圖的對(duì)比顯而易見(jiàn),epoll的效率得到了提升。


作者: 蘇丙榅
鏈接: https://subingwen.cn/linux/epoll/
來(lái)源: 愛(ài)編程的大丙
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處。

?第一種 IO多路轉(zhuǎn)接技術(shù):select/pollepoll() 多路復(fù)用 和 兩種工作模式,epoll,多路復(fù)用,兩種工作模式,ET模式,LT模式

?epoll() 多路復(fù)用 和 兩種工作模式,epoll,多路復(fù)用,兩種工作模式,ET模式,LT模式

epoll() 多路復(fù)用 和 兩種工作模式,epoll,多路復(fù)用,兩種工作模式,ET模式,LT模式

?

server.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/epoll.h>

int main() {
    
    // 創(chuàng)建socket
    int lfd = socket(AF_INET,SOCK_STREAM,0);
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(9999);
    saddr.sin_addr.s_addr = INADDR_ANY;

    // 綁定
    int ret = bind(lfd,(struct sockaddr*)&saddr,sizeof(saddr));
    if(ret == -1) {
        perror("bind");
        exit(-1);
    }

    // 監(jiān)聽(tīng)
    ret = listen(lfd,8);
    if(ret == -1) {
        perror("listen");
        exit(-1);
    }

    // 用epoll_create()創(chuàng)建一個(gè)epoll實(shí)例
    int epfd = epoll_create(100);
    
    // 將監(jiān)聽(tīng)的文件描述符相關(guān)的檢測(cè)信息添加到epoll實(shí)例中
    struct epoll_event epev;
    epev.events = EPOLLIN;
    epev.data.fd = lfd;
    epoll_ctl(epfd,EPOLL_CTL_ADD,lfd,&epev);

    // 創(chuàng)建一個(gè)數(shù)組,用于存儲(chǔ)epoll_wait()返回的文件描述符
    struct epoll_event epevs[1024];
    while (1) {
        ret = epoll_wait(epfd,epevs,1024,-1);
        if(ret == -1) {
            perror("epoll_wait");
            exit(-1);
        }
        printf("ret = %d\n",ret);
        for(int i = 0;i < ret;i++) {
            int curfd = epevs[i].data.fd;
            if(curfd == lfd) {
                // 監(jiān)聽(tīng)的文件描述符有數(shù)據(jù)到達(dá),有客戶端連接
                struct sockaddr_in caddr;
                int len = sizeof(caddr);
                int cfd = accept(lfd,(struct sockaddr*)&caddr,&len);

                // epev.events = EPOLLIN | EPOLLOUT;
                epev.events = EPOLLIN;
                epev.data.fd = cfd;
                epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&epev);

            } else {
                // if(epevs[i].events & EPOLLOUT) {
                //     continue;
                // }
                // 有數(shù)據(jù)到達(dá),需要通信
                char buf[1024] = {0};
                int len = read(curfd,buf,sizeof(buf));
                if (len == -1) {
                    perror("read");
                    exit(-1);
                } else if(len == 0) {
                    printf("client closed...\n");
                    epoll_ctl(epfd,EPOLL_CTL_DEL,curfd,NULL);
                    close(curfd);
                } else if(len > 0) {
                    printf("recv buf = %s\n",buf);
                    write(curfd,buf,strlen(buf) + 1);
                }
            }
        }
    }
    close(lfd);
    close(epfd);
    return 0;
}

client.c

#include <stdio.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

int main(int argc,char* argv[]) {
    int fd = socket(AF_INET,SOCK_STREAM,0);
    if(fd == -1) {
        perror("socket");
        return -1;
    }

    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(9999);
    inet_pton(AF_INET,"127.0.0.1",&saddr.sin_addr.s_addr);

    // 連接服務(wù)器
    int ret = connect(fd,(struct sockaddr*)&saddr,sizeof(saddr));

    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)斷開(kāi)連接...\n");
            break;
        }
        // sleep(1);
        usleep(1000);
    }
    
    close(fd);
    return 0;
}

2.epoll 的兩種工作模式?

Epoll 的工作模式:
	LT 模式 (水平觸發(fā))
		假設(shè)委托內(nèi)核檢測(cè)讀事件 -> 檢測(cè)fd的讀緩沖區(qū)
			讀緩沖區(qū)有數(shù)據(jù) - > epoll檢測(cè)到了會(huì)給用戶通知
				a.用戶不讀數(shù)據(jù),數(shù)據(jù)一直在緩沖區(qū),epoll 會(huì)一直通知
				b.用戶只讀了一部分?jǐn)?shù)據(jù),epoll會(huì)通知
				c.緩沖區(qū)的數(shù)據(jù)讀完了,不通知
	
	LT(level - triggered)是缺省的工作方式,并且同時(shí)支持 block 和 no-block socket。在這
	種做法中,內(nèi)核告訴你一個(gè)文件描述符是否就緒了,然后你可以對(duì)這個(gè)就緒的 fd 進(jìn)行 IO 操
	作。如果你不作任何操作,內(nèi)核還是會(huì)繼續(xù)通知你的。

	ET 模式(邊沿觸發(fā))
		假設(shè)委托內(nèi)核檢測(cè)讀事件 -> 檢測(cè)fd的讀緩沖區(qū)
			讀緩沖區(qū)有數(shù)據(jù) - > epoll檢測(cè)到了會(huì)給用戶通知
				a.用戶不讀數(shù)據(jù),數(shù)據(jù)一直在緩沖區(qū)中,epoll下次檢測(cè)的時(shí)候就不通知了
				b.用戶只讀了一部分?jǐn)?shù)據(jù),epoll不通知
				c.緩沖區(qū)的數(shù)據(jù)讀完了,不通知

	ET(edge - triggered)是高速工作方式,只支持 no-block socket。在這種模式下,當(dāng)描述
	符從未就緒變?yōu)榫途w時(shí),內(nèi)核通過(guò)epoll告訴你。然后它會(huì)假設(shè)你知道文件描述符已經(jīng)就緒,
	并且不會(huì)再為那個(gè)文件描述符發(fā)送更多的就緒通知,直到你做了某些操作導(dǎo)致那個(gè)文件描述
	符不再為就緒狀態(tài)了。但是請(qǐng)注意,如果一直不對(duì)這個(gè) fd 作 IO 操作(從而導(dǎo)致它再次變成
	未就緒),內(nèi)核不會(huì)發(fā)送更多的通知(only once)。
	
	ET 模式在很大程度上減少了 epoll 事件被重復(fù)觸發(fā)的次數(shù),因此效率要比 LT 模式高。epoll
	工作在 ET 模式的時(shí)候,必須使用非阻塞套接口,以避免由于一個(gè)文件句柄的阻塞讀/阻塞寫(xiě)
	操作把處理多個(gè)文件描述符的任務(wù)餓死。


綜上所述:epoll的邊沿模式下 epoll_wait檢測(cè)到文件描述符有新事件才會(huì)通知,
如果不是新的事情就不通知,通知的次數(shù)比水平模式少,效率比水平模式高。

【注意】 ET模式需要配合循環(huán)+非阻塞

(1)LT 模式

epoll_lt.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/epoll.h>

int main() {
    
    // 創(chuàng)建socket
    int lfd = socket(AF_INET,SOCK_STREAM,0);
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(9999);
    saddr.sin_addr.s_addr = INADDR_ANY;

    // 綁定
    int ret = bind(lfd,(struct sockaddr*)&saddr,sizeof(saddr));
    if(ret == -1) {
        perror("bind");
        exit(-1);
    }

    // 監(jiān)聽(tīng)
    ret = listen(lfd,8);
    if(ret == -1) {
        perror("listen");
        exit(-1);
    }

    // 用epoll_create()創(chuàng)建一個(gè)epoll實(shí)例
    int epfd = epoll_create(100);
    
    // 將監(jiān)聽(tīng)的文件描述符相關(guān)的檢測(cè)信息添加到epoll實(shí)例中
    struct epoll_event epev;
    epev.events = EPOLLIN;
    epev.data.fd = lfd;
    epoll_ctl(epfd,EPOLL_CTL_ADD,lfd,&epev);

    // 創(chuàng)建一個(gè)數(shù)組,用于存儲(chǔ)epoll_wait()返回的文件描述符
    struct epoll_event epevs[1024];
    while (1) {
        ret = epoll_wait(epfd,epevs,1024,-1);
        if(ret == -1) {
            perror("epoll_wait");
            exit(-1);
        }
        printf("ret = %d\n",ret);
        for(int i = 0;i < ret;i++) {
            int curfd = epevs[i].data.fd;
            if(curfd == lfd) {
                // 監(jiān)聽(tīng)的文件描述符有數(shù)據(jù)到達(dá),有客戶端連接
                struct sockaddr_in caddr;
                int len = sizeof(caddr);
                int cfd = accept(lfd,(struct sockaddr*)&caddr,&len);

                // epev.events = EPOLLIN | EPOLLOUT;
                epev.events = EPOLLIN;
                epev.data.fd = cfd;
                epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&epev);

            } else {
                // if(epevs[i].events & EPOLLOUT) {
                //     continue;
                // }
                // 有數(shù)據(jù)到達(dá),需要通信
                char buf[5] = {0};
                int len = read(curfd,buf,sizeof(buf));
                if (len == -1) {
                    perror("read");
                    exit(-1);
                } else if(len == 0) {
                    printf("client closed...\n");
                    epoll_ctl(epfd,EPOLL_CTL_DEL,curfd,NULL);
                    close(curfd);
                } else if(len > 0) {
                    printf("recv buf = %s\n",buf);
                    write(curfd,buf,strlen(buf) + 1);
                }
            }
        }
    }
    close(lfd);
    close(epfd);
    return 0;
}
(2)ET 模式
>> epoll在邊沿模式下非阻塞接收數(shù)據(jù)
    循環(huán)接收數(shù)據(jù)的處理方式:對(duì)于每次接收的buffer多小都不重要了,只不過(guò)我們需要多接收幾次數(shù)據(jù)。
    效率相對(duì)來(lái)說(shuō)低一些;如果說(shuō)buffer稍微大一點(diǎn),接收數(shù)據(jù)的次數(shù)就少一些,效率相對(duì)來(lái)說(shuō)高一些;
    可以把recv寫(xiě)到一個(gè)while循環(huán)里,通過(guò)while循環(huán),每次讀取5個(gè)字節(jié),直到把客戶端發(fā)過(guò)來(lái)的數(shù)據(jù)全部都讀到本地。
    【思考】這種方式的弊端在哪里?
    【思考】進(jìn)行套接字通信時(shí)阻塞的還是非阻塞的?

    【回答】很顯然默認(rèn)情況下進(jìn)行套接字通信,這個(gè)處理流程是阻塞的。如果是阻塞的,
    當(dāng)這個(gè)服務(wù)器端循環(huán)接收客戶端發(fā)過(guò)來(lái)的數(shù)據(jù),假設(shè)客戶端發(fā)來(lái)了100個(gè)字節(jié)的數(shù)據(jù),
    在服務(wù)端接收了20次,就把客戶端發(fā)過(guò)來(lái)的數(shù)據(jù)全部讀到本地了,但是在做第21次讀
    數(shù)據(jù)的時(shí)候,這個(gè)recv它還能讀到數(shù)據(jù)嗎?
    沒(méi)有了,也就是說(shuō)這個(gè)文件描述符對(duì)應(yīng)的讀緩沖區(qū)里邊是空的。如果說(shuō)這個(gè)文件描述符
    對(duì)應(yīng)的讀緩沖區(qū)里邊是空的。這個(gè)recv再去接收數(shù)據(jù)的話,服務(wù)器端的線程或者服務(wù)器
    端的進(jìn)程它就阻塞了。如果這個(gè)線程/進(jìn)程阻塞了,就不能干別的事情了。如果說(shuō)寫(xiě)的
    這個(gè)程序里邊就是單線程或者單進(jìn)程的程序,在這里阻塞了,就不能夠去做其他的事情
    了,整個(gè)程序就停止在這里了。

    【問(wèn)題】如何讓while循環(huán)中的break起作用?
        修改文件描述符為非阻塞,而不是修改read/recv函數(shù),因?yàn)檫@函數(shù)時(shí)基于文件描述符
        去進(jìn)行數(shù)據(jù)的接收操作,所以說(shuō)需要修改一下這個(gè)文件描述符的屬性,把這個(gè)文件描述
        符的默認(rèn)阻塞屬性修改為非阻塞屬性。再次調(diào)用recv/read函數(shù)的時(shí)候,它們也就不會(huì)阻塞了

    【思考】如何把這個(gè)文件描述符修改為非阻塞屬性?
        解決阻塞問(wèn)題,需要將套接字默認(rèn)的阻塞行為修改為非阻塞,需要使用fcntl()函數(shù)進(jìn)行處理

        // 設(shè)置完成之后,讀寫(xiě)都變成了非阻塞模式
        int flag = fcntl(cfd,F_GETFL);
        flag |= O_NOBLOCK;
        fcntl(cfd,F_SETFL,flag);  
>>什么時(shí)候使用EWOULDBLOCK?
    如果對(duì)于一個(gè)非阻塞socket,如果使用epoll邊緣模式去檢測(cè)數(shù)據(jù)是否可讀,觸發(fā)可讀
    事件,一定要一次性把socket上的數(shù)據(jù)收取干凈才行,也就是一定要循環(huán)調(diào)用recv函數(shù)
    直到recv出錯(cuò),錯(cuò)誤碼是EWOULDBLOCK,這個(gè)錯(cuò)誤碼表示的就是沒(méi)有數(shù)據(jù)可讀了,
    這個(gè)時(shí)候才能退出循環(huán),退出循環(huán)之后才能去處理可讀事件。
    如果使用水平模式,則不用,你可以根據(jù)業(yè)務(wù)一次性收取固定的字節(jié)數(shù),或者
    收完為止。
struct epoll_event {
    uint32_t events; /* Epoll events */
    epoll_data_t data; /* User data variable */
};

常見(jiàn)的Epoll檢測(cè)事件:
    - EPOLLIN
    - EPOLLOUT
    - EPOLLERR
    - EPOLLET
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <errno.h>

int main() {
    
    // 創(chuàng)建socket
    int lfd = socket(AF_INET,SOCK_STREAM,0);
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(9999);
    saddr.sin_addr.s_addr = INADDR_ANY;

    // 綁定
    int ret = bind(lfd,(struct sockaddr*)&saddr,sizeof(saddr));
    if(ret == -1) {
        perror("bind");
        exit(-1);
    }

    // 監(jiān)聽(tīng)
    ret = listen(lfd,8);
    if(ret == -1) {
        perror("listen");
        exit(-1);
    }

    // 用epoll_create()創(chuàng)建一個(gè)epoll實(shí)例
    int epfd = epoll_create(100);
    
    // 將監(jiān)聽(tīng)的文件描述符相關(guān)的檢測(cè)信息添加到epoll實(shí)例中
    struct epoll_event epev;
    epev.events = EPOLLIN;
    epev.data.fd = lfd;
    epoll_ctl(epfd,EPOLL_CTL_ADD,lfd,&epev);

    // 創(chuàng)建一個(gè)數(shù)組,用于存儲(chǔ)epoll_wait()返回的文件描述符
    struct epoll_event epevs[1024];
    while (1) {
        ret = epoll_wait(epfd,epevs,1024,-1);
        if(ret == -1) {
            perror("epoll_wait");
            exit(-1);
        }
        printf("ret = %d\n",ret);
        for(int i = 0;i < ret;i++) {
            int curfd = epevs[i].data.fd;
            if(curfd == lfd) {
                // 監(jiān)聽(tīng)的文件描述符有數(shù)據(jù)到達(dá),有客戶端連接
                struct sockaddr_in caddr;
                int len = sizeof(caddr);
                int cfd = accept(lfd,(struct sockaddr*)&caddr,&len);
                
                // 設(shè)置cfd屬性非阻塞
                int flag = fcntl(cfd,F_GETFL);
                flag |= O_NONBLOCK; 
                fcntl(cfd,F_SETFL,flag);

                epev.events = EPOLLIN | EPOLLET;// 設(shè)置邊沿觸發(fā)
                epev.data.fd = cfd;
                epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&epev);

            } else {
                if(epevs[i].events & EPOLLOUT) {
                    continue;
                }
                
                // 循環(huán)讀取出所有的數(shù)據(jù)
                char buf[5];
                int len = 0;
                while ((len = read(curfd,buf,sizeof(buf))) > 0) {
                    // 打印數(shù)據(jù)
                    // printf("recv data : %s\n",buf);
                    write(STDOUT_FILENO,buf,len);
                    write(curfd,buf,len);
                }

                if(len == 0) {
                    printf("client closed...\n");
                }else if(len == -1) {
                    if(errno == EAGAIN) {
                        printf("data over......\n");
                    } else {
                        perror("read");
                        exit(-1);
                    }
                }
            }
        }
    }
    close(lfd);
    close(epfd);
    return 0;
}

client.c

#include <stdio.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

int main(int argc,char* argv[]) {
    int fd = socket(AF_INET,SOCK_STREAM,0);
    if(fd == -1) {
        perror("socket");
        return -1;
    }

    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(9999);
    inet_pton(AF_INET,"127.0.0.1",&saddr.sin_addr.s_addr);

    // 連接服務(wù)器
    int ret = connect(fd,(struct sockaddr*)&saddr,sizeof(saddr));

    if(ret == -1) {
        perror("connect");
        return -1;
    }

    int num = 0;
    while (1) {
        char sendBuf[1024] = {0};
        // sprintf(sendBuf,"send data %d",num++);
        fgets(sendBuf,sizeof(sendBuf),stdin);
        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)斷開(kāi)連接...\n");
            break;
        }
        // sleep(1);
        // usleep(1000);
    }
    
    close(fd);
    return 0;
}

推薦和參考文章:

IO多路轉(zhuǎn)接(復(fù)用)之epoll | 愛(ài)編程的大丙 (subingwen.cn)https://subingwen.cn/linux/epoll/

網(wǎng)絡(luò)通信基礎(chǔ)重難點(diǎn)解析 12 :Linux epoll 模型-騰訊云開(kāi)發(fā)者社區(qū)-騰訊云 (tencent.com)https://cloud.tencent.com/developer/article/1419519

IO多路復(fù)用之select、poll、epoll之間的區(qū)別總結(jié)_io多路復(fù)用select,poll,epoll的區(qū)別_linux大本營(yíng)的博客-CSDN博客https://blog.csdn.net/qq_40989769/article/details/128647476文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-675685.html

到了這里,關(guān)于epoll() 多路復(fù)用 和 兩種工作模式的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • epoll多路復(fù)用_并發(fā)服務(wù)器

    應(yīng)用程序: 驅(qū)動(dòng)程序:

    2024年02月15日
    瀏覽(20)
  • 深入理解網(wǎng)絡(luò) I/O 多路復(fù)用:Epoll

    深入理解網(wǎng)絡(luò) I/O 多路復(fù)用:Epoll

    ?? 嗨,您好 ?? 我是 vnjohn,在互聯(lián)網(wǎng)企業(yè)擔(dān)任 Java 開(kāi)發(fā),CSDN 優(yōu)質(zhì)創(chuàng)作者 ?? 推薦專欄:Spring、MySQL、Nacos、Java,后續(xù)其他專欄會(huì)持續(xù)優(yōu)化更新迭代 ??文章所在專欄:網(wǎng)絡(luò) I/O ?? 我當(dāng)前正在學(xué)習(xí)微服務(wù)領(lǐng)域、云原生領(lǐng)域、消息中間件等架構(gòu)、原理知識(shí) ?? 向我詢問(wèn)任何您想

    2024年02月05日
    瀏覽(25)
  • IO多路復(fù)用之select/poll/epoll

    IO多路復(fù)用之select/poll/epoll

    掌握select編程模型,能夠?qū)崿F(xiàn)select版本的TCP服務(wù)器. 掌握poll編程模型,能夠?qū)崿F(xiàn)poll版本的TCP服務(wù)器. 掌握epoll的編程模型,能夠?qū)崿F(xiàn)epoll版本的TCP服務(wù)器. epoll的LT模式和ET模式. 理解select和epoll的優(yōu)缺點(diǎn)對(duì)比. 提示:以下是本篇文章正文內(nèi)容,下面案例可供參考 多路轉(zhuǎn)接天然的是讓我

    2023年04月09日
    瀏覽(21)
  • 驅(qū)動(dòng)開(kāi)發(fā),IO多路復(fù)用實(shí)現(xiàn)過(guò)程,epoll方式

    驅(qū)動(dòng)開(kāi)發(fā),IO多路復(fù)用實(shí)現(xiàn)過(guò)程,epoll方式

    被稱為當(dāng)前時(shí)代最好用的io多路復(fù)用方式; 核心操作:一棵樹(shù)(紅黑樹(shù))、一張表(內(nèi)核鏈表)以及三個(gè)接口; ?思想:(fd代表文件描述符) ????????epoll要把檢測(cè)的事件fd掛載到內(nèi)核空間紅黑樹(shù)上,遍歷紅黑樹(shù),調(diào)用每個(gè)fd對(duì)應(yīng)的操作方法,找到發(fā)生事件的fd,如果沒(méi)有發(fā)

    2024年02月07日
    瀏覽(31)
  • 網(wǎng)絡(luò)編程 IO多路復(fù)用 [epoll版] (TCP網(wǎng)絡(luò)聊天室)

    網(wǎng)絡(luò)編程 IO多路復(fù)用 [epoll版] (TCP網(wǎng)絡(luò)聊天室)

    //head.h? ? ? ? ? ? 頭文件 //TcpGrpSer.c? ? ?服務(wù)器端 //TcpGrpUsr.c? ? ?客戶端 通過(guò)IO多路復(fù)用實(shí)現(xiàn)服務(wù)器在單進(jìn)程單線程下可以與多個(gè)客戶端交互 ?API epoll函數(shù) ?head.h TcpGrpSer.c TcpGrpUsr.c ?

    2024年02月11日
    瀏覽(25)
  • Linux多路IO復(fù)用技術(shù)——epoll詳解與一對(duì)多服務(wù)器實(shí)現(xiàn)

    Linux多路IO復(fù)用技術(shù)——epoll詳解與一對(duì)多服務(wù)器實(shí)現(xiàn)

    本文詳細(xì)介紹了Linux中epoll模型的優(yōu)化原理和使用方法,以及如何利用epoll模型實(shí)現(xiàn)簡(jiǎn)易的一對(duì)多服務(wù)器。通過(guò)對(duì)epoll模型的優(yōu)化和相關(guān)接口的解釋,幫助讀者理解epoll模型的工作原理和優(yōu)缺點(diǎn),同時(shí)附帶代碼實(shí)現(xiàn)和圖解說(shuō)明。

    2024年02月05日
    瀏覽(27)
  • 02-Linux-IO多路復(fù)用之select、poll和epoll詳解

    02-Linux-IO多路復(fù)用之select、poll和epoll詳解

    前言: 在linux系統(tǒng)中,實(shí)際上所有的 I/O 設(shè)備都被抽象為了文件這個(gè)概念,一切皆文件,磁盤(pán)、網(wǎng)絡(luò)數(shù)據(jù)、終端,甚至進(jìn)程間通信工具管道 pipe 等都被當(dāng)做文件對(duì)待。 在了解多路復(fù)用 select、poll、epoll 實(shí)現(xiàn)之前,我們先簡(jiǎn)單回憶復(fù)習(xí)以下兩個(gè)概念: 一、什么是多路復(fù)用: 多路

    2024年02月10日
    瀏覽(26)
  • 【TCP服務(wù)器的演變過(guò)程】使用IO多路復(fù)用器epoll實(shí)現(xiàn)TCP服務(wù)器

    【TCP服務(wù)器的演變過(guò)程】使用IO多路復(fù)用器epoll實(shí)現(xiàn)TCP服務(wù)器

    手把手教你從0開(kāi)始編寫(xiě)TCP服務(wù)器程序,體驗(yàn)開(kāi)局一塊磚,大廈全靠壘。 為了避免篇幅過(guò)長(zhǎng)使讀者感到乏味,對(duì)【TCP服務(wù)器的開(kāi)發(fā)】進(jìn)行分階段實(shí)現(xiàn),一步步進(jìn)行優(yōu)化升級(jí)。 本節(jié),在上一章節(jié)的基礎(chǔ)上,將IO多路復(fù)用機(jī)制select改為更高效的IO多路復(fù)用機(jī)制epoll,使用epoll管理每

    2024年01月17日
    瀏覽(17)
  • 【高并發(fā)網(wǎng)絡(luò)通信架構(gòu)】引入IO多路復(fù)用(select,poll,epoll)實(shí)現(xiàn)高并發(fā)tcp服務(wù)端

    【高并發(fā)網(wǎng)絡(luò)通信架構(gòu)】引入IO多路復(fù)用(select,poll,epoll)實(shí)現(xiàn)高并發(fā)tcp服務(wù)端

    目錄 一,往期文章 二,基本概念 IO多路復(fù)用 select 模型 poll 模型 epoll 模型 select,poll,epoll 三者對(duì)比 三,函數(shù)清單 1.select 方法 2.fd_set 結(jié)構(gòu)體 3.poll 方法 4.struct pollfd 結(jié)構(gòu)體 5.epoll_create 方法 6.epoll_ctl 方法 7.epoll_wait 方法 8.struct epoll_event 結(jié)構(gòu)體 四,代碼實(shí)現(xiàn) select 操作流程 s

    2024年02月12日
    瀏覽(33)
  • 【高并發(fā)網(wǎng)絡(luò)通信架構(gòu)】3.引入IO多路復(fù)用(select,poll,epoll)實(shí)現(xiàn)高并發(fā)tcp服務(wù)端

    【高并發(fā)網(wǎng)絡(luò)通信架構(gòu)】3.引入IO多路復(fù)用(select,poll,epoll)實(shí)現(xiàn)高并發(fā)tcp服務(wù)端

    目錄 一,往期文章 二,基本概念 IO多路復(fù)用 select 模型 poll 模型 epoll 模型 select,poll,epoll 三者對(duì)比 三,函數(shù)清單 1.select 方法 2.fd_set 結(jié)構(gòu)體 3.poll 方法 4.struct pollfd 結(jié)構(gòu)體 5.epoll_create 方法 6.epoll_ctl 方法 7.epoll_wait 方法 8.struct epoll_event 結(jié)構(gòu)體 四,代碼實(shí)現(xiàn) select 操作流程 s

    2024年02月14日
    瀏覽(26)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包