日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

Socket编程实践(11) --epoll原理与封装

發(fā)布時(shí)間:2025/3/17 编程问答 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Socket编程实践(11) --epoll原理与封装 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

常用模型的特點(diǎn)

? ? Linux?下設(shè)計(jì)并發(fā)網(wǎng)絡(luò)程序,有典型的Apache模型(Process?Per?Connection,PPC),?TPC(Thread?Per?Connection)模型,以及?select/polL模型和epoll模型。

?

1?、PPC/TPC?模型

? ??這兩種模型思想類似,就是讓每一個(gè)到來的連接一邊自己做事去,別再來煩我(詳見本系列博客).只是?PPC?是為它開了一個(gè)進(jìn)程,而?TPC?開了一個(gè)線程。可是別煩我是有代價(jià)的,它要時(shí)間和空間啊,連接多了之后,那么多的進(jìn)程/線程切換,這開銷就上來了;因此這類模型能接受的最大連接數(shù)都不會(huì)高,一般在幾百個(gè)左右

2?、select?模型

? ??1)?最大并發(fā)數(shù)限制,因?yàn)橐粋€(gè)進(jìn)程所打開的?FD?(文件描述符)是有限制的,由?FD_SETSIZE?設(shè)置,默認(rèn)值是?1024,因此?Select?模型的最大并發(fā)數(shù)就被相應(yīng)限制了。自己改改這個(gè)?FD_SETSIZE??想法雖好,可是先看看下面吧?…

? ??2)?效率問題,?select?每次調(diào)用都會(huì)線性掃描全部的?FD?集合,這樣效率就會(huì)呈現(xiàn)線性下降,把?FD_SETSIZE?改大的后果就是,大家都慢慢來,什么?都超時(shí)了??!!

? ??3)?內(nèi)核/用戶空間內(nèi)存拷貝問題,如何讓內(nèi)核把?FD?消息通知給用戶空間呢?在這個(gè)問題上?select?采取了內(nèi)存拷貝方法。

3、?poll?模型

? ??基本上效率和?select?是相同的,?select?缺點(diǎn)的?2?和?3?它都沒有改掉。

?

Epoll?的提升

? ??1.?Epoll?沒有最大并發(fā)連接的限制,上限是最大可以打開文件的數(shù)目,這個(gè)數(shù)字一般遠(yuǎn)大于?2048,?一般來說這個(gè)數(shù)目和系統(tǒng)內(nèi)存關(guān)系很大?,具體數(shù)目可以?cat?/proc/sys/fs/file-max[599534]?察看。

? ??2.?效率提升,?Epoll最大的優(yōu)點(diǎn)就在于它只管你“活躍”的連接?,而跟連接總數(shù)無關(guān),因此在實(shí)際的網(wǎng)絡(luò)環(huán)境中,?Epoll的效率就會(huì)遠(yuǎn)遠(yuǎn)高于?select?和?poll?。

? ??3.?內(nèi)存拷貝,?Epoll?在這點(diǎn)上使用了“共享內(nèi)存(詳見本系列其他博客)”,這個(gè)內(nèi)存拷貝也省略了。


epoll的使用

epoll的接口非常簡(jiǎn)單,一共就3/4個(gè)函數(shù):

int epoll_create(int size); int epoll_create1(int flags); int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

? ?1.?對(duì)于epoll_create1?的flag參數(shù):?可以設(shè)置為0?或EPOLL_CLOEXEC,為0時(shí)函數(shù)表現(xiàn)與epoll_create一致,?EPOLL_CLOEXEC標(biāo)志與open?時(shí)的O_CLOEXEC?標(biāo)志類似,即進(jìn)程被替換時(shí)會(huì)關(guān)閉打開的文件描述符(需要注意的是,epoll_create與epoll_create1當(dāng)創(chuàng)建好epoll句柄后,它就是會(huì)占用一個(gè)fd值,在linux下如果查看/proc/<pid>/fd/,是能夠看到這個(gè)fd的,所以在使用完epoll后,必須調(diào)用close()關(guān)閉,否則可能導(dǎo)致fd被耗盡)。

? ?2.?對(duì)于epoll_ctl,?op參數(shù)表示動(dòng)作,用三個(gè)宏來表示:

EPOLL_CTL_ADD

注冊(cè)新的fd到epfd中

EPOLL_CTL_DEL

從epfd中刪除一個(gè)fd

EPOLL_CTL_MOD

修改已經(jīng)注冊(cè)的fd的監(jiān)聽事件

? ?3.?對(duì)于epoll_wait:

