Linux网络编程服务器模型选择之IO复用循环并发服务器
????? 在前面我們介紹了循環(huán)服務(wù)器,并發(fā)服務(wù)器模型。簡單的循環(huán)服務(wù)器每次只能處理一個(gè)請求,即處理的請求是串行的,效率過低;并發(fā)服務(wù)器可以通過創(chuàng)建多個(gè)進(jìn)程或者是線程來并發(fā)的處理多個(gè)請求。但是當(dāng)客戶端增加時(shí),就需要?jiǎng)?chuàng)建更多的進(jìn)程或者線程,就會導(dǎo)致系統(tǒng)負(fù)載最終轉(zhuǎn)移到進(jìn)程或線程的切換開銷上。
????? 為了減少這類開銷,而使系統(tǒng)處理能力集中在核心業(yè)務(wù)上,就要求我們降低并發(fā)的進(jìn)程或線程數(shù)目,因此又實(shí)現(xiàn)了一個(gè)更高級的IO復(fù)用循環(huán)服務(wù)器。I/O復(fù)用的循環(huán)服務(wù)器一般創(chuàng)建兩個(gè)線程,一個(gè)是客戶端連接處理線程,專門用來處理客戶端的連接,當(dāng)有客戶端到來的時(shí)候,此線程把客戶端的套接字描述符放到一塊公共的區(qū)域中。另一個(gè)是業(yè)務(wù)處理線程,此線程輪循(select)客戶端套接字描述符集合中有沒有數(shù)據(jù)到來,如果有數(shù)據(jù)到來,那么就進(jìn)行處理。這樣,客戶 端的增加并不會造成系統(tǒng)進(jìn)程或線程數(shù)的明顯增加,而使其處理能力與CPU和內(nèi)存直接相關(guān)。
TCP并發(fā)服務(wù)器模型 I/O多路復(fù)用模型偽代碼
/* TCP并發(fā)服務(wù)器模型 I/O多路復(fù)用 */ /* 服務(wù)器主進(jìn)程 */socket();bind();listen(); pthread_create( ); //創(chuàng)建客戶端連接線程和業(yè)務(wù)處理線程pthread_join( ); //等待線程結(jié)束close( ); //關(guān)閉服務(wù)器套接字 /* 連接處理線程 */while(1){accept( ); //接受一個(gè)客戶端連接store();//存儲客戶端套接字描述符到一個(gè)公共集合中 }/* 業(yè)務(wù)處理線程 */while(1){get( ); //取出可用的客戶端套接字描述符select( ); //設(shè)置監(jiān)聽讀寫文件描述符集合 recv( );process( );send( );close( );}一個(gè)I/O多路復(fù)用模型的例子
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <time.h> #include <string.h> #include <stdio.h> #include <pthread.h> #include <sys/select.h> #define BUFFLEN 1024 #define SERVER_PORT 12349 #define BACKLOG 5 #define CLIENTNUM 1024 /**最大支持客戶端數(shù)量*//*可連接客戶端的文件描述符數(shù)組*/ int connect_host[CLIENTNUM]; int connect_number = 0; static void *handle_request(void *argv) { time_t now; /*時(shí)間*/char buff[BUFFLEN];/*收發(fā)數(shù)據(jù)緩沖區(qū)*/int n = 0;int maxfd = -1;/*最大偵聽文件描述符*/fd_set scanfd; /*偵聽描述符集合*/struct timeval timeout; /*超時(shí)*/timeout.tv_sec = 1; /* 阻塞1秒后超時(shí)返回 */ timeout.tv_usec = 0; int i = 0;int err = -1;for(;;){ /*最大文件描述符值初始化為-1*/ maxfd = -1;FD_ZERO(&scanfd);/*清零文件描述符集合*/for(i=0;i<CLIENTNUM;i++)/*將文件描述符放入集合*/{if(connect_host[i] != -1)/*合法的文件描述符*/{FD_SET(connect_host[i], &scanfd);/*放入集合*/if(maxfd < connect_host[i])/*更新最大文件描述符值*/{maxfd = connect_host[i];}}}/*select等待*/err = select(maxfd + 1, &scanfd, NULL, NULL, &timeout) ; switch(err){case 0:/*超時(shí)*/break;case -1:/*錯(cuò)誤發(fā)生*/break;default:/*有可讀套接字文件描述符*/if(connect_number<=0)break;for(i = 0;i<CLIENTNUM;i++){/*查找激活的文件描述符*/if(connect_host[i] != -1)if(FD_ISSET(connect_host[i],&scanfd)) { memset(buff, 0, BUFFLEN);/*清零*/n = recv(connect_host[i], buff, BUFFLEN,0);/*接收發(fā)送方數(shù)據(jù)*/if(n > 0 && !strncmp(buff, "TIME", 4))/*判斷是否合法接收數(shù)據(jù)*/{memset(buff, 0, BUFFLEN);/*清零*/now = time(NULL);/*當(dāng)前時(shí)間*/sprintf(buff, "%24s\r\n",ctime(&now));/*將時(shí)間拷貝入緩沖區(qū)*/send(connect_host[i], buff, strlen(buff),0);/*發(fā)送數(shù)據(jù)*/}/*關(guān)閉客戶端*/close(connect_host[i]); /*更新文件描述符在數(shù)組中的值*/connect_host[i] = -1;connect_number --; /*客戶端計(jì)數(shù)器減1*/ } }break; } } return NULL; }static void *handle_connect(void *argv) { int s_s = *((int*)argv) ;/*獲得服務(wù)器偵聽套接字文件描述符*/int s_c = -1;/*連接客戶端文件描述符*/struct sockaddr_in from;int len = sizeof(from);/*接收客戶端連接*/for(;;){int i = 0;int s_c = accept(s_s, (struct sockaddr*)&from, &len);/*接收客戶端的請求*/printf("a client connect, from:%s\n",inet_ntoa(from.sin_addr));/*查找合適位置,將客戶端的文件描述符放入*/ for(i=0;i<CLIENTNUM;i++){if(connect_host[i] == -1)/*找到*/{/*放入*/connect_host[i]= s_c;/*客戶端計(jì)數(shù)器加1*/connect_number ++;/*繼續(xù)輪詢等待客戶端連接*/break; } } } return NULL; }int main(int argc, char *argv[]) {int s_s; /*服務(wù)器套接字文件描述符*/struct sockaddr_in local; /*本地地址*/ int i = 0;memset(connect_host, -1, CLIENTNUM);/*建立TCP套接字*/s_s = socket(AF_INET, SOCK_STREAM, 0);/*初始化地址接哦股*/memset(&local, 0, sizeof(local));/*清零*/local.sin_family = AF_INET;/*AF_INET協(xié)議族*/local.sin_addr.s_addr = htonl(INADDR_ANY);/*任意本地地址*/local.sin_port = htons(SERVER_PORT);/*服務(wù)器端口*//*將套接字文件描述符綁定到本地地址和端口*/int err = bind(s_s, (struct sockaddr*)&local, sizeof(local));err = listen(s_s, BACKLOG);/*偵聽*/pthread_t thread_do[2];/*線程ID*//*創(chuàng)建線程處理客戶端連接*/pthread_create(&thread_do[0],/*線程ID*/NULL,/*屬性*/handle_connect,/*線程回調(diào)函數(shù)*/(void*)&s_s); /*線程參數(shù)*//*創(chuàng)建線程處理客戶端請求*/ pthread_create(&thread_do[1],/*線程ID*/NULL,/*屬性*/handle_request,/*線程回調(diào)函數(shù)*/NULL); /*線程參數(shù)*//*等待線程結(jié)束*/for(i=0;i<2;i++)pthread_join(thread_do[i], NULL);close(s_s);return 0; }轉(zhuǎn)載于:https://www.cnblogs.com/lizhenghn/p/3619091.html
總結(jié)
以上是生活随笔為你收集整理的Linux网络编程服务器模型选择之IO复用循环并发服务器的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [51nod1773]A国的贸易
- 下一篇: 《linux下sudo服务的使用》RHE