日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

linux select 与 阻塞( blocking ) 及非阻塞 (non blocking)实现io多路复用的示例

發(fā)布時(shí)間:2023/12/9 41 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux select 与 阻塞( blocking ) 及非阻塞 (non blocking)实现io多路复用的示例 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

除了自己實(shí)現(xiàn)之外,還有個(gè)c語言寫的基于事件的開源網(wǎng)絡(luò)庫:libevent

http://www.cnblogs.com/Anker/p/3265058.html

?

最簡單的select示例:

#include <stdio.h> #include <sys/time.h> #include <sys/types.h> #include <unistd.h>#define STDIN 0 // file descriptor for standard inputint main(void) {struct timeval tv;fd_set readfds;tv.tv_sec = 2;tv.tv_usec = 500000;FD_ZERO(&readfds);FD_SET(STDIN, &readfds);// don't care about writefds and exceptfds:select(STDIN+1, &readfds, NULL, NULL, &tv);if (FD_ISSET(STDIN, &readfds))printf("A key was pressed!\n");elseprintf("Timed out.\n");return 0; }

?

?

select、poll、epoll之間的區(qū)別總結(jié)[整理]

select,poll,epoll都是IO多路復(fù)用的機(jī)制。I/O多路復(fù)用就通過一種機(jī)制,可以監(jiān)視多個(gè)描述符,一旦某個(gè)描述符就緒(一般是讀就緒或者寫就緒),能夠通知程序進(jìn)行相應(yīng)的讀寫操作。但select,poll,epoll本質(zhì)上都是同步I/O,因?yàn)樗麄兌夹枰谧x寫事件就緒后自己負(fù)責(zé)進(jìn)行讀寫,也就是說這個(gè)讀寫過程是阻塞的,而異步I/O則無需自己負(fù)責(zé)進(jìn)行讀寫,異步I/O的實(shí)現(xiàn)會(huì)負(fù)責(zé)把數(shù)據(jù)從內(nèi)核拷貝到用戶空間。關(guān)于這三種IO多路復(fù)用的用法,前面三篇總結(jié)寫的很清楚,并用服務(wù)器回射echo程序進(jìn)行了測試。連接如下所示:

select:http://www.cnblogs.com/Anker/archive/2013/08/14/3258674.html

poll:http://www.cnblogs.com/Anker/archive/2013/08/15/3261006.html

epoll:http://www.cnblogs.com/Anker/archive/2013/08/17/3263780.html

  今天對(duì)這三種IO多路復(fù)用進(jìn)行對(duì)比,參考網(wǎng)上和書上面的資料,整理如下:

1、select實(shí)現(xiàn)

select的調(diào)用過程如下所示:

(1)使用copy_from_user從用戶空間拷貝fd_set到內(nèi)核空間

(2)注冊(cè)回調(diào)函數(shù)__pollwait

(3)遍歷所有fd,調(diào)用其對(duì)應(yīng)的poll方法(對(duì)于socket,這個(gè)poll方法是sock_poll,sock_poll根據(jù)情況會(huì)調(diào)用到tcp_poll,udp_poll或者datagram_poll)

(4)以tcp_poll為例,其核心實(shí)現(xiàn)就是__pollwait,也就是上面注冊(cè)的回調(diào)函數(shù)。

(5)__pollwait的主要工作就是把current(當(dāng)前進(jìn)程)掛到設(shè)備的等待隊(duì)列中,不同的設(shè)備有不同的等待隊(duì)列,對(duì)于tcp_poll 來說,其等待隊(duì)列是sk->sk_sleep(注意把進(jìn)程掛到等待隊(duì)列中并不代表進(jìn)程已經(jīng)睡眠了)。在設(shè)備收到一條消息(網(wǎng)絡(luò)設(shè)備)或填寫完文件數(shù) 據(jù)(磁盤設(shè)備)后,會(huì)喚醒設(shè)備等待隊(duì)列上睡眠的進(jìn)程,這時(shí)current便被喚醒了。

(6)poll方法返回時(shí)會(huì)返回一個(gè)描述讀寫操作是否就緒的mask掩碼,根據(jù)這個(gè)mask掩碼給fd_set賦值。

(7)如果遍歷完所有的fd,還沒有返回一個(gè)可讀寫的mask掩碼,則會(huì)調(diào)用schedule_timeout是調(diào)用select的進(jìn)程(也就是 current)進(jìn)入睡眠。當(dāng)設(shè)備驅(qū)動(dòng)發(fā)生自身資源可讀寫后,會(huì)喚醒其等待隊(duì)列上睡眠的進(jìn)程。如果超過一定的超時(shí)時(shí)間(schedule_timeout 指定),還是沒人喚醒,則調(diào)用select的進(jìn)程會(huì)重新被喚醒獲得CPU,進(jìn)而重新遍歷fd,判斷有沒有就緒的fd。

(8)把fd_set從內(nèi)核空間拷貝到用戶空間。

總結(jié):

select的幾大缺點(diǎn):