? ?? ?events:結(jié)構(gòu)體指針,?一般是一個(gè)數(shù)組

? ?? ?maxevents:事件的最大個(gè)數(shù),?或者說是數(shù)組的大小

? ?? ?timeout:超時(shí)時(shí)間,?含義與poll的timeout參數(shù)相同,設(shè)為-1表示永不超時(shí);

? ?4.?epoll_event結(jié)構(gòu)體

struct epoll_event {uint32_t events; /* Epoll events */epoll_data_t data; /* User data variable */ }; typedef union epoll_data {void *ptr;int fd;uint32_t u32;uint64_t u64; } epoll_data_t;

一般data?共同體我們?cè)O(shè)置其成員fd即可,也就是epoll_ctl?函數(shù)的第三個(gè)參數(shù)。

events集合

EPOLLIN

表示對(duì)應(yīng)的文件描述符可以讀(包括對(duì)端SOCKET正常關(guān)閉)

EPOLLOUT

表示對(duì)應(yīng)的文件描述符可以寫

EPOLLPRI

表示對(duì)應(yīng)的文件描述符有緊急的數(shù)據(jù)可讀(這里應(yīng)該表示有帶外數(shù)據(jù)到來)

EPOLLERR

表示對(duì)應(yīng)的文件描述符發(fā)生錯(cuò)誤

EPOLLHUP

表示對(duì)應(yīng)的文件描述符被掛斷

EPOLLET

將EPOLL設(shè)為邊緣觸發(fā)(Edge?Triggered)模式,這是相對(duì)于水平觸發(fā)(Level?Triggered)來說的

EPOLLONESHOT

只監(jiān)聽一次事件,當(dāng)監(jiān)聽完這次事件之后,如果還需要繼續(xù)監(jiān)聽這個(gè)socket的話,需要再次把這個(gè)socket加入到EPOLL隊(duì)列里

/**示例: epoll使用示例注:client端與測(cè)試端與前同, 而且使用相同的測(cè)試端測(cè)試select/poll/epoll, 可以發(fā)現(xiàn)epoll的效率是非常高的**///添加fd到epoll void addFd(int epollfd, int fd, const uint32_t &events = EPOLLIN, bool et = false) {struct epoll_event event;event.events = events;if (et)event.events |= EPOLLET;event.data.fd = fd;if( epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event) == -1 )err_exit("epoll_ctl_add error"); } //從epoll刪除fd void delFd(int epollfd, int fd) {struct epoll_event event;event.data.fd = fd;if( epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, &event) == -1 )err_exit("epoll_ctl_del error"); }int main() {signal(SIGPIPE, sigHandlerForSigPipe);try{TCPServer server(8001);int listenfd = server.getfd();int epollfd = epoll_create1(EPOLL_CLOEXEC);if (epollfd == -1)err_exit("epoll_create1 error");// 將監(jiān)聽套接字注冊(cè)到epolladdFd(epollfd, listenfd, EPOLLIN, true);// 用于保存epoll_wait返回事件數(shù)組std::vector<struct epoll_event> events(16);char buf[BUFSIZ];int count = 0;while (true){// 等待epoll返回int nReady = epoll_wait(epollfd, &*events.begin(), (int)events.size(), -1);if (nReady == -1){if (errno == EINTR)continue;err_exit("epoll_wait error");}if ((size_t)nReady == events.size())events.resize(events.size()*2);for (int i = 0; i < nReady; ++i){// 如果是監(jiān)聽套接字發(fā)送了可讀事件if (events[i].data.fd == listenfd){int connectfd = accept(listenfd, NULL, NULL);if (connectfd == -1)err_exit("accept error");cout << "accept success..." << endl;cout << "count = " << ++count << endl;setUnBlock(connectfd, true);addFd(epollfd, connectfd, EPOLLIN, true);}// 如果是已連接套接字發(fā)生了可讀事件else if (events[i].events & EPOLLIN){int connectfd = events[i].data.fd;if (connectfd < 0)continue;memset(buf, 0, sizeof(buf));int ret = readline(connectfd, buf, sizeof(buf)-1);if (ret == -1)err_exit("read-line error");// 如果對(duì)端關(guān)閉else if (ret == 0){cerr << "client connect closed..." << endl;// 將該套接字同epoll中移除delFd(epollfd, connectfd);close(connectfd);continue;}cout << buf;writen(connectfd, buf, strlen(buf));}}}}catch (const SocketException &e){cerr << e.what() << endl;err_exit("TCPServer error");} }

小結(jié)-epoll與select、poll的區(qū)別

1.相比于select與poll,?epoll最大的好處在于它不會(huì)隨著監(jiān)聽fd數(shù)目的增長(zhǎng)而降低效率。

? ?因?yàn)閮?nèi)核中select/poll的實(shí)現(xiàn)是采用輪詢來處理的,?因此他們檢測(cè)就緒實(shí)踐的算法時(shí)間復(fù)雜度是O(N),?因此,?需要輪詢的fd數(shù)目越多,?自然耗時(shí)越多,?他們的性能呈線性甚至指數(shù)的方式下降。

? ?而epoll的實(shí)現(xiàn)是基于事件回調(diào)的,如果fd有期望的事件發(fā)生就通過回調(diào)函數(shù)將其加入epoll就緒隊(duì)列中,也就是說它只關(guān)心“活躍”的fd,與fd數(shù)目無關(guān)?其算法時(shí)間復(fù)雜度為O(1)。

2.?內(nèi)核空間與用戶空間內(nèi)存拷貝問題,如何讓內(nèi)核把?fd消息通知給用戶空間呢?在這個(gè)問題上select/poll采取了內(nèi)存拷貝方法。而epoll采用了內(nèi)核和用戶空間共享內(nèi)存的方式。

3.?epoll不僅會(huì)告訴應(yīng)用程序有I/0?事件到來,還會(huì)告訴應(yīng)用程序相關(guān)的信息,這些信息是應(yīng)用程序填充的,因此根據(jù)這些信息應(yīng)用程序就能直接定位到事件,而不必遍歷整個(gè)fd集合。而select/poll模型,當(dāng)有?I/O?事件到來時(shí),?select/poll通知應(yīng)用程序有事件到達(dá),而應(yīng)用程序必須輪詢所有的fd集合,測(cè)試每個(gè)fd是否有事件發(fā)生,并處理事件。

4.?當(dāng)活動(dòng)連接比較多的時(shí)候,?epoll_wait的效率就未必比select/poll高了,?因?yàn)檫@時(shí)候?qū)τ趀poll?來說一直在調(diào)用callback?函數(shù),?回調(diào)函數(shù)被觸發(fā)得過于頻繁,?所以epoll_wait適用于連接數(shù)量多,?但活動(dòng)連接少的情況;

?

ET/LT模式

1、EPOLLLT:完全靠Linux-kernel-epoll驅(qū)動(dòng),應(yīng)用程序只需要處理從epoll_wait返回的fds,?這些fds我們認(rèn)為它們處于就緒狀態(tài)。此時(shí)epoll可以認(rèn)為是更快速的poll。

2、EPOLLET:此模式下,系統(tǒng)僅僅通知應(yīng)用程序哪些fds變成了就緒狀態(tài),一旦fd變成就緒狀態(tài),epoll將不再關(guān)注這個(gè)fd的任何狀態(tài)信息(從epoll隊(duì)列移除),?直到應(yīng)用程序通過讀寫操作(非阻塞)觸發(fā)EAGAIN狀態(tài),epoll認(rèn)為這個(gè)fd又變?yōu)榭臻e狀態(tài),那么epoll又重新關(guān)注這個(gè)fd的狀態(tài)變化(重新加入epoll隊(duì)列)。?隨著epoll_wait的返回,隊(duì)列中的fds是在減少的,所以在大并發(fā)的系統(tǒng)中,EPOLLET更有優(yōu)勢(shì),但是對(duì)程序員的要求也更高,因?yàn)橛锌赡軙?huì)出現(xiàn)數(shù)據(jù)讀取不完整的問題,舉例如下:

? ?假設(shè)現(xiàn)在對(duì)方發(fā)送了2k的數(shù)據(jù),而我們先讀取了1k,然后這時(shí)調(diào)用了epoll_wait,如果是邊沿觸發(fā)ET,那么這個(gè)fd變成就緒狀態(tài)就會(huì)從epoll?隊(duì)列移除,則epoll_wait?會(huì)一直阻塞,忽略尚未讀取的1k數(shù)據(jù);?而如果是水平觸發(fā)LT,那么epoll_wait?還會(huì)檢測(cè)到可讀事件而返回,我們可以繼續(xù)讀取剩下的1k?數(shù)據(jù)。

? ?因此總結(jié)來說:?LT模式可能觸發(fā)的次數(shù)更多,?一旦觸發(fā)的次數(shù)多,?也就意味著效率會(huì)下降;?但這樣也不能就說LT模式就比ET模式效率更低,?因?yàn)镋T的使用對(duì)編程人員提出了更高更精細(xì)的要求,?一旦編程人員水平達(dá)不到(比如本人),?那ET模式還不如LT模式;


