有天一個女士出門散步,路過一個建筑工地,看到三個男人在干活。她問第一個男人,“你在干什么呢?”,第一個男人被問得很煩,咆哮道,“你沒看到我在碼磚嗎?”。她對回答不滿意,然后問第二個男人他在干什么。第二個男人回答,“我正在砌墻”,然后轉(zhuǎn)移注意力到第一個男人,他說,“嘿,你碼過頭了,你要把最后一塊磚拿掉?!薄K€是對回答不滿意,然后問第三個男人在干什么。第三個男人仰望著天空對她說,“我正在建造世界上最大的教堂?!?。當(dāng)他站在那里仰望天空的時候,另外兩個男人開始爭論磚位置不對的問題。第三個男人轉(zhuǎn)向前兩個男人說,“嘿,伙計們,別擔(dān)心那塊磚了,那是里面的墻,它會被灰泥堵塞起來,然后沒人會看到那塊磚。去另一層干活吧?!?/p>
故事的寓意是說,當(dāng)你了解整個系統(tǒng),理解不同的部分如何組織到一起的(磚、墻、教堂),你就能找出問題并快速解決之(磚位置不對)。
這跟從零開始搭建你的WEB服務(wù)器有什么關(guān)系呢?
我相信,要成為優(yōu)秀的開發(fā)者,你必須對你每天都用的底層的軟件系統(tǒng)有進一步的理解,包括編程語言、編譯器和解釋器、數(shù)據(jù)庫和操作系統(tǒng)、WEB服務(wù)器和WEB框架。為了更好更深入的理解這些系統(tǒng),你可以從零開始一塊磚地,一面墻地,重建它們。
子曰:聞之我也野,視之我也饒,行之我也明

“我看過的,我還記得?!?/p>

“我做過的,我都理解了?!?/p>

(子曰:聞之我也野,視之我也饒,行之我也明)
此時我希望你能夠相信,從重建不同的軟件系統(tǒng)來開始來學(xué)習(xí)它們是如何工作的,是一個好主意。
在這個由3部分組成的系列文章中,我會向你展示怎樣搭建一個基本的WEB服務(wù)器。咱們開始吧。
重中之重,什么是WEB服務(wù)器?

簡而言之,它是一個位于一個物理服務(wù)器上的網(wǎng)絡(luò)服務(wù)器(呀,服務(wù)器上的服務(wù)器),它等待客戶端發(fā)送請求。當(dāng)它接收到一個請求,就會生成一個響應(yīng)并回發(fā)給客戶端??蛻舳撕头?wù)器使用HTTP協(xié)議通信??蛻舳丝梢允菫g覽器或者別的使用HTTP協(xié)議的軟件。
一個非常簡單的WEB服務(wù)器實現(xiàn)長什么樣呢?以下是我寫的一個。例子是用Python語言寫的,但是即使你不會Python(它是一個非常易學(xué)的語言,試試?。闳匀豢梢酝ㄟ^代碼和下面的解釋理解相關(guān)概念:
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或者直接從GitHub下載,然后像下面這樣在命令行運行它
$ python webserver1.py
Serving HTTP on port 8888 …
現(xiàn)在在你的WEB瀏覽器地址欄里輸入以下URL http://localhost:8888/hello,敲回車,見證奇跡的時刻。你會看到瀏覽器顯示”Hello, World!“,像這樣:

認真做一下吧,我會等你的。
做完了?很好?,F(xiàn)在我們討論一下它到底怎么工作的。
首先我們從你剛才鍵入的WEB地址開始。它叫URL,這是它的基本結(jié)構(gòu):

這個就表示怎樣告訴瀏覽器要查找和連接的WEB服務(wù)器地址,和你要獲取的服務(wù)器上的頁面(路徑)。但是在瀏覽器發(fā)送HTTP請求前,瀏覽器需要先和WEB服務(wù)器建立TCP連接。然后瀏覽器在TCP連接上發(fā)送HTTP請求,然后等待服務(wù)器回發(fā)HTTP響應(yīng)。當(dāng)瀏覽器接收到響應(yīng)后,顯示響應(yīng),在本次例子中,瀏覽器顯示“Hello, World!”。
我們再詳細探索一下客戶端和服務(wù)器在發(fā)送HTTP請求和響應(yīng)前如何建立TCP連接的。在建立連接,它們必須使用所謂的sockets。用你命令行下的telnet手動模擬瀏覽器吧,而不是直接使用瀏覽器。
在運行WEB服務(wù)器的同一臺電腦上,在命令行啟動一個telnet會話,指定連接到localhost主機,連接端口為8888,然后按回車:
$ telnet localhost 8888
Trying 127.0.0.1 …
Connected to localhost.
此時,你已經(jīng)和運行在你本地主機的服務(wù)器建立了TCP連接,已經(jīng)準(zhǔn)備好發(fā)送并接收HTTP消息了。下圖中你可以看到一個服務(wù)器要經(jīng)過的標(biāo)準(zhǔn)步驟,然后才能接受新的TCP連接。

在同一個telnet會話中,輸入 GET /hello HTTP/1.1然后敲回車:
$ telnet localhost 8888
Trying 127.0.0.1 …
Connected to localhost.
GET /hello HTTP/1.1
HTTP/1.1 200 OK
Hello, World!
你完成了手動模擬瀏覽器!你發(fā)送了一個HTTP請求并得到了一個HTTP響應(yīng)。這是HTTP請求的基本結(jié)構(gòu):

HTTP請求由行組成。行指示了HTTP方法(GET,因為我們請求我們的服務(wù)器返回給我們一些東西)、代表我們想要的服務(wù)器上的“頁面”的路徑 /hello和協(xié)議版本。
為了簡單起見,此時我們的WEB服務(wù)器完全忽略了上面的請求行。你也可以輸入任何垃圾字符取代“GET /hello HTTP/1.1”,你仍然會得到“Hello, World!”響應(yīng)。
一旦你輸入了請求行,敲了回車,客戶端就發(fā)送請求給服務(wù)器,服務(wù)器讀取請求行,打印出來然后返回相應(yīng)的HTTP響應(yīng)。
以下是服務(wù)器回發(fā)給客戶端(這個例子中是telnet)的HTTP響應(yīng):

咱們分析一下它,響應(yīng)包含了狀態(tài)行HTTP/1.1 200 OK,隨后一個必須的空行,和HTTP響應(yīng)body。
響應(yīng)狀態(tài)行TTP/1.1 200 OK包含了HTTP版本,HTTP狀態(tài)碼和HTTP狀態(tài)碼理由短語OK。瀏覽器得到響應(yīng)時,它就顯示響應(yīng)的body,所以你就看到了“Hello, World!”
這就是WEB瀏覽器怎么工作的基本模型??偨Y(jié)來說:WEB服務(wù)器創(chuàng)建一個監(jiān)聽socket然后開始循環(huán)接受新連接。客戶端初始化一個TCP連接,在連接成功后,客戶端發(fā)送HTTP請求到服務(wù)器,服務(wù)器響應(yīng)一個顯示給用戶的HTTP響應(yīng)。客戶端和服務(wù)器都使用socket建立TCP連接。
你現(xiàn)在你擁有了一個非?;A(chǔ)的WEB服務(wù)器,你可以用瀏覽器或其他的HTTP客戶端測試它。正如你看到的,使用telnet手動輸入HTTP請求,你也就成了一個人肉 HTTP 客戶端。
對你來說有一個問題:“怎樣在你的剛完成的WEB服務(wù)器下運行 Django 應(yīng)用、Flask 應(yīng)用和 Pyramid 應(yīng)用?在不單獨修改服務(wù)器來適應(yīng)這些不同的 WEB 框架的情況下?!?/p>
我會在本系列的第 2 部分秀給你看的。請保持關(guān)注哦。
順便說下,我在寫一本書《一起構(gòu)建WEB服務(wù)器:第一步》,它解釋了從零開始寫一個基本的WEB服務(wù)器,還更詳細地講解了我上面提到的話題。訂閱郵件組來獲取關(guān)于書籍和發(fā)布時間和最近更新。
靈感來自于 Lead with a Story: A Guide to Crafting Business Narratives That Captivate, Convince, and Inspire