(1)每次調(diào)用select,都需要把fd集合從用戶態(tài)拷貝到內(nèi)核態(tài),這個(gè)開銷在fd很多時(shí)會(huì)很大

(2)同時(shí)每次調(diào)用select都需要在內(nèi)核遍歷傳遞進(jìn)來的所有fd,這個(gè)開銷在fd很多時(shí)也很大

(3)select支持的文件描述符數(shù)量太小了,默認(rèn)是1024

2 poll實(shí)現(xiàn)

  poll的實(shí)現(xiàn)和select非常相似,只是描述fd集合的方式不同,poll使用pollfd結(jié)構(gòu)而不是select的fd_set結(jié)構(gòu),其他的都差不多。

關(guān)于select和poll的實(shí)現(xiàn)分析,可以參考下面幾篇博文:

http://blog.csdn.net/lizhiguo0532/article/details/6568964#comments

http://blog.csdn.net/lizhiguo0532/article/details/6568968

http://blog.csdn.net/lizhiguo0532/article/details/6568969

http://www.ibm.com/developerworks/cn/linux/l-cn-edntwk/index.html?ca=drs-

http://linux.chinaunix.net/techdoc/net/2009/05/03/1109887.shtml

3、epoll

  epoll既然是對(duì)select和poll的改進(jìn),就應(yīng)該能避免上述的三個(gè)缺點(diǎn)。那epoll都是怎么解決的呢?在此之前,我們先看一下 epoll和select和poll的調(diào)用接口上的不同,select和poll都只提供了一個(gè)函數(shù)——select或者poll函數(shù)。而epoll提供 了三個(gè)函數(shù),epoll_create,epoll_ctl和epoll_wait,epoll_create是創(chuàng)建一個(gè)epoll句 柄;epoll_ctl是注冊(cè)要監(jiān)聽的事件類型;epoll_wait則是等待事件的產(chǎn)生。

  對(duì)于第一個(gè)缺點(diǎn),epoll的解決方案在epoll_ctl函數(shù)中。每次注冊(cè)新的事件到epoll句柄中時(shí)(在epoll_ctl中指定 EPOLL_CTL_ADD),會(huì)把所有的fd拷貝進(jìn)內(nèi)核,而不是在epoll_wait的時(shí)候重復(fù)拷貝。epoll保證了每個(gè)fd在整個(gè)過程中只會(huì)拷貝 一次。

  對(duì)于第二個(gè)缺點(diǎn),epoll的解決方案不像select或poll一樣每次都把current輪流加入fd對(duì)應(yīng)的設(shè)備等待隊(duì)列中,而只在 epoll_ctl時(shí)把current掛一遍(這一遍必不可少)并為每個(gè)fd指定一個(gè)回調(diào)函數(shù),當(dāng)設(shè)備就緒,喚醒等待隊(duì)列上的等待者時(shí),就會(huì)調(diào)用這個(gè)回調(diào) 函數(shù),而這個(gè)回調(diào)函數(shù)會(huì)把就緒的fd加入一個(gè)就緒鏈表)。epoll_wait的工作實(shí)際上就是在這個(gè)就緒鏈表中查看有沒有就緒的fd(利用 schedule_timeout()實(shí)現(xiàn)睡一會(huì),判斷一會(huì)的效果,和select實(shí)現(xiàn)中的第7步是類似的)。

  對(duì)于第三個(gè)缺點(diǎn),epoll沒有這個(gè)限制,它所支持的FD上限是最大可以打開文件的數(shù)目,這個(gè)數(shù)字一般遠(yuǎn)大于2048,舉個(gè)例子, 在1GB內(nèi)存的機(jī)器上大約是10萬左右,具體數(shù)目可以cat /proc/sys/fs/file-max察看,一般來說這個(gè)數(shù)目和系統(tǒng)內(nèi)存關(guān)系很大。

總結(jié):

(1)select,poll實(shí)現(xiàn)需要自己不斷輪詢所有fd集合,直到設(shè)備就緒,期間可能要睡眠和喚醒多次交替。而epoll其實(shí)也需要調(diào)用 epoll_wait不斷輪詢就緒鏈表,期間也可能多次睡眠和喚醒交替,但是它是設(shè)備就緒時(shí),調(diào)用回調(diào)函數(shù),把就緒fd放入就緒鏈表中,并喚醒在 epoll_wait中進(jìn)入睡眠的進(jìn)程。雖然都要睡眠和交替,但是select和poll在“醒著”的時(shí)候要遍歷整個(gè)fd集合,而epoll在“醒著”的 時(shí)候只要判斷一下就緒鏈表是否為空就行了,這節(jié)省了大量的CPU時(shí)間。這就是回調(diào)機(jī)制帶來的性能提升。

(2)select,poll每次調(diào)用都要把fd集合從用戶態(tài)往內(nèi)核態(tài)拷貝一次,并且要把current往設(shè)備等待隊(duì)列中掛一次,而epoll只要 一次拷貝,而且把current往等待隊(duì)列上掛也只掛一次(在epoll_wait的開始,注意這里的等待隊(duì)列并不是設(shè)備等待隊(duì)列,只是一個(gè)epoll內(nèi) 部定義的等待隊(duì)列)。這也能節(jié)省不少的開銷。