Epoll-Class封裝

? ?在本部分我們實(shí)現(xiàn)一個(gè)較為好用實(shí)用的Epoll并發(fā)類,?由于實(shí)現(xiàn)代碼與使用方式較簡(jiǎn)單,?因此就不在此贅述了,?下面我還使用了該類實(shí)現(xiàn)了一個(gè)基于Epoll的echo-server,?以演示該類的用法;

? ?由于此處僅為Epoll類庫(kù)的第一個(gè)版本,?因此錯(cuò)誤之處必然會(huì)存在,?如果讀者在閱讀的過程中發(fā)現(xiàn)了該類庫(kù)的BUG,?還望這篇博客的讀者朋友不吝賜教;?而作者也會(huì)不斷的更新該類庫(kù)(主要更新代碼我會(huì)發(fā)布到此處),?以處理新的業(yè)務(wù)需求;


Epoll類設(shè)計(jì)

class Epoll { public:Epoll(int flags = EPOLL_CLOEXEC, int noFile = 1024);~Epoll();void addfd(int fd, uint32_t events = EPOLLIN, bool ETorNot = false);void modfd(int fd, uint32_t events = EPOLLIN, bool ETorNot = false);void delfd(int fd);int wait(int timeout = -1);int getEventOccurfd(int eventIndex) const;uint32_t getEvents(int eventIndex) const;public:bool isValid(){if (m_epollfd == -1)return false;return true;}void close(){if (isValid()){:: close(m_epollfd);m_epollfd = -1;}}private:std::vector<struct epoll_event> events;int m_epollfd;int fdNumber;int nReady; private:struct epoll_event event; };

Epoll類實(shí)現(xiàn)

/** epoll_create **/ Epoll::Epoll(int flags, int noFile) : fdNumber(0), nReady(0) {struct rlimit rlim;rlim.rlim_cur = rlim.rlim_max = noFile;if ( ::setrlimit(RLIMIT_NOFILE, &rlim) == -1 )throw EpollException("setrlimit error");m_epollfd = ::epoll_create1(flags);if (m_epollfd == -1)throw EpollException("epoll_create1 error"); } Epoll::~Epoll() {this -> close(); } /** epoll_ctl **/ void Epoll::addfd(int fd, uint32_t events, bool ETorNot) {bzero(&event, sizeof(event));event.events = events;if (ETorNot)event.events |= EPOLLET;event.data.fd = fd;if( ::epoll_ctl(m_epollfd, EPOLL_CTL_ADD, fd, &event) == -1 )throw EpollException("epoll_ctl_add error");++ fdNumber; } void Epoll::modfd(int fd, uint32_t events, bool ETorNot) {bzero(&event, sizeof(event));event.events = events;if (ETorNot)event.events |= EPOLLET;event.data.fd = fd;if( ::epoll_ctl(m_epollfd, EPOLL_CTL_MOD, fd, &event) == -1 )throw EpollException("epoll_ctl_mod error"); } void Epoll::delfd(int fd) {bzero(&event, sizeof(event));event.data.fd = fd;if( ::epoll_ctl(m_epollfd, EPOLL_CTL_DEL, fd, &event) == -1 )throw EpollException("epoll_ctl_del error");-- fdNumber; } /** epoll_wait **/ int Epoll::wait(int timeout) {events.resize(fdNumber);while (true){nReady = epoll_wait(m_epollfd, &*events.begin(), fdNumber, timeout);if (nReady == 0)throw EpollException("epoll_wait timeout");else if (nReady == -1){if (errno == EINTR)continue;else throw EpollException("epoll_wait error");}elsereturn nReady;}return -1; }int Epoll::getEventOccurfd(int eventIndex) const {if (eventIndex > nReady)throw EpollException("parameter(s) error");return events[eventIndex].data.fd; } uint32_t Epoll::getEvents(int eventIndex) const {if (eventIndex > nReady)throw EpollException("parameter(s) error");return events[eventIndex].events; }

使用Epoll的echoserver(測(cè)試)代碼:

