自己動手開發網路伺服器(一)
這個譯文共分為三個部分,內容比較長,介紹瞭如何利用 Python 語言從頭開發一個網路伺服器,其中涉及瞭如何讓伺服器同時支援 Flask 、 Pyramid 等多種框架,以及讓伺服器同時處理多個客戶端請求等問題。我覺得對於理解網路伺服器的實現有較大的幫助。譯文如果有什麼不對的地方,麻煩大家指正。
有一天,一位女士散步時經過一個工地,看見有三個工人在幹活。她問第一個人,“你在做什麼?”第一個人有點不高興,吼道“難道你看不出來我在砌磚嗎?”女士對這個答案並不滿意,接著問第二個人他在做什麼。第二個人回答道,“我正在建造一堵磚牆。”然後,他轉向第一個人,說道:“嘿,你砌的磚已經超過牆高了。你得把最後一塊磚拿下來。”女士對這個答案還是不滿意,她接著問第三個人他在做什麼。第三個人抬頭看著天空,對她說:“我在建造這個世界上有史以來最大的教堂”。就在他望著天空出神的時候,另外兩個人已經開始爭吵多出的那塊磚。他慢慢轉向前兩個人,說道:“兄弟們,別管那塊磚了。這是一堵內牆,之後還會被刷上石灰的,沒人會注意到這塊磚。接著砌下層吧。”
這個故事的寓意在於,當你掌握了整個系統的設計,明白不同的元件是以何種方式組合在一起的(磚塊,牆,教堂)時候,你就能夠更快地發現並解決問題(多出的磚塊)。
但是,這個故事與從頭開發一個網路伺服器有什麼關係呢?
在我看來,要成為一名更優秀的程式設計師,你必須更好地理解自己日常使用的軟體系統,而這就包括了程式語言、編譯器、直譯器、資料庫與作業系統、網路伺服器和網路開發框架。而要想更好、更深刻地理解這些系統,你必須從頭重新開發這些系統,一步一個腳印地重來一遍。
孔子曰:不聞不若聞之,聞之不若見之,見之不若知之,知之不若行之。
不聞不若聞之
聞之不若見之
見之不若知之,知之不若行之。
譯者注:上面原作者所引用的那段話在國外的翻譯是:I hear and I forget, I see and I remember, I do and I understand。外國人普遍認為出自孔子,但在查詢這句英文的出處時,查到有篇博文稱這句話的中文實際出自荀子的《儒效篇》,經查確實如此。
我希望你讀到這裡的時候,已經認可了通過重新開發不同軟體系統來學習其原理這種方式。
《自己動手開發網路伺服器》會分為三個部分,將介紹如何從頭開發一個簡易網路伺服器。我們這就開始吧。
首先,到底什麼是網路伺服器?
簡而言之,它是在物理伺服器上搭建的一個網路連線伺服器(networking server),永久地等待客戶端傳送請求。當伺服器收到請求之後,它會生成響應並將其返回至客戶端。客戶端與伺服器之間的通訊,是以HTTP協議進行的。客戶端可以是瀏覽器,也可以是任何支援HTTP協議的軟體。
那麼,網路伺服器的簡單實現形式會是怎樣的呢?下面是我對此的理解。示例程式碼使用Python語言實現,不過即使你不懂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
,或者直接從我的Github倉庫下載,然後通過命令列執行該檔案:
$ python webserver1.py
Serving HTTP on port 8888 …
接下來,在瀏覽器的位址列輸入這個連結:http://localhost:8888/hello,然後按下Enter鍵,你就會看見神奇的一幕。在瀏覽器中,應該會出現“Hello, World!”這句話:
是不是很神奇?接下來,我們來分析背後的實現原理。
首先,我們來看你所輸入的網路地址。它的名字叫URL(Uniform Resource Locator,統一資源定位符),其基本結構如下:
通過URL,你告訴了瀏覽器它所需要發現並連線的網路伺服器地址,以及獲取伺服器上的頁面路徑。不過在瀏覽器傳送HTTP請求之前,它首先要與目標網路伺服器建立TCP連線。然後,瀏覽器再通過TCP連線傳送HTTP請求至伺服器,並等待伺服器返回HTTP響應。當瀏覽器收到響應的時候,就會在頁面上顯示響應的內容,而在上面的例子中,瀏覽器顯示的就是“Hello, World!”這句話。
那麼,在客戶端傳送請求、伺服器返回響應之前,二者究竟是如何建立起TCP連線的呢?要建立起TCP連線,伺服器和客戶端都使用了所謂的套接字(socket)。接下來,我們不直接使用瀏覽器,而是在命令列使用telnet
手動模擬瀏覽器。
在執行網路伺服器的同一臺電腦商,通過命令列開啟一次telnet
會話,將需要連線的主機設定為localhost
,主機的連線埠設定為8888
,然後按Enter鍵:
$ telnet localhost 8888
Trying 127.0.0.1 …
Connected to localhost.
完成這些操作之後,你其實已經與本地執行的網路伺服器建立了TCP連線,隨時可以傳送和接收HTTP資訊。在下面這張圖片裡,展示的是伺服器接受新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!
你成功地手動模擬了瀏覽器!你手動傳送了一條HTTP請求,然後收到了HTTP響應。下面這幅圖展示的是HTTP請求的基本結構:
HTTP請求行包括了HTTP方法(這裡使用的是GET
方法,因為我們希望從伺服器獲取內容),伺服器頁面路徑(/hello
)以及HTTP協議的版本。
為了儘量簡化,我們目前實現的網路伺服器並不會解析上面的請求,你完全可以輸入一些沒有任何意義的程式碼,也一樣可以收到"Hello, World!"響應。
在你輸入請求程式碼並按下Enter鍵之後,客戶端就將該請求傳送至伺服器了,伺服器則會解析你傳送的請求,並返回相應的HTTP響應。
下面這張圖顯示的是伺服器返回至客戶端的HTTP響應詳情:
我們來分析一下。響應中包含了狀態行HTTP/1.1 200 OK
,之後是必須的空行,然後是HTTP響應的正文。
響應的狀態行HTTP/1.1 200 OK
中,包含了HTTP版本、HTTP狀態碼以及與狀態碼相對應的原因短語(Reason Phrase)。瀏覽器收到響應之後,會顯示響應的正文,這就是為什麼你會在瀏覽器中看到“Hello, World!”這句話。
這就是網路伺服器基本的工作原理了。簡單回顧一下:網路伺服器首先建立一個偵聽套接字(listening socket),並開啟一個永續迴圈接收新連線;客戶端啟動一個與伺服器的TCP連線,成功建立連線之後,向伺服器傳送HTTP請求,之後伺服器返回HTTP響應。要建立TCP連線,客戶端和伺服器都使用了套接字。
現在,你已經擁有了一個基本可用的簡易網路伺服器,你可以使用瀏覽器或其他HTTP客戶端進行測試。正如上文所展示的,通過telnet
命令並手動輸入HTTP請求,你自己也可以成為一個HTTP客戶端。
下面給大家佈置一道思考題:如何在不對伺服器程式碼作任何修改的情況下,通過該伺服器執行Djando應用、Flask應用和Pyramid應用,同時滿足這些不同網路框架的要求?
答案將在《自己動手開發網路伺服器》系列文章的第二部分揭曉。
相關文章
- 自己動手開發一個 Web 伺服器(一)Web伺服器
- 自己動手開發一個 Web 伺服器(三)Web伺服器
- 自己動手開發一個 Web 伺服器(二)Web伺服器
- 全網唯一:移動網際網路伺服器端開發!伺服器
- 移動網際網路伺服器端開發伺服器
- 自己動手實現神經網路分詞模型神經網路分詞模型
- 自己動手作PPPOE伺服器(二)伺服器
- 自己動手開發一個Android持續整合工具-簡介Android
- 如何自己開發一個腳手架工具
- SAP成都研究院安德魯:自己動手開發一個Chrome ExtensionChrome
- 自己動手開發一個Android持續整合工具-關於TaskAndroid
- 自己動手開發一個Android持續整合工具-準備工作Android
- 【自己動手寫神經網路】---人人都可以學的神經網路書神經網路
- 自己動手寫一個 SimpleVueVue
- 自己動手寫 PHP 框架(一)PHP框架
- 找人做網站不如自己動手網站
- 手機一代 撬動網際網路版圖 95後影響移動網際網路發展
- 手動搭建自己的nuget伺服器及使用伺服器
- 自己公司網站開發網站
- RxJava:自己動手擼一個RxBinding(一)。RxJava
- 手動開發一個日曆元件元件
- 如何開發自己的 yeoman 腳手架
- 自己動手實現一個前端路由前端路由
- 自己動手寫basic直譯器 一
- 自己動手寫一個持久層框架框架
- 自己動手搞一個tip 外掛
- 自己動手實現一個EventBus框架框架
- 自己動手實現一個Unix Shell
- 自己動手寫PromisePromise
- 大公司爭相開源 AI 專案,網際網路公司聯手發起開源運動AI
- 自己動手實現一個阻塞佇列佇列
- RxJava:自己動手擼一個RxBinding(二)。RxJava
- 自己動手製作人工神經網路0x1:初始化部分神經網路
- 手動自己寫了一個波場(Tron)本地網頁版錢包網頁
- 【機器學習】動手寫一個全連線神經網路(一)機器學習神經網路
- 如何自己寫一個網路爬蟲爬蟲
- 開發一個自己的 CSS 框架(一)CSS框架
- 《PHP與MySQL動態網站開發》小編手記PHPMySql網站