[cpp] view plain
copy ??
1、關(guān)于字節(jié)排序? ? 網(wǎng)際協(xié)議采用大端字節(jié)序,來傳輸多字節(jié)整數(shù)。? ? 系統(tǒng)提供了轉(zhuǎn)換的宏定義,如果主機(jī)與網(wǎng)際協(xié)議相同,則宏定義為空。
2、客戶端? ? socket -> connect(阻塞,三次握手)-> rcv
3、服務(wù)器端? ? socket -> bind -> listen -> accept(阻塞,三次握手)-> send4、函數(shù)介紹? ??
? ? ?a..socket? ? ? ??
? ? ? ? ? 1)函數(shù)原型 int socket(int family, int type, int protocol)? ? ? ??
? ? ? ? ? 2)參數(shù): ? ?? ? ? ? ? ? family: 協(xié)議族AF_INET,IPv4協(xié)議 ...? ? ? ? ? ? type : type 套接字類型SOCK_STREAM 字節(jié)流套接字? ? ? ? ? ? protocol: IPPROCO_TCP IPPROCO_UDP ? ? ? ? ??? ? ? ? ??IPPROCO_SCTP? ? ? ?
?? ? ? ? ?3)返回值? ? ? ? ? ? 成功:返回套接字符? ? ? ? ? ? 錯誤:返回INVALID_SOCKET(-1)? ? ? ??
? ? ? ? ?4)示例
[cpp] view plain
copy #include?<netinet/in.h>??#include?<sys/types.h>??#include?<sys/socket.h>??int?main()??{??????int?socketfd;??????struct?sockaddr_in?servaddr;????????if((socketfd?=?socket(AF_INET,?SOCK_STREAM,?0))?==?-1)??????{??????????return?-1;??????}??}??
? ? ? ??
? ? b..connect
? ? ? ? 1)函數(shù)原型 int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen)
? ? ? ? 2)參數(shù): ? ?
? ? ? ? ? ? sockfd: socket 函數(shù)返回的套接字描述符
? ? ? ? ? ? servaddr : 服務(wù)器的IP和端口
? ? ? ? ? ? addrlen: 長度(sizeof(servaddr))
? ? ? ? 3)返回值
? ? ? ? ? ? 成功:0
? ? ? ? ? ? 錯誤:返回INVALID_SOCKET(-1)
? ? ? ? 4)示例
[cpp] view plain
copy #include?<stdio.h>??#include?<string.h>??#include?<netinet/in.h>??#include?<sys/types.h>??#include?<sys/socket.h>????int?main()??{??????int?socketfd;??????struct?sockaddr_in?servaddr;????????if((socketfd?=?socket(AF_INET,?SOCK_STREAM,?0))?==?-1)??????{??????????printf("socket?error\n");??????????return?-1;??????}????????bzero(&servaddr,?sizeof(servaddr));????????servaddr.sin_addr.s_addr?=?inet_addr("192.168.0.218");??????servaddr.sin_family?=?AF_INET;??????servaddr.sin_port?=?htons(55000);????????if(connect(socketfd,?(struct?sockaddr*)?&servaddr,?sizeof(servaddr))?<?0)??????{??????????printf("connect?error\n");??????}????????return?0;??}??
? ? c..bind
? ? ? ? 1)函數(shù)原型 int bind(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen)
? ? ? ? 2)參數(shù): ? ?
? ? ? ? ? ? sockfd: socket 函數(shù)返回的套接字描述符
? ? ? ? ? ? servaddr : 服務(wù)器的IP和端口
? ? ? ? ? ? addrlen: 長度(sizeof(servaddr))
? ? ? ? 3)返回值
? ? ? ? ? ? 成功:0
? ? ? ? ? ? 錯誤:返回INVALID_SOCKET(-1)
? ? ? ? ? ??
? ? d..listen
? ? ? ? 1)函數(shù)原型 int listen(int sockfd, int backlog)
? ? ? ? 2)參數(shù): ? ?
? ? ? ? ? ? sockfd: socket 函數(shù)返回的套接字描述符
? ? ? ? ? ? backlog : 內(nèi)核中套接字排隊的最大個數(shù)
? ? ? ? 3)返回值
? ? ? ? ? ? 成功:0
? ? ? ? ? ? 錯誤:返回INVALID_SOCKET
? ? ? ? ? ? ? ??
? ? e..accept
? ? ? ? 1)函數(shù)原型 int accept(int sockfd, const struct sockaddr *servaddr, socklen_t *addrlen)
? ? ? ? 2)參數(shù): ? ?
? ? ? ? ? ? sockfd: socket 函數(shù)返回的套接字描述符
? ? ? ? 3)返回值
? ? ? ? ? ? servaddr : 客戶進(jìn)程的IP和端口(可設(shè)為null)
? ? ? ? ? ? addrlen: 長度(sizeof(servaddr))(可設(shè)為null)
? ? ? ? ? ? 成功:從監(jiān)聽套接字返回已連接套接字
? ? ? ? ? ? 錯誤:
? ? ? ? ? ? 如果對客戶信息不感興趣,后兩個參數(shù)可以置空。
? ? ? ? 4)示例
[cpp] view plain
copy #include?<stdio.h>??#include?<string.h>??#include?<netinet/in.h>??#include?<sys/types.h>??#include?<sys/socket.h>????int?main()??{??????int?count?=?0;??????int?listenfd,?socketfd;??????int?nread;??????struct?sockaddr_in?servaddr;??????struct?timeval?timeoutval;??????char?readbuf[256];????????printf("accept?started\n");??????????????if((listenfd?=?socket(AF_INET,?SOCK_STREAM,?0))?==?-1)??????{??????????printf("socket?error\n");??????????return?-1;??????}????????bzero(&servaddr,?sizeof(servaddr));??????servaddr.sin_addr.s_addr?=?htonl(INADDR_ANY);??????servaddr.sin_family?=?AF_INET;??????servaddr.sin_port?=?htons(59000);??????????????if(bind(listenfd,?(struct?sockaddr*)&servaddr,?sizeof(servaddr))?<?0)??????{??????????printf("bind?error\n");????????????????}??????????????listen(listenfd,?5);??????????????socketfd?=?accept(listenfd,?NULL,?NULL);????????????while(1)??????{??????????printf("start?receive?%d...\n",?count++);??????????memset(readbuf,?sizeof(readbuf),?0);????????????nread?=?recv(socketfd,?readbuf,?10,?0);??????????if(nread>0)??????????{??????????????readbuf[10]?=?'\0';??????????????printf("receiveed?%s,?nread?=?%d\n\n",?readbuf,?nread);??????????}??????}????????return?0;??}??
? ? /**************************************************************
? ? 從 I/O 事件分派機(jī)制來看,使用 select()是不合適的,因為它所支持的并發(fā)連接數(shù)有限(通
? ? 常在 1024 個以內(nèi))。如果考慮性能,poll()也是不合適的,盡管它可以支持的較高的 TCP 并發(fā)
? ? 數(shù),但是由于其采用“輪詢”機(jī)制,當(dāng)并發(fā)數(shù)較高時,其運(yùn)行效率相當(dāng)?shù)?并可能存在 I/O 事
? ? 件分派不均,導(dǎo)致部分 TCP 連接上的 I/O 出現(xiàn)“饑餓”現(xiàn)象。而如果使用 epoll 或 AIO,則沒
? ? 有上述問題(早期 Linux 內(nèi)核的 AIO 技術(shù)實現(xiàn)是通過在內(nèi)核中為每個 I/O 請求創(chuàng)建一個線程來
? ? 實現(xiàn)的,這種實現(xiàn)機(jī)制在高并發(fā) TCP 連接的情形下使用其實也有嚴(yán)重的性能問題。但在最新的
? ? Linux 內(nèi)核中,AIO 的實現(xiàn)已經(jīng)得到改進(jìn))。
? ? 支持一個進(jìn)程打開大數(shù)目的 socket 描述符(FD)select 最不能忍受的是一個進(jìn)程所打開的
? ? FD 是有一定限制的,由 FD_SETSIZE 設(shè)置,默認(rèn)值是 2048。對于那些需要支持的上萬連接數(shù)目
? ? 的 IM 服務(wù)器來說顯然太少了。
? ? 這時候你一是可以選擇修改這個宏然后重新編譯內(nèi)核,不過資料
? ? 也同時指出這樣會帶來網(wǎng)絡(luò)效率的下降,二是可以選擇多進(jìn)程的解決方案(傳統(tǒng)的 Apache 方
? ? 案),不過雖然 linux 上面創(chuàng)建進(jìn)程的代價比較小,但仍舊是不可忽視的,加上進(jìn)程間數(shù)據(jù)同步
? ? 遠(yuǎn)比不上線程間同步的高效,所以也不是一種完美的方案。不過 epoll 則沒有這個限制,它所
? ? 支持的 FD 上限是最大可以打開文件的數(shù)目,這個數(shù)字一般遠(yuǎn)大于 2048,舉個例子,在 1GB 內(nèi)存
? ? 的機(jī)器上大約是 10 萬左右,具體數(shù)目可以 cat /proc/sys/fs/file-max 察看,一般來說這個數(shù)
? ? 目和系統(tǒng)內(nèi)存關(guān)系很大。
? ? ******************************************************************/ ? ? ? ?
5. select函數(shù)
? ? 1)函數(shù)原型 int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);
? ? 2)參數(shù): ? ?
? ? ? ? sockfd: socket 函數(shù)返回的套接字描述符
? ? ? ? readfds : 讀描述符集合
? ? ? ? writefds: 寫描述符集合
? ? ? ? errorfds: 錯誤描述符集合
? ? ? ? timeout: ?超時
? ? 3)返回值
? ? ? ? 成功:返回值 0:無 >0:描述符就緒的總位數(shù)
? ? ? ? 錯誤:返回INVALID_SOCKET(-1)
? ? 4)包含頭文件: include <sys/select.h> include <sys/time.h>
? ? 5)示例
[cpp] view plain
copy ????#include?"select.h"??#include?<stdio.h>??#include?<stdlib.h>??#include?<sys/socket.h>??#include?<sys/select.h>??#include?<sys/time.h>??#include?<netinet/in.h>????typedef?struct?_CLIENT{??????int?fd;??????struct?sockaddr_in?addr;???}?CLIENT;????#define?MYPORT?59000??????#define?BACKLOG?5??????CLIENT?client[BACKLOG];??????int?currentClient?=?0;???????#define?REVLEN?10??char?recvBuf[REVLEN];????void?showClient();????int?main()??{??????int?i,?ret,?sinSize;??????int?recvLen?=?0;??????fd_set?readfds,?writefds;??????int?sockListen,?sockSvr,?sockMax;??????struct?timeval?timeout;??????struct?sockaddr_in?server_addr;??????struct?sockaddr_in?client_addr;????????for(i=0;?i<BACKLOG;?i++)??????{??????????client[i].fd?=?-1;??????}??????????????if((sockListen=socket(AF_INET,?SOCK_STREAM,?0))?<?0)??????{??????????printf("socket?error\n");??????????return?-1;??????}????????bzero(&server_addr,?sizeof(server_addr));??????server_addr.sin_family??=??AF_INET;??????server_addr.sin_port?=?htons(MYPORT);??????server_addr.sin_addr.s_addr??=??htonl(INADDR_ANY);???????????????if(bind(sockListen,?(struct?sockaddr*)&server_addr,?sizeof(server_addr))?<?0)??????{??????????printf("bind?error\n");??????????return?-1;??????}??????????????if(listen(sockListen,?5)?<?0)??????{??????????printf("listen?error\n");??????????return?-1;??????}????????for(i=0;?i<BACKLOG;?i++)??????{??????????client[i].fd?=?-1;??????}??????????????while(1)??????{??????????FD_ZERO(&readfds);??????????FD_SET(sockListen,?&readfds);??????????sockMax?=?sockListen;??????????????????????????for(i=0;?i<BACKLOG;?i++)??????????{??????????????if(client[i].fd?>0)??????????????{??????????????????FD_SET(client[i].fd,?&readfds);??????????????????if(sockMax<client[i].fd)???????????????????????sockMax?=?client[i].fd;??????????????}??????????}????????????????????timeout.tv_sec=3;??????????????????????????timeout.tv_usec=0;????????????????????ret?=?select((int)sockMax+1,?&readfds,?NULL,?NULL,?&timeout);??????????if(ret?<?0)??????????{??????????????printf("select?error\n");??????????????break;??????????}??????????else?if(ret?==?0)??????????{??????????????printf("timeout?...\n");??????????????continue;??????????}??????????printf("test111\n");??????????????????????????for(i=0;?i<BACKLOG;?i++)??????????{??????????????if(client[i].fd>0?&&?FD_ISSET(client[i].fd,?&readfds))??????????????{??????????????????if(recvLen?!=?REVLEN)??????????????????{??????????????????????while(1)??????????????????????{????????????????????????????????????????????????????ret?=?recv(client[i].fd,?(char?*)recvBuf+recvLen,?REVLEN-recvLen,?0);??????????????????????????if(ret?==?0)??????????????????????????{??????????????????????????????client[i].fd?=?-1;??????????????????????????????recvLen?=?0;??????????????????????????????break;??????????????????????????}??????????????????????????else?if(ret?<?0)??????????????????????????{??????????????????????????????client[i].fd?=?-1;??????????????????????????????recvLen?=?0;??????????????????????????????break;??????????????????????????}????????????????????????????????????????????????????recvLen?=?recvLen+ret;??????????????????????????if(recvLen<REVLEN)??????????????????????????{??????????????????????????????continue;??????????????????????????}??????????????????????????else??????????????????????????{????????????????????????????????????????????????????????????printf("%s,?buf?=?%s\n",?inet_ntoa(client[i].addr.sin_addr)?,?recvBuf);??????????????????????????????????????????????????????????????????????????????????????????recvLen?=?0;??????????????????????????????break;??????????????????????????}??????????????????????}??????????????????}??????????????}??????????}??????????????????????????if(FD_ISSET(sockListen,?&readfds))??????????{??????????????printf("isset\n");??????????????sockSvr?=?accept(sockListen,?NULL,?NULL);????????????????????????if(sockSvr?==?-1)??????????????{??????????????????printf("accpet?error\n");??????????????}??????????????else??????????????{??????????????????currentClient++;??????????????}????????????????????????for(i=0;?i<BACKLOG;?i++)??????????????{??????????????????if(client[i].fd?<?0)??????????????????{??????????????????????client[i].fd?=?sockSvr;??????????????????????client[i].addr?=?client_addr;??????????????????????printf("You?got?a?connection?from?%s?\n",inet_ntoa(client[i].addr.sin_addr)?);??????????????????????break;??????????????????}??????????????}????????????????????????}??????}????????printf("test\n");??????return?0;??}??????void?showClient()??{??????int?i;??????printf("client?count?=?%d\n",?currentClient);????????for(i=0;?i<BACKLOG;?i++)??????{??????????printf("[%d]?=?%d",?i,?client[i].fd);??????}??????printf("\n");??}??
6. poll函數(shù)
? ? 1)函數(shù)原型 int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);
? ? 2)參數(shù): ? ?
? ? ? ? sockfd: socket 函數(shù)返回的套接字描述符
? ? ? ? readfds : 讀描述符集合
? ? ? ? writefds: 寫描述符集合
? ? ? ? errorfds: 錯誤描述符集合
? ? ? ? timeout: ?超時
? ? 3)返回值
? ? ? ? 成功:返回值 0:無 >0:描述符就緒的總位數(shù)
? ? ? ? 錯誤:返回INVALID_SOCKET(-1)
? ? 4)包含頭文件: include <sys/select.h> include <sys/time.h>
? ? 5) 示例
[cpp] view plain
copy ????#include?"select.h"??#include?<stdio.h>??#include?<stdlib.h>??#include?<string.h>??#include?<sys/socket.h>??#include?<poll.h>??#include?<sys/time.h>??#include?<netinet/in.h>????typedef?struct?_CLIENT{??????int?fd;??????struct?sockaddr_in?addr;???}?CLIENT;????#define?MYPORT?59000??????#define?BACKLOG?5??????int?currentClient?=?0;???????#define?REVLEN?10??char?recvBuf[REVLEN];????#define?OPEN_MAX?1024????int?main()??{??????int?i,?ret,?sinSize;??????int?recvLen?=?0;??????fd_set?readfds,?writefds;??????int?sockListen,?sockSvr,?sockMax;??????int?timeout;??????struct?sockaddr_in?server_addr;??????struct?sockaddr_in?client_addr;????????struct?pollfd?clientfd[OPEN_MAX];????????????????if((sockListen=socket(AF_INET,?SOCK_STREAM,?0))?<?0)??????{??????????printf("socket?error\n");??????????return?-1;??????}????????bzero(&server_addr,?sizeof(server_addr));??????server_addr.sin_family??=??AF_INET;??????server_addr.sin_port?=?htons(MYPORT);??????server_addr.sin_addr.s_addr??=??htonl(INADDR_ANY);???????????????if(bind(sockListen,?(struct?sockaddr*)&server_addr,?sizeof(server_addr))?<?0)??????{??????????printf("bind?error\n");??????????return?-1;??????}??????????????if(listen(sockListen,?5)?<?0)??????{??????????printf("listen?error\n");??????????return?-1;??????}????????????????clientfd[0].fd?=?sockListen;??????clientfd[0].events?=?POLLIN;???????sockMax?=?0;??????for(i=1;?i<OPEN_MAX;?i++)??????{??????????clientfd[i].fd?=?-1;??????}??????????????while(1)??????{??????????timeout=3000;????????????????????????????????????ret?=?poll(clientfd,?sockMax+1,?timeout);????????????????if(ret?<?0)??????????{??????????????printf("select?error\n");??????????????break;??????????}??????????else?if(ret?==?0)??????????{??????????????printf("timeout?...\n");??????????????continue;??????????}????????????????if?(clientfd[0].revents?&?POLLIN)??????????{??????????????sockSvr?=?accept(sockListen,?NULL,?NULL);????????????????????????if(sockSvr?==?-1)??????????????{??????????????????printf("accpet?error\n");??????????????}??????????????else??????????????{??????????????????currentClient++;??????????????}????????????????????????for(i=0;?i<OPEN_MAX;?i++)??????????????{??????????????????if(clientfd[i].fd<0)??????????????????{??????????????????????clientfd[i].fd?=?sockSvr;??????????????????????break;??????????????????}??????????????}??????????????if(i==OPEN_MAX)??????????????{??????????????????printf("too?many?connects\n");??????????????????return?-1;??????????????}??????????????clientfd[i].events?=?POLLIN;??????????????if(i>sockMax)??????????????????sockMax?=?i;??????????}??????????????????????????for(i=1;?i<=sockMax;?i++)??????????{??????????????if(clientfd[i].fd?<?0)??????????????????continue;????????????????????????if?(clientfd[i].revents?&?(POLLIN?|?POLLERR))??????????????{??????????????????if(recvLen?!=?REVLEN)??????????????????{??????????????????????while(1)??????????????????????{????????????????????????????????????????????????????ret?=?recv(clientfd[i].fd,?(char?*)recvBuf+recvLen,?REVLEN-recvLen,?0);??????????????????????????if(ret?==?0)??????????????????????????{??????????????????????????????clientfd[i].fd?=?-1;??????????????????????????????recvLen?=?0;??????????????????????????????break;??????????????????????????}??????????????????????????else?if(ret?<?0)??????????????????????????{??????????????????????????????clientfd[i].fd?=?-1;??????????????????????????????recvLen?=?0;??????????????????????????????break;??????????????????????????}????????????????????????????????????????????????????recvLen?=?recvLen+ret;??????????????????????????if(recvLen<REVLEN)??????????????????????????{??????????????????????????????continue;??????????????????????????}??????????????????????????else??????????????????????????{????????????????????????????????????????????????????????????printf("buf?=?%s\n",??recvBuf);??????????????????????????????????????????????????????????????????????????????????????????recvLen?=?0;??????????????????????????????break;??????????????????????????}??????????????????????}??????????????????}??????????????}??????????}??????}????????return?0;??}??
6. epoll函數(shù)
? ? ? ? 2. 常用模型的缺點(diǎn)
? ? ? ? 如果不擺出來其他模型的缺點(diǎn),怎么能對比出 Epoll 的優(yōu)點(diǎn)呢。
? ? ? ? 2.1 PPC/TPC 模型
? ? ? ? 這兩種模型思想類似,就是讓每一個到來的連接一邊自己做事去,別再來煩我 。只是 PPC 是為它開了一個進(jìn)程,而 TPC 開了一個線程。可是別煩我是有代價的,它要時間和空間啊,連接多了之后,那么多的進(jìn)程 / 線程切換,這開銷就上來了;因此這類模型能接受的最大連接數(shù)都不會高,一般在幾百個左右。
? ? ? ? 2.2 select 模型
? ? ? ? 1. 最大并發(fā)數(shù)限制,因為一個進(jìn)程所打開的 FD (文件描述符)是有限制的,由 FD_SETSIZE 設(shè)置,默認(rèn)值是 1024/2048 ,因此 Select 模型的最大并發(fā)數(shù)就被相應(yīng)限制了。自己改改這個 FD_SETSIZE ?想法雖好,可是先看看下面吧 …
? ? ? ? 2. 效率問題, select 每次調(diào)用都會線性掃描全部的 FD 集合,這樣效率就會呈現(xiàn)線性下降,把 FD_SETSIZE 改大的后果就是,大家都慢慢來,什么?都超時了??!!
? ? ? ? 3. 內(nèi)核 / 用戶空間 內(nèi)存拷貝問題,如何讓內(nèi)核把 FD 消息通知給用戶空間呢?在這個問題上 select 采取了內(nèi)存拷貝方法。
? ? ? ? 2.3 poll 模型
? ? ? ? 基本上效率和 select 是相同的, select 缺點(diǎn)的 2 和 3 它都沒有改掉。
? ? ? ? 3. Epoll 的提升
? ? ? ? 把其他模型逐個批判了一下,再來看看 Epoll 的改進(jìn)之處吧,其實把 select 的缺點(diǎn)反過來那就是 Epoll 的優(yōu)點(diǎn)了。
? ? ? ? 3.1. Epoll 沒有最大并發(fā)連接的限制,上限是最大可以打開文件的數(shù)目,這個數(shù)字一般遠(yuǎn)大于 2048, 一般來說這個數(shù)目和系統(tǒng)內(nèi)存關(guān)系很大 ,具體數(shù)目可以 cat /proc/sys/fs/file-max 察看。
? ? ? ? 3.2. 效率提升, Epoll 最大的優(yōu)點(diǎn)就在于它只管你“活躍”的連接 ,而跟連接總數(shù)無關(guān),因此在實際的網(wǎng)絡(luò)環(huán)境中, Epoll 的效率就會遠(yuǎn)遠(yuǎn)高于 select 和 poll 。
? ? ? ? 3.3. 內(nèi)存拷貝, Epoll 在這點(diǎn)上使用了“共享內(nèi)存 ”,這個內(nèi)存拷貝也省略了。
? ? ? ? ?
? ? ? ? 4. Epoll 為什么高效
? ? ? ? Epoll 的高效和其數(shù)據(jù)結(jié)構(gòu)的設(shè)計是密不可分的,這個下面就會提到。
? ? ? ? 首先回憶一下 select 模型,當(dāng)有 I/O 事件到來時, select 通知應(yīng)用程序有事件到了快去處理,而應(yīng)用程序必須輪詢所有的 FD 集合,測試每個 FD 是否有事件發(fā)生,并處理事件;代碼像下面這樣:
? ? ? ? int res = select(maxfd+1, &readfds, NULL, NULL, 120);
? ? ? ? if (res > 0)
? ? ? ? {
? ? ? ? ? ? for (int i = 0; i < MAX_CONNECTION; i++)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? if (FD_ISSET(allConnection[i], &readfds))
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? handleEvent(allConnection[i]);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? // if(res == 0) handle timeout, res < 0 handle error
? ? ? ? ?
? ? ? ? Epoll 不僅會告訴應(yīng)用程序有I/0 事件到來,還會告訴應(yīng)用程序相關(guān)的信息,這些信息是應(yīng)用程序填充的,因此根據(jù)這些信息應(yīng)用程序就能直接定位到事件,而不必遍歷整個FD 集合。
? ? ? ? int res = epoll_wait(epfd, events, 20, 120);
? ? ? ? for (int i = 0; i < res;i++)
? ? ? ? {
? ? ? ? ? ? handleEvent(events[n]);
? ? ? ? }
? ? ? ? 5. Epoll 關(guān)鍵數(shù)據(jù)結(jié)構(gòu)
? ? ? ? 前面提到 Epoll 速度快和其數(shù)據(jù)結(jié)構(gòu)密不可分,其關(guān)鍵數(shù)據(jù)結(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;
? ? ? ??
? ? ? ? 可見 epoll_data 是一個 union 結(jié)構(gòu)體 , 借助于它應(yīng)用程序可以保存很多類型的信息 :fd 、指針等等。有了它,應(yīng)用程序就可以直接定位目標(biāo)了。
? ? ? ? 6. 使用 Epoll
? ? ? ? 既然 Epoll 相比 select 這么好,那么用起來如何呢?會不會很繁瑣啊 … 先看看下面的三個函數(shù)吧,就知道 Epoll 的易用了。
? ? ? ? ?
? ? ? ? int epoll_create(int size);
? ? ? ? 生成一個 Epoll 專用的文件描述符,其實是申請一個內(nèi)核空間,用來存放你想關(guān)注的 socket fd 上是否發(fā)生以及發(fā)生了什么事件。 size 就是你在這個 Epoll fd 上能關(guān)注的最大 socket fd 數(shù),大小自定,只要內(nèi)存足夠。
? ? ? ??
? ? ? ? int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event );
? ? ? ? 控制某個 Epoll 文件描述符上的事件:注冊、修改、刪除。其中參數(shù) epfd 是 epoll_create() 創(chuàng)建 Epoll 專用的文件描述符。相對于 select 模型中的 FD_SET 和 FD_CLR 宏。
? ? ? ? op:EPOLL_CTL_ADD
? ? ? ? ? ? ? ? ?Register the target file descriptor fd on the epoll instance?
? ? ? ? ? ? ? EPOLL_CTL_MOD
? ? ? ? ? ? ? ? ? Change the event event associated with the target file descriptor fd.
? ? ? ? ? ? ? EPOLL_CTL_DEL
? ? ? ? ? ? ? ? ? ?Remove ?(deregister) ?the ?target ?file descriptor fd from the epoll instance
? ? ? ??
? ? ? ? int epoll_wait(int epfd,struct epoll_event * events,int maxevents,int timeout);
? ? ? ? 等待 I/O 事件的發(fā)生;參數(shù)說明:
? ? ? ? epfd: 由 epoll_create() 生成的 Epoll 專用的文件描述符;
? ? ? ? epoll_event: 用于回傳代處理事件的數(shù)組;
? ? ? ? maxevents: 每次能處理的事件數(shù);
? ? ? ? timeout: 等待 I/O 事件發(fā)生的超時值,單位 ms
? ? ? ? 返回發(fā)生事件數(shù)。
? ? ? ? 相對于 select 模型中的 select 函數(shù)。
? ? ? ??
[cpp] view plain
copy ????#include?"select.h"??#include?<stdio.h>??#include?<stdlib.h>??#include?<string.h>??#include?<sys/socket.h>??#include?<poll.h>??#include?<sys/epoll.h>??#include?<sys/time.h>??#include?<netinet/in.h>????typedef?struct?_CLIENT{??????int?fd;??????struct?sockaddr_in?addr;???}?CLIENT;????#define?MYPORT?59000??????#define?MAX_EVENTS?500??????int?currentClient?=?0;???????#define?REVLEN?10??char?recvBuf[REVLEN];????????int?epollfd;????struct?epoll_event?eventList[MAX_EVENTS];????void?AcceptConn(int?srvfd);??void?RecvData(int?fd);????int?main()??{??????int?i,?ret,?sinSize;??????int?recvLen?=?0;??????fd_set?readfds,?writefds;??????int?sockListen,?sockSvr,?sockMax;??????int?timeout;??????struct?sockaddr_in?server_addr;??????struct?sockaddr_in?client_addr;??????????????????if((sockListen=socket(AF_INET,?SOCK_STREAM,?0))?<?0)??????{??????????printf("socket?error\n");??????????return?-1;??????}????????????bzero(&server_addr,?sizeof(server_addr));??????server_addr.sin_family??=??AF_INET;??????server_addr.sin_port?=?htons(MYPORT);??????server_addr.sin_addr.s_addr??=??htonl(INADDR_ANY);???????????????????if(bind(sockListen,?(struct?sockaddr*)&server_addr,?sizeof(server_addr))?<?0)??????{??????????printf("bind?error\n");??????????return?-1;??????}??????????????????if(listen(sockListen,?5)?<?0)??????{??????????printf("listen?error\n");??????????return?-1;??????}??????????????????epollfd?=?epoll_create(MAX_EVENTS);??????struct?epoll_event?event;??????event.events?=?EPOLLIN|EPOLLET;??????event.data.fd?=?sockListen;??????????????????if(epoll_ctl(epollfd,?EPOLL_CTL_ADD,?sockListen,?&event)?<?0)??????{??????????printf("epoll?add?fail?:?fd?=?%d\n",?sockListen);??????????return?-1;??????}??????????????????while(1)??????{??????????timeout=3000;????????????????????????????????????int?ret?=?epoll_wait(epollfd,?eventList,?MAX_EVENTS,?timeout);????????????????????if(ret?<?0)??????????{??????????????printf("epoll?error\n");??????????????break;??????????}??????????else?if(ret?==?0)??????????{??????????????printf("timeout?...\n");??????????????continue;??????????}??????????????????????????????int?n?=?0;??????????for(n=0;?n<ret;?n++)??????????{????????????????????????????if?((eventList[n].events?&?EPOLLERR)?||??????????????????(eventList[n].events?&?EPOLLHUP)?||??????????????????!(eventList[n].events?&?EPOLLIN))??????????????{????????????????printf?(?"epoll?error\n");????????????????close?(eventList[n].data.fd);????????????????return?-1;??????????????}????????????????????????????if?(eventList[n].data.fd?==?sockListen)??????????????{??????????????????AcceptConn(sockListen);????????????????????????}else{??????????????????RecvData(eventList[n].data.fd);???????????????????????????????????????????????}??????????}??????}????????????close(epollfd);??????close(sockListen);????????????printf("test\n");??????return?0;??}??????????void?AcceptConn(int?srvfd)??{??????struct?sockaddr_in?sin;??????socklen_t?len?=?sizeof(struct?sockaddr_in);??????bzero(&sin,?len);????????int?confd?=?accept(srvfd,?(struct?sockaddr*)&sin,?&len);????????if?(confd?<?0)??????{?????????printf("bad?accept\n");?????????return;??????}else??????{??????????printf("Accept?Connection:?%d",?confd);??????}????????????????????????????struct?epoll_event?event;??????event.data.fd?=?confd;??????event.events?=??EPOLLIN|EPOLLET;??????epoll_ctl(epollfd,?EPOLL_CTL_ADD,?confd,?&event);??}??????void?RecvData(int?fd)??{??????int?ret;??????int?recvLen?=?0;????????????memset(recvBuf,?0,?REVLEN);??????printf("RecvData?function\n");????????????if(recvLen?!=?REVLEN)??????{??????????while(1)??????????{????????????????????????????ret?=?recv(fd,?(char?*)recvBuf+recvLen,?REVLEN-recvLen,?0);??????????????if(ret?==?0)??????????????{??????????????????recvLen?=?0;??????????????????break;??????????????}??????????????else?if(ret?<?0)??????????????{??????????????????recvLen?=?0;??????????????????break;??????????????}????????????????????????????recvLen?=?recvLen+ret;??????????????if(recvLen<REVLEN)??????????????{??????????????????continue;??????????????}??????????????else??????????????{????????????????????????????????????printf("buf?=?%s\n",??recvBuf);??????????????????recvLen?=?0;??????????????????break;??????????????}??????????}??????}????????printf("content?is?%s",?recvBuf);??}?
總結(jié)
以上是生活随笔為你收集整理的socket编程 及select poll epoll示例的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。