Unix C語言編寫基于IO多路復用的小型并發服務器
本博文主要介紹了如何用Unix C語言編寫基于IO多路復用的小型并發服務器,即如何同時處理網絡連接請求和標準輸入交互。
背景介紹
如果服務器要同時處理網絡上的套接字連接請求和本地的標準輸入命令請求,那么如果我們使用accept來接受連接請求,則無法處理標準輸入請求;類似地,如果在read中等待一個輸入請求,則無法處理網絡連接的請求。
所謂I/O多路復用機制,就是說通過一種機制,可以監視多個描述符,一旦某個描述符就緒(一般是讀就緒或者寫就緒),能夠通知程序進行相應的讀寫操作。但 select,poll,epoll本質上都是同步I/O,因為他們都需要在讀寫事件就緒后自己負責進行讀寫,也就是說這個讀寫過程是阻塞的,而還有一種情況是異步IO,異步I /O則無需自己負責進行讀寫,異步I/O的實現會負責把數據從內核拷貝到用戶空間。
使用select實現IO多路復用
我們可以使用select函數來實現等待一組描述符準備好讀。select函數處理類型為fd_set的集合,也叫做描述符集合。邏輯上,我們可以將描述符集合看成一個大小為n的位向量,每個位對應一個描述符。select函數是一個阻塞函數,即只有等到讀集合中至少有一個描述符可以讀時,就不會阻塞,開始處理請求了。
代碼如下:
#include "csapp.h"//此程序是使用基于 IO多路復用的并發服務器
void echo(int connfd) { int n; char buf[MAXLINE]; rio_t rio;
rio_readinitb(&rio,connfd); //帶緩沖的讀取函數 while((n=rio_readlineb(&rio,buf,MAXLINE))>0) { //向連接符寫入內容 printf("server received %d bytes \n",n); rio_writen(connfd,buf,n); }
}
/command是作為鍵盤輸入時執行的驅動動作/ void command(void) { char buf[MAXLINE]; printf("you input just now!\n"); //從標準輸入中讀取輸入到buf中存儲 if(!fgets(buf,MAXLINE,stdin)) exit(0); //輸出buf中的數據 printf("%s",buf); }
//主程序 int main(int argc,char **argv) {
//監聽符,連接符,端口號 int listenfd,connfd,port; //套接字地址結構的大小 socklen_t clientlen=sizeof(struct sockaddr_in); //新建套接字地址結構 struct sockaddr_in clientaddr; //fd_set為描述符集合,此處定義了兩個read_set,ready_set描述符集合,分別是讀集合/準備好集合 fd_set read_set,ready_set;//如果運行時參數小于2,則提示錯誤 if(argc!=2) { fprintf(stderr,"usage :%s <port>\n",argv[0]); exit(0); } //將第二個參數轉化為整型端口號,args to integer port=atoi(argv[1]); //打開端口號,返回監聽描述符 listenfd=open_listenfd(port); //清空讀集合 FD_ZERO(&read_set); //將標準輸入加到讀集合 FD_SET(STDIN_FILENO,&read_set); //將監聽描述符加到讀集合 FD_SET(listenfd,&read_set); //服務器監聽處理主程序 while(1) { //將讀集合賦值給準備好集合 ready_set=read_set; //select函數會要求內核掛起進程,等待一個或多個IO事件發生后,才將控制返回給應用程序,就像在下面的示例一樣 select(listenfd+1,&ready_set,NULL,NULL,NULL); //有IO事件后,將判斷是來自從鍵盤上鍵入命令還是從客戶端發來的請求,分別給出不同的回應 if(FD_ISSET(STDIN_FILENO,&ready_set)) command(); if(FD_ISSET(listenfd,&ready_set)) { connfd=accept(listenfd,(SA *)&clientaddr,&clientlen); printf("client connected!"); //向連接符回送數據 echo(connfd); //關閉連接符,釋放資源 close(connfd); } }
}</pre>
測試部分
zzw@zzw-ThinkPad-Edge-E430c:~$ telnet localhost 9999 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. hello hello
zzw@zzw-ThinkPad-Edge-E430c:~/doc_main/CProgram/Concurrency$ ./select.o 9999 client connected!server received 7 bytes
最后強調一下,這里是同步的IO問題,并非異步。
來自:http://my.oschina.net/zzw922cn/blog/493723