int main() { signal(SIGPIPE, SIG_IGN);/**將下面的這兩個(gè)變量設(shè)置成為放在程序的開頭,只是因?yàn)檫@樣可以使得業(yè)務(wù)處理部分的代碼顯得簡(jiǎn)潔一些,在實(shí)際應(yīng)用(C++)中,沒必要也不推薦這樣使用**/char buf[BUFSIZ];int clientCount = 0;try{TCPServer server(8001);int listenfd = server.getfd();Epoll epoll;// 將監(jiān)聽套接字注冊(cè)到epollepoll.addfd(server.getfd(), EPOLLIN, true);while (true){int nReady = epoll.wait();for (int i = 0; i < nReady; ++i)// 如果是監(jiān)聽套接字發(fā)生了可讀事件if (epoll.getEventOccurfd(i) == listenfd){int connectfd = accept(listenfd, NULL, NULL);if (connectfd == -1)err_exit("accept error");cout << "accept success..." << endl;cout << "clientCount = " << ++ clientCount << endl;setUnBlock(connectfd, true);epoll.addfd(connectfd, EPOLLIN, true);}else if (epoll.getEvents(i) & EPOLLIN){TCPClient *client = new TCPClient(epoll.getEventOccurfd(i));memset(buf, 0, sizeof(buf));if (client->read(buf, sizeof(buf)) == 0){cerr << "client connect closed..." << endl;// 將該套接字從epoll中移除epoll.delfd(client->getfd());delete client;continue;}cout << buf;client->write(buf);}}}catch (const SocketException &e){cerr << e.what() << endl;err_exit("TCPServer error");}catch (const EpollException &e){cerr << e.what() << endl;err_exit("Epoll error");} }

完整源代碼請(qǐng)參照:

http://download.csdn.net/detail/hanqing280441589/8492911

總結(jié)

以上是生活随笔為你收集整理的Socket编程实践(11) --epoll原理与封装的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 这里只有精品免费视频 | 日韩一区二区精品 | 熊出没之冬日乐翻天免费高清观看 | 天堂va欧美va亚洲va老司机 | 日韩欧美一区二区在线 | 男人的天堂网在线 | 欧美日韩中文字幕一区二区 | 性欧美18| xxsm.com | 精品亚洲aⅴ无码一区二区三区 | 日韩国产片 | 欧美成人三级在线 | 正在播放adn156松下纱荣子 | 国产69精品久久久久999小说 | 波多在线播放 | 国产一二区在线观看 | av成人免费在线观看 | 免费看黄视频的网站 | 精品无码久久久久久久久 | 欧美夫妻性生活视频 | 无遮挡av | 捆绑无遮挡打光屁股调教女仆 | 爆乳熟妇一区二区三区 | 91精品国产高清一区二区三密臀 | 久久久精品人妻av一区二区三区 | 久免费一级suv好看的国产 | 国产亚洲欧美日韩精品 | 美女交配| 永久免费视频网站 | 青草视频免费观看 | av影音先锋 | 欧美激情二区三区 | 自拍偷拍亚洲图片 | aa丁香综合激情 | 91成人在线观看喷潮蘑菇 | 一区二区三区免费观看 | www.四虎影视.com| 少妇紧身牛仔裤裤啪啪 | 蜜臀久久99精品久久一区二区 | juliaann欧美二区三区 | 在线精品视频播放 | 亚洲女人被黑人巨大进入 | 涩av | 999久久| 欧美放荡性医生videos | 性免费网站 | 午夜免费视频网站 | 51热门大瓜今日大瓜 | 久久精品一区二区在线观看 | 国产精品国产精品国产专区不卡 | 国产亚洲精品成人无码精品网站 | 男女污污网站 | jizz亚洲女人 | 深夜福利视频导航 | 亚州黄色| 精品国产乱码久久久久久浪潮 | 久久99久 | 台湾佬综合网 | 中文字幕第66页 | 青草av在线 | 亚洲先锋影音 | 亚洲av电影天堂男人的天堂 | 色偷偷资源网 | 视频黄页在线观看 | 亚洲精品国产欧美 | 一级人爱视频 | 五号特工组之偷天换月 | 久热最新视频 | 色七七久久 | 狠狠操狠狠| 丰满白嫩尤物一区二区 | 欧美视频色 | 成人午夜网 | 精品久久久中文字幕人妻 | 中文字幕免费在线观看视频 | 精品人妻一区二区三区四区久久 | 韩国精品一区二区 | 亚洲天堂网一区二区 | 国产免费观看久久黄av片 | 18禁男女爽爽爽午夜网站免费 | 伊人96| 美女天天操 | 亚洲熟女综合一区二区三区 | 懂色av蜜臀av粉嫩av分享吧最新章节 | 日本欧美视频 | 久久久久成人网站 | 国产成人精品免高潮费视频 | 亚洲a网站 | 国产绿帽一区二区三区 | 在线观看亚洲 | 操人网| 99成人在线观看 | 国产精品入口免费 | 国产一二精品 | 射进来av影视 | 高清在线一区二区三区 | 寻找身体恐怖电影免费播放 | 国产又大又黄的视频 | 国产精品卡一 |