參考資料:

http://www.cnblogs.com/apprentice89/archive/2013/05/09/3070051.html

http://www.linuxidc.com/Linux/2012-05/59873p3.htm

http://xingyunbaijunwei.blog.163.com/blog/static/76538067201241685556302/

http://blog.csdn.net/kkxgx/article/details/7717125

https://banu.com/blog/2/how-to-use-epoll-a-complete-example-in-c/epoll-example.c

?

IO多路復(fù)用之select總結(jié)

1、基本概念

  IO多路復(fù)用是指內(nèi)核一旦發(fā)現(xiàn)進(jìn)程指定的一個(gè)或者多個(gè)IO條件準(zhǔn)備讀取,它就通知該進(jìn)程。IO多路復(fù)用適用如下場合:

  (1)當(dāng)客戶處理多個(gè)描述字時(shí)(一般是交互式輸入和網(wǎng)絡(luò)套接口),必須使用I/O復(fù)用。

  (2)當(dāng)一個(gè)客戶同時(shí)處理多個(gè)套接口時(shí),而這種情況是可能的,但很少出現(xiàn)。

  (3)如果一個(gè)TCP服務(wù)器既要處理監(jiān)聽套接口,又要處理已連接套接口,一般也要用到I/O復(fù)用。

  (4)如果一個(gè)服務(wù)器即要處理TCP,又要處理UDP,一般要使用I/O復(fù)用。

  (5)如果一個(gè)服務(wù)器要處理多個(gè)服務(wù)或多個(gè)協(xié)議,一般要使用I/O復(fù)用。

  與多進(jìn)程和多線程技術(shù)相比,I/O多路復(fù)用技術(shù)的最大優(yōu)勢是系統(tǒng)開銷小,系統(tǒng)不必創(chuàng)建進(jìn)程/線程,也不必維護(hù)這些進(jìn)程/線程,從而大大減小了系統(tǒng)的開銷。

2、select函數(shù)

  該函數(shù)準(zhǔn)許進(jìn)程指示內(nèi)核等待多個(gè)事件中的任何一個(gè)發(fā)送,并只在有一個(gè)或多個(gè)事件發(fā)生或經(jīng)歷一段指定的時(shí)間后才喚醒。函數(shù)原型如下:

#include <sys/select.h> #include <sys/time.h>int select(int maxfdp1,fd_set *readset,fd_set *writeset,fd_set *exceptset,const struct timeval *timeout)
返回值:就緒描述符的數(shù)目,超時(shí)返回0,出錯(cuò)返回-1

函數(shù)參數(shù)介紹如下:

(1)第一個(gè)參數(shù)maxfdp1指定待測試的描述字個(gè)數(shù),它的值是待測試的最大描述字加1(因此把該參數(shù)命名為maxfdp1),描述字0、1、2...maxfdp1-1均將被測試。

因?yàn)槲募枋龇菑?開始的。

(2)中間的三個(gè)參數(shù)readset、writeset和exceptset指定我們要讓內(nèi)核測試讀、寫和異常條件的描述字。如果對(duì)某一個(gè)的條件不感興趣,就可以把它設(shè)為空指針。struct fd_set可以理解為一個(gè)集合,這個(gè)集合中存放的是文件描述符,可通過以下四個(gè)宏進(jìn)行設(shè)置:

????????? void FD_ZERO(fd_set *fdset);?????????? //清空集合

????????? void FD_SET(int fd, fd_set *fdset);?? //將一個(gè)給定的文件描述符加入集合之中

????????? void FD_CLR(int fd, fd_set *fdset);?? //將一個(gè)給定的文件描述符從集合中刪除

????????? int FD_ISSET(int fd, fd_set *fdset);?? // 檢查集合中指定的文件描述符是否可以讀寫?

(3)timeout告知內(nèi)核等待所指定描述字中的任何一個(gè)就緒可花多少時(shí)間。其timeval結(jié)構(gòu)用于指定這段時(shí)間的秒數(shù)和微秒數(shù)。

???????? struct timeval{

?????????????????? long tv_sec;?? //seconds

?????????????????? long tv_usec;? //microseconds

?????? };

這個(gè)參數(shù)有三種可能:

(1)永遠(yuǎn)等待下去:僅在有一個(gè)描述字準(zhǔn)備好I/O時(shí)才返回。為此,把該參數(shù)設(shè)置為空指針NULL。

(2)等待一段固定時(shí)間:在有一個(gè)描述字準(zhǔn)備好I/O時(shí)返回,但是不超過由該參數(shù)所指向的timeval結(jié)構(gòu)中指定的秒數(shù)和微秒數(shù)。

(3)根本不等待:檢查描述字后立即返回,這稱為輪詢。為此,該參數(shù)必須指向一個(gè)timeval結(jié)構(gòu),而且其中的定時(shí)器值必須為0。

?原理圖:

3、測試程序

  寫一個(gè)TCP回射程序,程序的功能是:客戶端向服務(wù)器發(fā)送信息,服務(wù)器接收并原樣發(fā)送給客戶端,客戶端顯示出接收到的信息。

