基於 Python3
寫的極簡版 webserver
。用於學習 HTTP協議
,及 WEB服務器
工做原理。筆者對 WEB服務器
的工做原理理解的比較粗淺,僅是基於我的的理解來寫的,存在不少不足和漏洞,目的在於給你們提供一個寫 webserver
的思路。項目GitHub地址:https://github.com/hanrenguang/simple-webserver。html
學過計網的同窗應該都知道 HTTP協議
是在 TCP協議
之上實現的。瀏覽器與服務器之間的通訊首先是創建 TCP
鏈接,再進行請求和響應報文的傳輸。服務器是屬於被動的一方,當瀏覽器發起請求的時候,服務器才能和瀏覽器通訊,在此以前,服務器都處於一個等待監聽的狀態。git
實現服務器的第一步是創建一個 socket
鏈接,socket
套接字是對 TCP/UDP協議
的一個封裝,Python
就自帶有 socket
模塊,因此使用起來很方便。github
import socket sk = socket.socket( socket.AF_INET, socket.SOCK_STREAM ) # 監聽本地 8888 端口 host = '127.0.0.1' port = 8888 sk.bind((host, port)) sk.listen(5) while True: try: clientSk, addr = sk.accept() print("address is: %s" % str(addr)) req = clientSk.recv(1024) clientSk.sendall('...') clientSk.close() except Exception as err: print(err) clientSk.close()
這是一個極簡的 socket-server
,須要注意的是,咱們僅實現了 TCP協議
的部分。web
拿到瀏覽器的請求很簡單,clientSk.recv()
便可獲取請求報文,而些數據咱們沒法直接拿來用,由於它是基於 HTTP協議
封裝的數據,在咱們進行下一步操做前,須要對請求報文「解封」。而在此以前,咱們須要瞭解請求報文的格式。最快捷的方式呢,是打開瀏覽器(以 chrome
爲例),隨便打開百度啥的,F12
打開開發者工具,在 Network
一欄就能夠觀察到。大概長下面這樣:chrome
GET / HTTP/1.1 Host: xxx Connection: xxx Cache-Control: xxx Upgrade-Insecure-Requests: xxx User-Agent: xxx Accept: xxx Accept-Encoding: xxx Accept-Language: xxx Cookie: xxx
咱們把關注點放在第一行,GET
方法,請求的資源路徑爲 /
,使用的協議是 HTTP1.1
,以後就是一回車換行符 \r\n
。因此咱們對報文的解析以下(存在許多不足之處):瀏覽器
# 第一步先對數據進行解碼 decode(), # 再以行爲單位進行分割 requestList = clientSk.recv(1024).decode().split("\r\n") # 調用寫好的函數對其進行解析 parseReq(requestList) # 解析請求報文 def parseReq(reqList): # 保存解析結果 parseRet = {} # 請求的方法,如 GET method = reqList[0].split(' ')[0] # 請求的資源路徑,如 '/' sourcePath = reqList[0].split(' ')[1] parseRet['method'] = method parseRet['sourcePath'] = sourcePath i = len(reqList) - 1 # 以 key: value 的形式保存解析結果 while i: if len(reqList[i].split(':')) == 1: i = i - 1 continue idx = reqList[i].find(':') key, value = reqList[i][0:idx], reqList[i][idx+1:] parseRet[key] = value.strip() i = i - 1 return parseRet
拿到了請求報文並將其解析後,咱們能夠開始構造響應報文的內容了,以請求靜態資源爲例,假設請求報文第一行爲 GET /index.html HTTP/1.1
。那麼我首先要作的就是先獲取路徑爲 /index.html
的文件內容:bash
# 獲取資源內容 try: f = open(path, 'r') while True: chunk = f.read(1024) if not chunk: f.close() break; content += chunk except: pass
那接下來就是構造響應報文了,同理能夠觀察 HTTP
響應報文的格式,在此就不舉例了,直接上代碼:服務器
try: f = open(path, 'r') while True: chunk = f.read(1024) if not chunk: f.close() break; content += chunk except: pass # 省略了大部分頭部信息 headers = 'HTTP/1.1 200 OK\r\n' contentType = 'Content-Type: text/html; charset=utf-8\r\n' contentLen = 'Content-Length: ' + str(len(content)) + '\r\n' # 組合成響應報文 res res = headers + contentType + contentLen + '\r\n' + content # 編碼後發送給瀏覽器, # 至此,本次通訊結束 clientSk.sendall(res.encode(encoding='UTF-8')) clientSk.close()
到項目GitHub:https://github.com/hanrenguang/simple-webserver,下載本項目到本地,雙擊 server.py
,並訪問 http://localhost:8888/index.html,你應該會看到十分親切的 Hello world!
。socket