【系統設計】用python搭個web服務器玩玩(一)

若是你想成爲一個優秀的開發者,你應該對平常使用的軟件系統的內部結構有深刻的理解,包括編程語言、數據庫及操做系統、Web 服務器及 Web 框架。並且,爲了更好更深刻地理解這些系統,你應當從頭開始,用一磚一瓦來從新構建這個系統。python

在Ruslan的三篇系列文章中教咱們如何從頭開始創造一個簡單的Web 服務器,來幫助你們更好的理解web服務器,本文主要是對Ruslan三篇文章的翻譯以及加上本身的一些理解而成,但願能對你們有幫助,爲了更好的理解本文,能夠參考協議森林系列先生,要點單嗎? (HTTP協議概覽)(請戳我)web

web服務器是什麼

要構建本身的web服務器,首先要知道Web 服務器是什麼?下面就是一個典型的web服務器,相信你們在生活中都使用過:
【系統設計】用python搭個web服務器玩玩(一)
簡而言之,web服務器是一個運行的網絡服務器(也就是一個軟件),等待客戶端向其發送請求。當它接收請求後,會生成響應,並回送至客戶端。客戶端和服務端之間經過 HTTP 協議來實現相互交流。客戶端能夠是你的瀏覽器,也能夠是使用 HTTP 協議的其它任何軟件,其實,在你看這篇公衆號文章的時候,使用的就是web服務器提供的服務。數據庫

一個簡單的web服務器實現

商用web服務器是很複雜的,動輒幾十萬行代碼是很常見的,可是它也能夠很簡單,簡單到幾十行代碼就能搞定。不信,看Ruslan先生的初版web服務器代碼。這個例子由 Python 寫成,即便你沒據說過 Python(它是一門超級容易上手的語言,快去試試看!),你也應該可以從代碼及註釋中理解其中的理念:編程

import socket
HOST, PORT = '', 8888
listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
listen_socket.bind((HOST, PORT))
listen_socket.listen(1)
print 'Serving HTTP on port %s ...' % PORT
while True:
    client_connection, client_address = listen_socket.accept()
    request = client_connection.recv(1024)    
    print request

    http_response = """\
HTTP/1.1 200 OK

Hello, World!"""
    client_connection.sendall(http_response)
    client_connection.close()

將以上代碼保存爲 webserver1.py,而後,在命令行中運行這個程序。像這樣:瀏覽器

$ python webserver1.py
Serving HTTP on port 8888 ...

如今,在你的網頁瀏覽器的地址欄中輸入 URL:http://localhost:8888/hello ,敲一下回車,而後來見證奇蹟。你應該看到「Hello, World!」顯示在你的瀏覽器中,就像下圖那樣:
【系統設計】用python搭個web服務器玩玩(一)服務器

web服務器工做流程
如今咱們來討論一下它其實是怎麼工做的。首先咱們從你剛剛輸入的 Web 地址開始。它叫 URL,這是它的基本結構:
【系統設計】用python搭個web服務器玩玩(一)
URL 是一個 Web 服務器的地址,瀏覽器用這個地址來尋找並鏈接 Web 服務器,並將上面的內容返回給你。在你的瀏覽器可以發送 HTTP 請求以前,它須要與 Web 服務器創建一個 TCP 鏈接。而後會在TCP 鏈接中發送 HTTP 請求,並等待服務器返回 HTTP 響應。當你的瀏覽器收到響應後,就會顯示其內容,在上面的例子中,它顯示了「Hello, World!」。網絡

咱們來進一步探索在發送 HTTP 請求以前,客戶端與服務器創建 TCP 鏈接的過程。爲了創建連接,它們使用了socket(套接字)。咱們如今不直接使用瀏覽器發送請求,而在命令行中用 telnet 來人工模擬這個過程。架構

在你運行 Web 服務器的電腦上,在命令行中創建一個 telnet 會話,指定一個本地域名,使用端口 8888,而後按下回車:框架

$ telnet localhost 8888
Trying 127.0.0.1 ...
Connected to localhost.

這個時候,你已經與運行在你本地主機的服務器創建了一個 TCP 鏈接。在下圖中,你能夠看到一個服務器從頭開始,到可以創建 TCP 鏈接的基本過程。
【系統設計】用python搭個web服務器玩玩(一)
在同一個 telnet 會話中,輸入 GET /hello HTTP/1.1,而後輸入回車:socket

$ telnet localhost 8888
Trying 127.0.0.1 ...Connected to localhost.
GET /hello HTTP/1.1

HTTP/1.1 200 OK
Hello, World!

你剛剛手動模擬了你的瀏覽器(的工做)!你發送了 HTTP 請求,而且收到了一個 HTTP 應答。下面是一個 HTTP 請求的基本結構:
【系統設計】用python搭個web服務器玩玩(一)
HTTP 請求的第一行由三部分組成:HTTP 方法(GET,由於咱們想讓咱們的服務器返回一些內容),以及標明所需頁面的路徑 /hello,還有協議版本。

爲了簡單一些,咱們剛剛構建的 Web 服務器徹底忽略了上面的請求內容。你也能夠試着輸入一些無用內容而不是「GET /hello HTTP/1.1」,但你仍然會收到一個「Hello, World!」響應。

一旦你輸入了請求行並敲了回車,客戶端就會將請求發送至服務器;服務器讀取請求行,就會返回相應的 HTTP 響應。

下面是服務器返回客戶端(在上面的例子裏是 telnet)的響應內容:
【系統設計】用python搭個web服務器玩玩(一)
這個響應由三部分組成:一個狀態行 HTTP/1.1 200 OK,後面跟着一個空行,再下面是響應正文。

HTTP 響應的狀態行 HTTP/1.1 200 OK 包含了 HTTP 版本號,HTTP 狀態碼200以及 HTTP 狀態短語「OK」。當瀏覽器收到響應後,它會將響應正文顯示出來,這也就是爲何你會在瀏覽器中看到「Hello, World!」。

以上就是 Web 服務器的基本工做模型。總結一下:Web 服務器建立一個處於監聽狀態的套接字,循環接收新的鏈接。客戶端創建 TCP 鏈接成功後,會向服務器發送 HTTP 請求,而後服務器會以一個 HTTP 響應作應答,客戶端會將 HTTP 的響應內容顯示給用戶。爲了創建 TCP 鏈接,客戶端和服務端均會使用套接字。

如今,你應該瞭解了 Web 服務器的基本工做方式,你能夠使用瀏覽器或其它 HTTP 客戶端進行試驗。若是你嘗試過、觀察過,你應該也可以使用 telnet,人工編寫 HTTP 請求,成爲一個「人形」 HTTP 客戶端。

如今留一個小問題:「你要如何在不對程序作任何改動的狀況下,在你剛剛搭建起來的 Web 服務器上適配 Django, Flask 或 Pyramid 應用呢?」將在本系列的第二部分中來詳細講解。

推薦閱讀:

精心整理 | 歷史乾貨文章目錄
【福利】本身蒐集的網上精品課程視頻分享(上)
【系統架構】大型網站架構演化歷程(上)
【系統架構】大型網站架構演化歷程(下)
【C++札記】C++對象模型以內存佈局(2)
【C++札記】C++對象模型以內存佈局(1)

專一服務器後臺技術棧知識總結分享

歡迎關注交流共同進步

【系統設計】用python搭個web服務器玩玩(一)

碼農有道 coding

碼農有道,爲您提供通俗易懂的技術文章,讓技術變的更簡單!