服務(wù)端程序如下所示:

#include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <netinet/in.h> #include <sys/socket.h> #include <sys/select.h> #include <sys/types.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <assert.h>#define IPADDR "127.0.0.1" #define PORT 8787 #define MAXLINE 1024 #define LISTENQ 5 #define SIZE 10typedef struct server_context_st {int cli_cnt; /*客戶端個(gè)數(shù)*/int clifds[SIZE]; /*客戶端的個(gè)數(shù)*/fd_set allfds; /*句柄集合*/int maxfd; /*句柄最大值*/ } server_context_st;static server_context_st *s_srv_ctx = NULL;/*===========================================================================* ==========================================================================*/ static int create_server_proc(const char* ip,int port) {int fd;struct sockaddr_in servaddr;fd = socket(AF_INET, SOCK_STREAM,0);if (fd == -1) {fprintf(stderr, "create socket fail,erron:%d,reason:%s\n",errno, strerror(errno));return -1;}int yes = 1;if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) { return -1;}int reuse = 1;if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1) {return -1;}bzero(&servaddr,sizeof(servaddr));servaddr.sin_family = AF_INET;inet_pton(AF_INET,ip,&servaddr.sin_addr);servaddr.sin_port = htons(port);if (bind(fd,(struct sockaddr*)&servaddr,sizeof(servaddr)) == -1) {perror("bind error: ");return -1;}listen(fd,LISTENQ);return fd; }static int accept_client_proc(int srvfd) {struct sockaddr_in cliaddr;socklen_t cliaddrlen;cliaddrlen = sizeof(cliaddr);int clifd = -1;printf("accpet clint proc is called.\n");ACCEPT:clifd = accept(srvfd,(struct sockaddr*)&cliaddr,&cliaddrlen);if (clifd == -1) {if (errno == EINTR) {goto ACCEPT;} else {fprintf(stderr, "accept fail,error:%s\n", strerror(errno));return -1;}}fprintf(stdout, "accept a new client: %s:%d\n",inet_ntoa(cliaddr.sin_addr),cliaddr.sin_port);//將新的連接描述符添加到數(shù)組中int i = 0;for (i = 0; i < SIZE; i++) {if (s_srv_ctx->clifds[i] < 0) {s_srv_ctx->clifds[i] = clifd;s_srv_ctx->cli_cnt++;break;}}if (i == SIZE) {fprintf(stderr,"too many clients.\n");return -1;}}static int handle_client_msg(int fd, char *buf) {assert(buf);printf("recv buf is :%s\n", buf);write(fd, buf, strlen(buf) +1);return 0; }static void recv_client_msg(fd_set *readfds) {int i = 0, n = 0;int clifd;char buf[MAXLINE] = {0};for (i = 0;i <= s_srv_ctx->cli_cnt;i++) {clifd = s_srv_ctx->clifds[i];if (clifd < 0) {continue;}if (FD_ISSET(clifd, readfds)) {//接收客戶端發(fā)送的信息n = read(clifd, buf, MAXLINE);if (n <= 0) {FD_CLR(clifd, &s_srv_ctx->allfds);close(clifd);s_srv_ctx->clifds[i] = -1;continue;}handle_client_msg(clifd, buf);}} } static void handle_client_proc(int srvfd) {int clifd = -1;int retval = 0;fd_set *readfds = &s_srv_ctx->allfds;struct timeval tv;int i = 0;while (1) {/*每次調(diào)用select前都要重新設(shè)置文件描述符和時(shí)間,因?yàn)槭录l(fā)生后,文件描述符和時(shí)間都被內(nèi)核修改啦*//*添加監(jiān)聽套接字*/FD_ZERO(readfds);FD_SET(srvfd, readfds);s_srv_ctx->maxfd = srvfd;tv.tv_sec = 30;tv.tv_usec = 0;/*添加客戶端套接字*/for (i = 0; i < s_srv_ctx->cli_cnt; i++) {clifd = s_srv_ctx->clifds[i];FD_SET(clifd, readfds);s_srv_ctx->maxfd = (clifd > s_srv_ctx->maxfd ? clifd : s_srv_ctx->maxfd);}retval = select(s_srv_ctx->maxfd + 1, readfds, NULL, NULL, &tv);if (retval == -1) {fprintf(stderr, "select error:%s.\n", strerror(errno));return;}if (retval == 0) {fprintf(stdout, "select is timeout.\n");continue;}if (FD_ISSET(srvfd, readfds)) {/*監(jiān)聽客戶端請(qǐng)求*/accept_client_proc(srvfd);} else {/*接受處理客戶端消息*/recv_client_msg(readfds);}} }static void server_uninit() {if (s_srv_ctx) {free(s_srv_ctx);s_srv_ctx = NULL;} }static int server_init() {s_srv_ctx = (server_context_st *)malloc(sizeof(server_context_st));if (s_srv_ctx == NULL) {return -1;}memset(s_srv_ctx, 0, sizeof(server_context_st));int i = 0;for (;i < SIZE; i++) {s_srv_ctx->clifds[i] = -1;}return 0; }int main(int argc,char *argv[]) {int srvfd;if (server_init() < 0) {return -1;}srvfd = create_server_proc(IPADDR, PORT);if (srvfd < 0) {fprintf(stderr, "socket create or bind fail.\n");goto err;}handle_client_proc(srvfd);return 0;err:server_uninit();return -1; }

