在[從輸入網址到瀏覽器返回內容(一),服務器處理篇](http://www.cnblogs.com/dy2903/p/8305881.html "從輸入網址到瀏覽器返回內容(一),服務器處理篇")提到了Web服務器以及應用服務器,它主要用來接收從瀏覽器發過來的HTTP請求,并進行頁面的呈現。
本文主要講一下HTTP服務器的進化過程
# HTTP服務器1.0:單進程
所謂`HTTP協議`實際上就是瀏覽器向服務器發送一個HTTP Request請求,然后HTTP服務器進行處理,從硬盤拿一個HTML文件,然后以文本的形式返回過去(HTTP response)
麻煩的是需要請操作系統建立HTTP協議下面的TCP連接通道。這個通道是通過Socket建立的:
- 先在80端口監聽
- 進入無限循環,如果有連接請求來,就接受(accept)
- 創建新的socket,然后通過這個新的socket來接收,發送HTTP數據
這就是HTTP Server 1.0版本。
但是網絡傳輸是不穩定的,有可能很長時間都沒有響應,所以服務器的receive進程很可能因為得不到數據而**阻塞**。但是進程也不能歇著,因為還有很多瀏覽器的連接在那里等著。
解決方案就是為每個請求開一個進程或者線程
# HTTP服務器2.0:多進程
多進程的思路是當accept獲得到連接的時候,生成了新的socket,但是不在主進程里面處理,而是**新創建一個子進程**來接管。這樣主進程就不會阻塞在receive上,可以繼續接受新的連接了。
但是如果有成千上萬個連接來了以后,如果每個連接都開一個進程,而每個進程都**耗費大量的系統資源,而且進程切換也非常消耗資源。**
如果把多進程換為多線程,只能說可能會好一點,但是治標不治本。

# HTTP 服務器3.0:Select模型
之前的方法存在一個非常大的問題,一個Socket連接就是一個文件描述符,這個描述符是個指針指向一個簡單的數據結構,但是我們用了一個重量級的進程來對它進行讀寫,非常的浪費。
那么思路又回到了單進程上,只需要**改變一下工作方式**就可以用單進程進行連接的處理。
在1.0的階段,為什么會阻塞,因為瀏覽器只是和我們建立了連接,但是沒有把**數據**發送過來,但是我們又迫不及待的去讀,所以只能阻塞。所以,問題的癥結就在于此。
所以,可以接收了客戶端連接以后,**不急著讀**,而是拿到socket fd的編號,就可以處理其他的連接了。
下面操作系統會在后臺檢查socket,如果發現有socket可以讀寫了,就會做個**標記**,然后通知HTTP服務器。
HTTP服務器遍歷一遍所有的socket 描述符,看看誰有標記,有標記的才做處理。
處理完了,把socket的描述符告訴操作系統,然后再繼續等待,
這種方式就是Select

# HTTP服務器4.0:epoll
Select方法存在一個弊端,每次被喚醒之后,需要遍歷所有的socket 描述符,看看是否有標志位。
所以要如果能把**發生了變化的socket**告訴HTTP服務器就好了。這種方式就是epoll。
使用了epoll,就不需要遍歷全部的集合,只需要處理哪些有變化的。

# 參考
[Http Server:一個差生的逆襲](http://mp.weixin.qq.com/s?__biz=MzAxOTc0NzExNg==&mid=2665513467&idx=1&sn=178459f4bb9891c9cf471a28e7c340be&chksm=80d679b8b7a1f0aea8f6e3f09acb6969993825753170dc3db63f8ef35c95cce98aa40a0c7097&scene=21#wechat_redirect)