http://www.cnblogs.com/mingda/archive/2010/11/12/1875465.htmlhtml
http://www.cnblogs.com/mingda/archive/2010/11/14/1876961.html算法
http://www.ibm.com/developerworks/cn/aix/library/au-libev/編程
http://blog.csdn.net/hguisu/article/details/7445768/ socket編程,講的比較清楚,看的比較明白
http://acm.tzc.edu.cn/acmhome/projectList.do?method=projectNewsDetail&nid=2
http://goodcandle.cnblogs.com/archive/2005/12/10/294652.aspx服務器
構建現代的服務器應用程序須要以某種方法同時接收數百、數千甚至數萬個事件,不管它們是內部請求仍是網絡鏈接,都要有效地處理它們的操做。有許多解決方案,可是 libevent 庫和 libev 庫可以大大提升性能和事件處理能力。網絡
在 Linux 下一切資源皆文件,普通文件是文件,磁盤打印機是文件,socket 固然也是文件。在 Linux 下建立一個新的 socket 鏈接,實際上就是建立一個新的文件描述符。
所以,咱們能夠經過使用 ulimit -n 來限制程序所能打開的最大文件描述符數量,從而達到限制 socket 建立的數量。多線程
在閱讀這一系列的文章時,我但願你們始終記住如下幾點:架構
1. 軟件開發沒有銀彈,人們老是試圖找到問題的惟一解和最優解,但事實是每一個問題都有N種解,而且在不一樣狀況下最優解是不同的,若是非要說軟件開發有銀彈, 那麼這顆銀彈就是人的心,是否找能到最優解,在於你是否能把握住了全部事情的平衡點。因此,請不要說某某機制最好、某某算法最優、某某架構萬能、不須要再 瞭解其餘了。也請不要自覺得目空一切技術,商業和盈利至上,實現途徑和方式無所謂。請抱着一切皆有可能的心態看待全部事物,纔有更多機會看到平衡點。框架
2. 語言、平臺、API只是迷人眼的東西,它們好像什麼都是,其實什麼都不是。解決問題的關鍵在於設計者的心,設計者是否對要解決的問題和問題的上下文了然於 心。不要把學習語言、學習平臺、學習API看成目標,它們只是泥沙和工具,最終咱們要建造的是房屋,因此請把目標放得更遠。也不要把它們當成阻礙,由於它 們生來就是要讓人使用的東西,不可能造成阻礙,若是你以爲它們是阻礙,那實際上那個阻礙只在你內心。socket
3. 記得:Do one thing, and do it well。一次只作一件事,而且作好它。這一系列文章的基本開發和測試環境是Linux,編譯器是gcc。若是你以前對Linux不是很熟悉,我建議你安 裝一個VMware,並安裝Ubuntu桌面系統,而後apt-get install build-essential,這樣你的系統裏就有完整的開發環境了,gnome自帶的gedit很好用,我也是這麼作的。不要一上來就Linux命令 行界面加vi編輯器,不必爲難本身也不要搞得本身像黑客同樣。請記住,咱們當前要作的是高性能socket服務器,只作這一件事,而且作好它!不是研究 Linux命令行或者vi編輯器,那些等有空再慢慢研究還來的及。編輯器
4. 師傅請進門修行靠我的。文章和教程的內容其實都是轉之由轉的東西,若是要了解原汁原味的內容,首選應該去閱讀操做系統的代碼,其次是系統文檔,再次纔是網 絡教程。而經驗是世界上最難傳達的東西,文字只能讓你造成記憶,不能讓你得到經驗,它只是像買彩票同樣給你提供一個機會,讓在你實踐過程當中可能會有那麼一 下的靈光一現,而後得出本身的結論,那纔是你的真正經驗。若是把生活比喻成RPG,造物主怎麼可能讓經驗能夠在玩家之間傳遞呢?那不是亂了套了,打RPG 咱們能夠學各類技能,可是要獲得經驗就得打怪作任務,生活其實也是同樣的道理。
本章演示了一個簡單的echo服務器,它只支持一次處理一個鏈接,而且在鏈接退出時,服務器端也跟着關閉。
經過這個簡單的例子,咱們學習了基本的socket初始化過程和鏈接的響應方式。
固然這些知識對於咱們的遠大目標「高性能socket服務器"來講是遠遠不夠的,可是這些是基礎的基礎,至少咱們已經邁開了第一步。
下一章我將向你們介紹如何利用IO重用,在一個進程中同時處理多個鏈接的請求,敬請期待。
上一章,我向你們演示了一個最基本的socket服務器結構,它一次只能響應一個鏈接請求,而「能同時響應多個鏈接和請求」無疑是現實生活中對 socket服務器的最基本要求。要如何讓socket服務器能夠同時響應多個鏈接和請求呢?多線和多進程程確定是大部分人首先想到的,可能不少人不必定 真正清楚多線程和多進程的socket服務器架構具體意味着什麼,可是至少你們都或多或少據說過這兩種技術。不過本章中,咱們暫時還不會涉及到多線程和多 進程的服務器架構,我它們歸類爲設計範疇,而咱們暫時尚未脫離泥水匠身份,因此還要繼續學習「泥沙之用途「,設計的事情須要等到咱們泥水匠畢業,升級建 築設計師的時候再說。
那麼本章具體的內容是什麼呢?真是沒有懸念,在上一章中我已經提早透露了:IO重用。下面就正式進入主題吧。
IO重用技術有不少種,有些是誇平臺的,有些是平臺獨有的,這裏就列舉一些我知道的:
名稱 平臺
select Linux, *BSD, Mac OS X, Solaris, Windows
poll Linux, *BSD, Mac OS X
epoll Linux
/dev/poll Solaris
kqueue FreeBSD
IOCP Windows
每一種IO重用技術都是經過操做系統提供的一組特定的API函數調用來提供支持的,咱們所要學的就是學會怎麼用這些API而且瞭解每種技術背後的原理和前因後果。
本章就從最通用應該也是最先出現的IO重用技術select開始。
補充說明一點,IO重用並不侷限於用在socket編程上,只要涉及到IO的編程均可以應用。
後續我可能會繼續花一到兩章來描述poll和epoll,但也可能直接就介紹如何使用誇平臺的libev和libevent庫。特別是poll實際 上它對select來講沒有改進的地方,屬於同一水平,實際上它們只是起源不同,可是時代差很少因此水平也就差很少,然後續的epoll、 dev/poll、iocp則是百家爭鳴時期各個操做系統平臺爲了進一步提升io重用效率而設計的新機制,它們才本質上對select和poll等老模式 進行了改進,因此我可能會跳過poll介紹epoll。
libev的實驗代碼我其實已經作好了,poll和epoll有點懶得重複了,具體怎麼樣還不肯定就留個懸念吧,呵呵。
libev是libevent以後的一個事件驅動的編程框架,其接口和libevent基本相似。據官方介紹,其性能比libevent還要高,bug比libevent還少。
libevent
Chromium、Memcached、NTP、HTTPSQS等著名的開源程序都使用libevent庫
/* File Name: server.c */ #include<stdio.h> #include<stdlib.h> #include<string.h> #include<errno.h> #include<sys/types.h> #include<sys/socket.h> #include<netinet/in.h> #define DEFAULT_PORT 8000 #define MAXLINE 4096 int main(int argc, char** argv) { int socket_fd, connect_fd; struct sockaddr_in servaddr; char buff[4096]; int n; //初始化Socket if( (socket_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ){ printf("create socket error: %s(errno: %d)\n",strerror(errno),errno); exit(0); } //初始化 memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//IP地址設置成INADDR_ANY,讓系統自動獲取本機的IP地址。 servaddr.sin_port = htons(DEFAULT_PORT);//設置的端口爲DEFAULT_PORT //將本地地址綁定到所建立的套接字上 if( bind(socket_fd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1){ printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno); exit(0); } //開始監聽是否有客戶端鏈接 if( listen(socket_fd, 10) == -1){ printf("listen socket error: %s(errno: %d)\n",strerror(errno),errno); exit(0); } printf("======waiting for client's request======\n"); while(1){ //阻塞直到有客戶端鏈接,否則多浪費CPU資源。 if( (connect_fd = accept(socket_fd, (struct sockaddr*)NULL, NULL)) == -1){ printf("accept socket error: %s(errno: %d)",strerror(errno),errno); continue; } //接受客戶端傳過來的數據 n = recv(connect_fd, buff, MAXLINE, 0); //向客戶端發送迴應數據 if(!fork()){ /*紫禁城*/ if(send(connect_fd, "Hello,you are connected!\n", 26,0) == -1) perror("send error"); close(connect_fd); exit(0); } buff[n] = '\0'; printf("recv msg from client: %s\n", buff); close(connect_fd); } close(socket_fd); }