客戶端程序如下:

#include <netinet/in.h> #include <sys/socket.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <sys/select.h> #include <time.h> #include <unistd.h> #include <sys/types.h> #include <errno.h>#define MAXLINE 1024 #define IPADDRESS "127.0.0.1" #define SERV_PORT 8787#define max(a,b) (a > b) ? a : bstatic void handle_recv_msg(int sockfd, char *buf) {printf("client recv msg is:%s\n", buf);sleep(5);write(sockfd, buf, strlen(buf) +1); }static void handle_connection(int sockfd) {char sendline[MAXLINE],recvline[MAXLINE];int maxfdp,stdineof;fd_set readfds;int n;struct timeval tv;int retval = 0;while (1) {FD_ZERO(&readfds);FD_SET(sockfd,&readfds);maxfdp = sockfd;tv.tv_sec = 5;tv.tv_usec = 0;retval = select(maxfdp+1,&readfds,NULL,NULL,&tv);if (retval == -1) {return ;}if (retval == 0) {printf("client timeout.\n");continue;}if (FD_ISSET(sockfd, &readfds)) {n = read(sockfd,recvline,MAXLINE);if (n <= 0) {fprintf(stderr,"client: server is closed.\n");close(sockfd);FD_CLR(sockfd,&readfds);return;}handle_recv_msg(sockfd, recvline);}} }int main(int argc,char *argv[]) {int sockfd;struct sockaddr_in servaddr;sockfd = socket(AF_INET,SOCK_STREAM,0);bzero(&servaddr,sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(SERV_PORT);inet_pton(AF_INET,IPADDRESS,&servaddr.sin_addr);int retval = 0;retval = connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr));if (retval < 0) {fprintf(stderr, "connect fail,error:%s\n", strerror(errno));return -1;}printf("client send to server .\n");write(sockfd, "hello server", 32);handle_connection(sockfd);return 0; }

4、程序結(jié)果

  啟動(dòng)服務(wù)程序,執(zhí)行三個(gè)個(gè)客戶程序進(jìn)行測試,結(jié)果如下圖所示:

參考:

http://konglingchun.is-programmer.com/posts/12146.html

http://blog.163.com/smileface100@126/blog/static/27720874200951024532966/

?

linux select 多路復(fù)用機(jī)制

下面給一個(gè)偽碼說明基本select模型的服務(wù)器模型:

array[slect_len]; nSock=0; array[nSock++]=listen_fd;(之前l(fā)isten port已綁定并listen) maxfd=listen_fd; while(1){ FD_ZERO(&set); foreach (fd in array) { fd大于maxfd,則maxfd=fd FD_SET(fd,&set) } res=select(maxfd+1,&set,0,0,0); if(FD_ISSET(listen_fd,&set)) { newfd=accept(listen_fd); array[nsock++]=newfd; if(--res<=0) continue; } foreach 下標(biāo)1開始 (fd in array) { if(FD_ISSET(fd,&tyle="COLOR: #ff0000">set)) 執(zhí)行讀等相關(guān)操作 如果錯(cuò)誤或者關(guān)閉,則要?jiǎng)h除該fd,將array中相應(yīng)位置和最后一個(gè)元素互換就好,nsock減一 if(--res<=0) continue; } }

檢測鍵盤有無輸入,完整的程序如下:

?

#include<sys/time.h> ?
#include<sys/types.h> ?
#include<unistd.h> ?
#include<string.h> ?
#include<stdlib.h> ?
#include<stdio.h> ?

#define LEN 10

int main() ?
{ ?
??? char buf[LEN]=""; ?
??? fd_set rdfds; ?
??? struct timeval tv; ?
??? int ret; ?
??? FD_ZERO(&rdfds); ?
??? FD_SET(0,&rdfds);?? //文件描述符0表示stdin鍵盤輸入 ?
??? tv.tv_sec = 3; ?
??? tv.tv_usec = 500; ?

??? ret = select(1,&rdfds,NULL,NULL,&tv); ?
??? if(ret<0) ?
??????? printf("\n selcet"); ?
??? else if(ret == 0) ?
??????? printf("\n timeout"); ?
??? else ?
??????? printf("\n ret = %d",ret); ?

??? if(FD_ISSET(1,&rdfds))? //如果有輸入,從stdin中獲取輸入字符 ?
??? { ?
??????? printf("\n reading"); ?
??????? fread(buf, LEN-1, 1, stdin); ?
??? } ?

??? write(1,buf,strlen(buf)); ?
??? printf("\n %d \n",strlen(buf)); ?

??? return 0; ?
} ?
//執(zhí)行結(jié)果ret = 1.

?

