http://blog.csdn.net/lianghe_work/article/details/46519633
與多線程、多進程相比,I/O復用最大的優勢是系統開銷小,系統不需要建立新的進程或者線程,也不必維護這些線程和進程。
代碼示例:
[csharp]?view plain?copy
- #include?<stdio.h>???
- #include?<unistd.h>??
- #include?<stdlib.h>??
- #include?<errno.h>??
- #include?<string.h>??
- #include?<sys/socket.h>??
- #include?<sys/types.h>??
- #include?<netinet/in.h>??
- #include?<arpa/inet.h>??
- #include?<sys/select.h>??
- ?
- #define?SERV_PORT?8080??
- #define?LIST?20????????????????//服務器最大接受連接??
- #define?MAX_FD?10??????????????//FD_SET支持描述符數量??
- ??
- ??
- int?main(int?argc,?char?*argv[])??
- {??
- ????int?sockfd;??
- ????int?err;??
- ????int?i;??
- ????int?connfd;??
- ????int?fd_all[MAX_FD];?//保存所有描述符,用于select調用后,判斷哪個可讀??
- ??????
- ????//下面兩個備份原因是select調用后,會發生變化,再次調用select前,需要重新賦值??
- ????fd_set?fd_read;????//FD_SET數據備份??
- ????fd_set?fd_select;??//用于select??
- ??
- ????struct?timeval?timeout;?????????//超時時間備份??
- ????struct?timeval?timeout_select;??//用于select??
- ??????
- ????struct?sockaddr_in?serv_addr;???//服務器地址??
- ????struct?sockaddr_in?cli_addr;????//客戶端地址??
- ????socklen_t?serv_len;??
- ????socklen_t?cli_len;??
- ??????
- ????//超時時間設置??
- ????timeout.tv_sec?=?10;??
- ????timeout.tv_usec?=?0;??
- ??????
- ????//創建TCP套接字??
- ????sockfd?=?socket(AF_INET,?SOCK_STREAM,?0);??
- ????if(sockfd?<?0)??
- ????{??
- ????????perror("fail?to?socket");??
- ????????exit(1);??
- ????}??
- ??????
- ????//?配置本地地址??
- ????memset(&serv_addr,?0,?sizeof(serv_addr));??
- ????serv_addr.sin_family?=?AF_INET;?????????//?ipv4??
- ????serv_addr.sin_port?=?htons(SERV_PORT);??//?端口,?8080??
- ????serv_addr.sin_addr.s_addr?=?htonl(INADDR_ANY);?//?ip??
- ??
- ????serv_len?=?sizeof(serv_addr);??
- ??????
- ????//?綁定??
- ????err?=?bind(sockfd,?(struct?sockaddr?*)&serv_addr,?serv_len);??
- ????if(err?<?0)??
- ????{??
- ????????perror("fail?to?bind");??
- ????????exit(1);??
- ????}??
- ??
- ????//?監聽??
- ????err?=?listen(sockfd,?LIST);??
- ????if(err?<?0)??
- ????{??
- ????????perror("fail?to?listen");??
- ????????exit(1);??
- ????}??
- ??????
- ????//初始化fd_all數組??
- ????memset(fd_all,?-1,?sizeof(fd_all));??
- ??
- ????fd_all[0]?=?sockfd;???//第一個為監聽套接字??
- ??????
- ????FD_ZERO(&fd_read);??//?清空??
- ????FD_SET(sockfd,?&fd_read);??//將監聽套接字加入fd_read??
- ??
- ????int?maxfd?=?fd_all[0];??//監聽的最大套接字??
- ??????
- ????while(1){??
- ??????
- ????????//?每次都需要重新賦值,fd_select,timeout_select每次都會變??
- ????????fd_select?=?fd_read;??
- ????????timeout_select?=?timeout;??
- ??????????
- ????????//?檢測監聽套接字是否可讀,沒有可讀,此函數會阻塞??
- ????????//?只要有客戶連接,或斷開連接,select()都會往下執行??
- ????????err?=?select(maxfd+1,?&fd_select,?NULL,?NULL,?NULL);??
- ????????//err?=?select(maxfd+1,?&fd_select,?NULL,?NULL,?(struct?timeval?*)&timeout_select);??
- ????????if(err?<?0)??
- ????????{??
- ????????????????perror("fail?to?select");??
- ????????????????exit(1);??
- ????????}??
- ??
- ????????if(err?==?0){??
- ????????????printf("timeout\n");??
- ????????}??
- ??????????
- ????????//?檢測監聽套接字是否可讀??
- ????????if(?FD_ISSET(sockfd,?&fd_select)?){//可讀,證明有新客戶端連接服務器??
- ??????????????
- ????????????cli_len?=?sizeof(cli_addr);??
- ??????????????
- ????????????//?取出已經完成的連接??
- ????????????connfd?=?accept(sockfd,?(struct?sockaddr?*)&cli_addr,?&cli_len);??
- ????????????if(connfd?<?0)??
- ????????????{??
- ????????????????perror("fail?to?accept");??
- ????????????????exit(1);??
- ????????????}??
- ??????????????
- ????????????//?打印客戶端的?ip?和端口??
- ????????????char?cli_ip[INET_ADDRSTRLEN]?=?{0};??
- ????????????inet_ntop(AF_INET,?&cli_addr.sin_addr,?cli_ip,?INET_ADDRSTRLEN);??
- ????????????printf("----------------------------------------------\n");??
- ????????????printf("client?ip=%s,port=%d\n",?cli_ip,ntohs(cli_addr.sin_port));??
- ??????????????
- ????????????//?將新連接套接字加入?fd_all?及?fd_read??
- ????????????for(i=0;?i?<?MAX_FD;?i++){??
- ????????????????if(fd_all[i]?!=?-1){??
- ????????????????????continue;??
- ????????????????}else{??
- ????????????????????fd_all[i]?=?connfd;??
- ????????????????????printf("client?fd_all[%d]?join\n",?i);??
- ????????????????????break;??
- ????????????????}??
- ????????????}??
- ??????????????
- ????????????FD_SET(connfd,?&fd_read);??
- ??????????????
- ????????????if(maxfd?<?connfd)??
- ????????????{??
- ????????????????maxfd?=?connfd;??//更新maxfd??
- ????????????}??
- ??????????
- ????????}??
- ??????????
- ????????//從1開始查看連接套接字是否可讀,因為上面已經處理過0(sockfd)??
- ????????for(i=1;?i?<?maxfd;?i++){??
- ????????????if(FD_ISSET(fd_all[i],?&fd_select)){??
- ????????????????printf("fd_all[%d]?is?ok\n",?i);??
- ??????????????????
- ????????????????char?buf[1024]={0};??//讀寫緩沖區??
- ????????????????int?num?=?read(fd_all[i],?buf,?1024);??
- ????????????????if(num?>?0){??
- ??
- ????????????????????//收到?客戶端數據并打印??
- ????????????????????printf("receive?buf?from?client?fd_all[%d]?is:?%s\n",?i,?buf);??
- ??????????????????????
- ????????????????????//回復客戶端??
- ????????????????????num?=?write(fd_all[i],?buf,?num);??
- ????????????????????if(num?<?0){??
- ????????????????????????perror("fail?to?write?");??
- ????????????????????????exit(1);??
- ????????????????????}else{??
- ????????????????????????//printf("send?reply\n");??
- ????????????????????}??
- ??????????????????????
- ????????????????}??
- ????????????????else?if(0?==?num){?//?客戶端斷開時??
- ??????????????????????
- ????????????????????//客戶端退出,關閉套接字,并從監聽集合清除??
- ????????????????????printf("client:fd_all[%d]?exit\n",?i);??
- ????????????????????FD_CLR(fd_all[i],?&fd_read);??
- ????????????????????close(fd_all[i]);??
- ????????????????????fd_all[i]?=?-1;??
- ??????????????????????
- ????????????????????continue;??
- ????????????????}??
- ??????????????????
- ????????????}else?{??
- ????????????????//printf("no?data\n");????????????????????
- ????????????}??
- ????????}??
- ????}??
- ??????
- ????return?0;??
- }??
運行結果: