【翻譯】一步步開發一個Web伺服器.Part 1.

原文鏈接:Let』s Build A Web Server. Part 1.

譯文鏈接:ZKeeer』s Blog

感謝 @劉志軍 的推薦

原文代碼基於python2,本文代碼基於python3

發現錯誤請在評論區指出,多謝

本系列其他文章:

【翻譯】一步步開發一個Web伺服器.Part 1.

【翻譯】一步步開發一個Web伺服器.Part 2.

【翻譯】一步步開發一個Web伺服器.Part 3.

一個女人外出散步,路過一個工地,看到三個人在工作。她問第一個人,「你在做什麼呀?」 第一個人不耐煩地沖她吼:「你沒看到我在壘磚啊?!」 她不滿足於這個回答,又問了第二個人他在做什麼。第二個人回答道:「我在砌一堵牆。」 第二個人轉向第一個人,說道:「嘿!你砌過頭了,趕緊把最後一塊兒轉拿掉。」 女人還是對這個答案不滿意,她問了第三個人同樣的問題。第三個人仰望天空,說:「我在蓋一座迄今為止世界上最大的教堂。」 他站在這兒仰望著天空,背後那兩個人卻還在為那塊兒不合時宜地磚爭論不休。他對第一個人說,「哥們,不用擔心那塊磚了,那是內牆,最後會塗滿塗料,不會有人看見那塊兒磚。開始壘另一層好了。」

這個故事地寓意是當你了解了整個系統並且明白不同部分(磚,牆,教堂)是如何組合在一起的時候,你才能更快地明確並解決問題(擺放不對的磚)。

該如何從頭創建你的Web伺服器呢?

我相信成為一個更好地開發人員,你必須更好地明白你日常使用的底層軟體系統,這底層軟體系統包括編程語言、編譯器和解釋器、資料庫和操作系統、web伺服器和web框架。並且,為了更好的,更深入的理解這些系統,你必須一磚一瓦地從頭重建一遍。

正如孔子所說:不聞不若聞之,聞之不若見之,見之不若知之,知之不若行之。(實際出於《荀子·儒效》,是荀子說的)

在這點兒上,我希望你能堅信,從頭重建不同軟體系統以了解它們如何運行是一個極好的方法。

在這系列地三部分文章中,我將帶領你怎樣寫出你自己地web伺服器。來,開始吧!

第一件事,什麼是web伺服器呢?

簡言之,它是個部署在物理伺服器上地網路伺服器(oops,object-oriented programming system,a server on server),並且等著客戶端向它發送請求。當它接收到一個請求,它會產生一個響應並且發送回去。客戶端和伺服器之間的交流基於HTTP協議。這個客戶端可以是你的瀏覽器或者其他任何使用HTTP協議的軟體。

一個最簡單的web伺服器實現應該是怎樣的?這是我的看法。這個例子基於python(這是一門非常簡單易學的語言,人生苦短,快用python),即使你不知道python是什麼鬼,也不耽誤你理解下面的代碼和概念。

import socketnnHOST, PORT = , 8888nnlisten_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)nlisten_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)nlisten_socket.bind((HOST, PORT))nlisten_socket.listen(1)nprint(Serving HTTP on port %s ... % PORT)nwhile True:n client_connection, client_address = listen_socket.accept()n request = client_connection.recv(1024)n print(request)nn http_response = b"""n HTTP/1.1 200 OKnn Hello, World!n """n client_connection.sendall(http_response)n client_connection.close()n

譯者註:http_response必須為bytes類型,通過加b解決。

保存上面的代碼,命名為webserver1.py或者從我的github上down下來,用下面的命令運行:

$ python webserver1.pynServing HTTP on port 8888 …n

運行之後,在你的瀏覽器中輸入 http://localhost:8888/hello 敲回車,然後你會驚訝「還有這種操作?!」你的瀏覽器中會出現如下:

擼起袖子加油干!等你凱旋歸來!