利用Select模型,設(shè)計(jì)的web服務(wù)器:

#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #define MYPORT 88960 // the port users will be connecting to #define BACKLOG 10 // how many pending connections queue will hold #define BUF_SIZE 200 int fd_A[BACKLOG]; // accepted connection fd int conn_amount; // current connection amount void showclient() { int i; printf("client amount: %d\n", conn_amount); for (i = 0; i < BACKLOG; i++) { printf("[%d]:%d ", i, fd_A[i]); } printf("\n\n"); } int main(void) { int sock_fd, new_fd; // listen on sock_fd, new connection on new_fd struct sockaddr_in server_addr; // server address information struct sockaddr_in client_addr; // connector's address information socklen_t sin_size; int yes = 1; char buf[BUF_SIZE]; int ret; int i; if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("socket"); exit(1); } if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) { perror("setsockopt"); exit(1); } server_addr.sin_family = AF_INET; // host byte order server_addr.sin_port = htons(MYPORT); // short, network byte order server_addr.sin_addr.s_addr = INADDR_ANY; // automatically fill with my IP memset(server_addr.sin_zero, '\0', sizeof(server_addr.sin_zero)); if (bind(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) { perror("bind"); exit(1); } if (listen(sock_fd, BACKLOG) == -1) { perror("listen"); exit(1); } printf("listen port %d\n", MYPORT); fd_set fdsr; int maxsock; struct timeval tv; conn_amount = 0; sin_size = sizeof(client_addr); maxsock = sock_fd; while (1) { // initialize file descriptor set FD_ZERO(&fdsr); FD_SET(sock_fd, &fdsr); // timeout setting tv.tv_sec = 30; tv.tv_usec = 0; // add active connection to fd set for (i = 0; i < BACKLOG; i++) { if (fd_A[i] != 0) { FD_SET(fd_A[i], &fdsr); } } ret = select(maxsock + 1, &fdsr, NULL, NULL, &tv); if (ret < 0) { perror("select"); break; } else if (ret == 0) { printf("timeout\n"); continue; } // check every fd in the set for (i = 0; i < conn_amount; i++) { if (FD_ISSET(fd_A[i], &fdsr)) { ret = recv(fd_A[i], buf, sizeof(buf), 0); char str[] = "Good,very nice!\n"; send(fd_A[i],str,sizeof(str) + 1, 0); if (ret <= 0) { // client close printf("client[%d] close\n", i); close(fd_A[i]); FD_CLR(fd_A[i], &fdsr); fd_A[i] = 0; } else { // receive data if (ret < BUF_SIZE) memset(&buf[ret], '\0', 1); printf("client[%d] send:%s\n", i, buf); } } } // check whether a new connection comes if (FD_ISSET(sock_fd, &fdsr)) { new_fd = accept(sock_fd, (struct sockaddr *)&client_addr, &sin_size); if (new_fd <= 0) { perror("accept"); continue; } // add to fd queue if (conn_amount < BACKLOG) { fd_A[conn_amount++] = new_fd; printf("new connection client[%d] %s:%d\n", conn_amount, inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); if (new_fd > maxsock) maxsock = new_fd; } else { printf("max connections arrive, exit\n"); send(new_fd, "bye", 4, 0); close(new_fd); break; } } showclient(); } // close other connections for (i = 0; i < BACKLOG; i++) { if (fd_A[i] != 0) { close(fd_A[i]); } } exit(0); }

?

上面的都是 select 和 blocking io的使用示例。

?

下面是select 和 non blocking io的使用示例:

Nonblocking I/O and select()

https://www-01.ibm.com/support/knowledgecenter/ssw_ibm_i_71/rzab6/xnonblock.htm

This sample program illustrates a server application that uses nonblocking and the select() API.

?

Socket flow of events: Server that uses nonblocking I/O and select()