…接下來我們討論一下它實際工作原理。

首先,我們從上面輸入的地址入手。此地址叫做URL(統一資源定位器),來看看它的結構:

這就是你告訴你的瀏覽器要找到的web伺服器,鏈接到那個伺服器上的頁面並且給你取回來。在你的瀏覽器發送一個HTTP請求之前,它會和伺服器建立一個TCP連接。然後通過TCP鏈接給伺服器發送一個HTTP請求,等著web伺服器返回一個響應。當你的瀏覽器接收到這個請求後,會在瀏覽器中展示出來,在這個例子中瀏覽器顯示的是「Hello World!」

在發送HTTP請求並得到回應之前,客戶端和伺服器是怎樣建立一個TCP鏈接的,接下來細細探究。為了建立TCP鏈接,伺服器和客戶端都使用了所謂的sockets。接下來我們使用telnet命令行模擬瀏覽器而不是直接用瀏覽器。

在同一台電腦上,運行web伺服器,同時在命令行啟動telnet會話。指定一個連接的主機和埠,例如localhost 8888

$ telnet localhost 8888nTrying 127.0.0.1 …nConnected to localhost.n

此時,你已經和本機上的web伺服器及那裡了一個TCP連接,可以準備發送接收消息了。在下圖中,你可以看到伺服器在接受一個新的TCP連接之前所進行的標準流程。

在上面打開的telnet會話中,輸入

GET/hello HTTP/1.1n

回車。

$ telnet localhost 8888nTrying 127.0.0.1 …nConnected to localhost.nGET /hello HTTP/1.1nnHTTP/1.1 200 OKnHello, World!n

恭喜!你模擬了一個瀏覽器。與此同時,你發送了一個HTTP請求並得到了一個HTTP響應。HTTP請求的基本結構如下:

HTTP請求包含了HTTP方法(這裡使用的是GET方法,因為我們要伺服器返回給我們響應)、表示伺服器上頁面的路徑例如 /hello、協議版本。

為簡單起見,此時我們的Web伺服器完全忽略了上述請求。你可以輸入任何無意義的輸入,你得到的還會是「Hello World!」的響應。

一旦我們輸入了請求點擊回車,客戶端會發送這個請求給伺服器,伺服器會讀取請求,列印出來並且返迴響應的HTTP響應。

下圖是伺服器發送給客戶端的HTTP響應:

我們來解析一下這個響應。響應包含了HTTP版本、HTTP狀態,緊跟著一個必須的空行,然後是響應的主體。

這個響應狀態 HTTP/1.1 200 OK 包含了HTTP版本、HTTP狀態碼、HTTP狀態碼對應的原因短語OK 。當瀏覽器接收到這個響應時,它展示了響應的主體部分,這就是為什麼你能在瀏覽器看到「Hello World!」

那是一個最基本的web伺服器模型。總結一下:web伺服器創建了一個監聽套接字,並且開啟了一個循環,不斷接受新的連接。客戶端初始化一個TCP連接,成功建立連接之後,客戶端發送給伺服器HTTP請求,得到一個伺服器的HTTP響應,解析這個響應展示給用戶。為了建立這個TCP連接,客戶端和伺服器都使用了sockets。

現在我們已經建立了一個最基礎的web伺服器,你可以用你的瀏覽器或者其他HTTP客戶端進行測試。正如上文所示,你可以用telnet通過輸入請求來模擬一個HTTP客戶端。

留一個課後作業題:不改變伺服器代碼的情況下,怎樣在你剛完成的伺服器上運行一個Django、Flask、Pyramid應用,適應不同的web框架?

我會在Part 2中講解,敬請期待。


推薦閱讀:

利用 pwntools 編寫 socket 腳本
Python數據分析之anaconda安裝和使用
[11] Python條件判斷語句(二)
高效靈活的概率建模方法基於Python
Python數據分析及可視化實例之常規存儲txt、csv、xls

TAG:Python | Web服务器 |