The following calls are used in the example:

  • The socket() API returns a socket descriptor, which represents an endpoint. The statement also identifies that the INET (Internet Protocol) address family with the TCP transport (SOCK_STREAM) is used for this socket.
  • The ioctl() API allows the local address to be reused when the server is restarted before the required wait time expires. In this example, it sets the socket to be nonblocking. All of the sockets for the incoming connections are also nonblocking because they inherit that state from the listening socket.
  • After the socket descriptor is created, the bind() gets a unique name for the socket.
  • The listen() allows the server to accept incoming client connections.
  • The server uses the accept() API to accept an incoming connection request. The accept() API call blocks indefinitely, waiting for the incoming connection to arrive.
  • The select() API allows the process to wait for an event to occur and to wake up the process when the event occurs. In this example, the select() API returns a number that represents the socket descriptors that are ready to be processed.0
    Indicates that the process times out. In this example, the timeout is set for 3 minutes.
    -1
    Indicates that the process has failed.
    1
    Indicates only one descriptor is ready to be processed. In this example, when a 1 is returned, the FD_ISSET and the subsequent socket calls complete only once.
    n
    Indicates that multiple descriptors are waiting to be processed. In this example, when an n is returned, the FD_ISSET and subsequent code loops and completes the requests in the order they are received by the server.
  • The accept() and recv() APIs are completed when the EWOULDBLOCK is returned.
  • The send() API echoes the data back to the client.
  • The close() API closes any open socket descriptors.
  • Note: By using the examples, you agree to the terms of the Code license and disclaimer information. #include <stdio.h> #include <stdlib.h> #include <sys/ioctl.h> #include <sys/socket.h> #include <sys/time.h> #include <netinet/in.h> #include <errno.h>#define SERVER_PORT 12345#define TRUE 1 #define FALSE 0main (int argc, char *argv[]) {int i, len, rc, on = 1;int listen_sd, max_sd, new_sd;int desc_ready, end_server = FALSE;int close_conn;char buffer[80];struct sockaddr_in6 addr;struct timeval timeout;struct fd_set master_set, working_set;/*************************************************************//* Create an AF_INET6 stream socket to receive incoming *//* connections on *//*************************************************************/listen_sd = socket(AF_INET6, SOCK_STREAM, 0);if (listen_sd < 0){perror("socket() failed");exit(-1);}/*************************************************************//* Allow socket descriptor to be reuseable *//*************************************************************/rc = setsockopt(listen_sd, SOL_SOCKET, SO_REUSEADDR,(char *)&on, sizeof(on));if (rc < 0){perror("setsockopt() failed");close(listen_sd);exit(-1);}/*************************************************************//* Set socket to be nonblocking. All of the sockets for *//* the incoming connections will also be nonblocking since *//* they will inherit that state from the listening socket. *//*************************************************************/rc = ioctl(listen_sd, FIONBIO, (char *)&on);if (rc < 0){perror("ioctl() failed");close(listen_sd);exit(-1);}/*************************************************************//* Bind the socket *//*************************************************************/memset(&addr, 0, sizeof(addr));addr.sin6_family = AF_INET6;memcpy(&addr.sin6_addr, &in6addr_any, sizeof(in6addr_any));addr.sin6_port = htons(SERVER_PORT);rc = bind(listen_sd,(struct sockaddr *)&addr, sizeof(addr));if (rc < 0){perror("bind() failed");close(listen_sd);exit(-1);}/*************************************************************//* Set the listen back log *//*************************************************************/rc = listen(listen_sd, 32);if (rc < 0){perror("listen() failed");close(listen_sd);exit(-1);}/*************************************************************//* Initialize the master fd_set *//*************************************************************/FD_ZERO(&master_set);max_sd = listen_sd;FD_SET(listen_sd, &master_set);/*************************************************************//* Initialize the timeval struct to 3 minutes. If no *//* activity after 3 minutes this program will end. *//*************************************************************/timeout.tv_sec = 3 * 60;timeout.tv_usec = 0;/*************************************************************//* Loop waiting for incoming connects or for incoming data *//* on any of the connected sockets. *//*************************************************************/do{/**********************************************************//* Copy the master fd_set over to the working fd_set. *//**********************************************************/memcpy(&working_set, &master_set, sizeof(master_set));/**********************************************************//* Call select() and wait 3 minutes for it to complete. *//**********************************************************/printf("Waiting on select()...\n");rc = select(max_sd + 1, &working_set, NULL, NULL, &timeout);/**********************************************************//* Check to see if the select call failed. *//**********************************************************/if (rc < 0){perror(" select() failed");break;}/**********************************************************//* Check to see if the 3 minute time out expired. *//**********************************************************/if (rc == 0){printf(" select() timed out. End program.\n");break;}/**********************************************************//* One or more descriptors are readable. Need to *//* determine which ones they are. *//**********************************************************/desc_ready = rc;for (i=0; i <= max_sd && desc_ready > 0; ++i){/*******************************************************//* Check to see if this descriptor is ready *//*******************************************************/if (FD_ISSET(i, &working_set)){/****************************************************//* A descriptor was found that was readable - one *//* less has to be looked for. This is being done *//* so that we can stop looking at the working set *//* once we have found all of the descriptors that *//* were ready. *//****************************************************/desc_ready -= 1;/****************************************************//* Check to see if this is the listening socket *//****************************************************/if (i == listen_sd){printf(" Listening socket is readable\n");/*************************************************//* Accept all incoming connections that are *//* queued up on the listening socket before we *//* loop back and call select again. *//*************************************************/do{/**********************************************//* Accept each incoming connection. If *//* accept fails with EWOULDBLOCK, then we *//* have accepted all of them. Any other *//* failure on accept will cause us to end the *//* server. *//**********************************************/new_sd = accept(listen_sd, NULL, NULL);if (new_sd < 0){if (errno != EWOULDBLOCK){perror(" accept() failed");end_server = TRUE;}break;}/**********************************************//* Add the new incoming connection to the *//* master read set *//**********************************************/printf(" New incoming connection - %d\n", new_sd);FD_SET(new_sd, &master_set);if (new_sd > max_sd)max_sd = new_sd;/**********************************************//* Loop back up and accept another incoming *//* connection *//**********************************************/} while (new_sd != -1);}/****************************************************//* This is not the listening socket, therefore an *//* existing connection must be readable *//****************************************************/else{printf(" Descriptor %d is readable\n", i);close_conn = FALSE;/*************************************************//* Receive all incoming data on this socket *//* before we loop back and call select again. *//*************************************************/do{/**********************************************//* Receive data on this connection until the *//* recv fails with EWOULDBLOCK. If any other *//* failure occurs, we will close the *//* connection. *//**********************************************/rc = recv(i, buffer, sizeof(buffer), 0);if (rc < 0){if (errno != EWOULDBLOCK){perror(" recv() failed");close_conn = TRUE;}break;}/**********************************************//* Check to see if the connection has been *//* closed by the client *//**********************************************/if (rc == 0){printf(" Connection closed\n");close_conn = TRUE;break;}/**********************************************//* Data was received *//**********************************************/len = rc;printf(" %d bytes received\n", len);/**********************************************//* Echo the data back to the client *//**********************************************/rc = send(i, buffer, len, 0);if (rc < 0){perror(" send() failed");close_conn = TRUE;break;}} while (TRUE);/*************************************************//* If the close_conn flag was turned on, we need *//* to clean up this active connection. This *//* clean up process includes removing the *//* descriptor from the master set and *//* determining the new maximum descriptor value *//* based on the bits that are still turned on in *//* the master set. *//*************************************************/if (close_conn){close(i);FD_CLR(i, &master_set);if (i == max_sd){while (FD_ISSET(max_sd, &master_set) == FALSE)max_sd -= 1;}}} /* End of existing connection is readable */} /* End of if (FD_ISSET(i, &working_set)) */} /* End of loop through selectable descriptors */} while (end_server == FALSE);/*************************************************************//* Clean up all of the sockets that are open *//*************************************************************/for (i=0; i <= max_sd; ++i){if (FD_ISSET(i, &master_set))close(i);} }

    ?

    EAGAIN=EWOULDBLOCK(BSD風(fēng)格)
    此錯(cuò)誤由在非阻塞套接字上不能立即完成的操作返回,例如,當(dāng)套接字上沒有排隊(duì)數(shù)據(jù)可讀時(shí)調(diào)用了recv()函數(shù)(比如協(xié)議棧接收到了數(shù)據(jù)但拷貝狀態(tài)還未結(jié)束,忙)。此錯(cuò)誤不是嚴(yán)重錯(cuò)誤,相應(yīng)操作應(yīng)該稍后重試。對(duì)于在非阻塞?SOCK_STREAM套接字上調(diào)用connect()函數(shù)來說,報(bào)告EWOULDBLOCK是正常的,因?yàn)榻⒁粋€(gè)連接必須花費(fèi)一些時(shí)間。

    在linux進(jìn)行非阻塞的socket接收數(shù)據(jù)時(shí)經(jīng)常出現(xiàn)Resource temporarily unavailable,errno代碼為11(EAGAIN),這是什么意思?
    這表明你在非阻塞模式下調(diào)用了阻塞操作,在該操作沒有完成就返回這個(gè)錯(cuò)誤,這個(gè)錯(cuò)誤不會(huì)破壞socket的同步,不用管它,下次循環(huán)接著recv就可以。 對(duì)非阻塞socket而言,EAGAIN不是一種錯(cuò)誤。在VxWorks和Windows上,EAGAIN的名字叫做EWOULDBLOCK。

    non-blocking和select結(jié)合使用

    select通過輪詢,監(jiān)視指定file descriptor(包括socket)的變化,知道:哪些ready for reading, 哪些ready for writing,哪些發(fā)生了錯(cuò)誤等。select和non-blocking結(jié)合使用可很好地實(shí)現(xiàn)socket的多client同步通信。

    通過判斷返回的errno了解狀態(tài)。

    accept():

    ??????? 在non-blocking模式下,如果返回值為-1,且errno == EAGAIN或errno == EWOULDBLOCK表示no connections沒有新連接請(qǐng)求;

    recv()/recvfrom():

    ??????? 在non-blocking模式下,如果返回值為-1,且errno == EAGAIN表示沒有可接受的數(shù)據(jù)或很在接受尚未完成;

    send()/sendto():

    ??????? 在non-blocking模式下,如果返回值為-1,且errno == EAGAIN或errno == EWOULDBLOCK表示沒有可發(fā)送數(shù)據(jù)或數(shù)據(jù)發(fā)送正在進(jìn)行沒有完成。

    read/write:

    ??????? 在non-blocking模式下,如果返回-1,且errno == EAGAIN表示沒有可讀寫數(shù)據(jù)或可讀寫正在進(jìn)行尚未完成。

    connect():

    ??????? 在non-bloking模式下,如果返回-1,且errno = EINPROGRESS表示正在連接。

    ?

    ?

    ?

    ?

    ?

    ?

    IO多路復(fù)用之epoll總結(jié)

    http://www.cnblogs.com/Anker/archive/2013/08/17/3263780.html

    總結(jié)

    以上是生活随笔為你收集整理的linux select 与 阻塞( blocking ) 及非阻塞 (non blocking)实现io多路复用的示例的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

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