http
是一個應用層協議,準確的來說是基於TCP/IP
4層網路協定中的傳輸層中的TCP
應用層協定。
額,4層模型大概是這樣的:
在網路通訊中,使用者的資料是以封包來傳送的,但是在實際通訊中,每一層都會對包進行封裝,從而形成段、數數據報、幀,最後是以比特流(二進制)進行傳輸,到了目標主機後,會對每一層又進行拆解,從而得到最後的報文。
http
就在最上層,就是應用層那裡。
http
到底離我們多近呢?甚至於你現在看到的文章,使用的就是http
協議,它還有一個名字,叫做超文本傳輸協議,為什麼叫超文本呢?因為最開始的是時候,http
是被用來傳輸Hypertext
文檔的,所以被叫做超文本協議,當然現在可以傳輸其他類型的數據,如: 視頻、音頻、圖片等,但是它依然被稱之為超文本協議。
很難理解吧,沒關係,繼續往下看。
透過上面的簡介,我們知道http
是應用程式層協議,它在網路協定中,被稱為之為報文的,讓我們來看看http
的請求封包和回應封包吧。
http
封包由4部分組成,分別是起始行、首部行、空白行 以及 實體組成。以\r\n
(也稱為CRLF
)進行分割。
讓我們來看一下實際的報文呢。
在linux
中,我們可以使用curl -v 網址
來列印詳細的請求信息,其中就包括了報文。
指令:
curl -v http://juejin.cn
請求訊息:
#其中輸出的結果中>
代表我們發出的報文,而代表伺服器發送給我們的回應訊息。下面我們將結合報文來看上面的數據資訊。
請求封包格式如下:
其中請求行會指定http
的請求方法,如: GET
、POST
、HEAD
等,URL
則是要求的檔案路徑,協定版本需要指定http
的版本,最後是以CRLF
結束。
首部行可以有多個,以 (字段名: 值) 的方式出現,每一個首部行同樣以CRLF
結束。
而後是空白行。空白行則代表http
封包頭結束了,接下來該是發送的封包主體了,接下來,我們將上述請求http://juejin.cn
的例子,填入表格來看下:
上述是我們使用curl
工具請求的http://juejin.cn
請求封包整體形式,我們可以看到,我們使用了GET
方法,取得伺服器的/
訊息,使用的協定是HTTP/1.1
,而後攜帶了3個首部行,分別是User-Agent
、Host
以及Accept
。
回應封包格式如下:
將回應訊息和請求訊息進行對比,我們不難發現,除了第一行以外,其他的格式都是一樣的,所以,我們只介紹響應行的信息,在響應報行中,第一個是協議的版本,這個是伺服器的協議版本,而後便是狀態碼,用於告知客戶端,伺服器回應的訊息,最後是短語,短語的作用是告知使用者,這個回傳訊息大概是什麼意思。
好了,我們將上述juejin.cn
回應給我們的報文,我們填到表格中呢:
上述是我們使用curl
請求http://juejin.cn/
,伺服器傳回的訊息,我們逐行來看下,回應行,告知了我們http
版本是HTTP/1.1
,狀態碼是301
,短語是連結被轉移了。
上述我們若僅透過狀態碼的話,是很難get
到整個報文的意思的,不過有短語,就可以猜一下了。
首部行,告知了我们服务器 、时间 、 报文类型 以及 报文长度。还记得我们第一段落介绍过得,http
现在除了发送超文本以外,还可以发送图片、视频等,就是通过首部行Content-Type
来确定的。
接着是空白行,最后是报文主体,哎,有没有感觉奇怪呢?为什么请求报文主体是空的呢?这是因为报文主体长度是由首部行Content-Length
来定义的,如上报文展示的是,我们报文主体有262个字符。
上述,我们介绍了,什么是http
以及初略的看了一下 http
的请求报文和响应报文,那么,我们如何构建一个http
服务器呢?
我们知道,http
是应用层协议,是基于传输层tcp
来实现的,所以,我们若想构建一个http
服务器,那么应该写一个socket
程序出来吧。
import socket import threading def handle(client , addr): print("from " , addr) data = client.recv(1024) for k,v in enumerate(data.decode().split("\r\n")): print(k ,v) def main(): s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.bind(("127.0.0.1",8080)) s.listen() while True: client , addr = s.accept() t = threading.Thread(target=handle,args=(client,addr)) t.start() if __name__ == '__main__' main()
上述,我们写了一个tcp
程序,它将监听本地回环地址的8080
端口,若此时我们使用curl -v 127.0.0.1:8080
请求一下该接口,我们将会得到请求报文了,如下:
我们得到请求报文后,可以构建一个响应报文发送回去,例如: Hello, Destined Person.
,我们就可以这样来构建http
import socket import threading def handle(client , addr): print("from " , addr) data = client.recv(1024) #请求报文 for k,v in enumerate(data. decode() .split("\r\n")): print(k ,v) bodyText = "He1lo,Destined Person." #响应报文 #响应行 client.send(b"HTTP/1.1 200 OK\r\n") #首部行 client. send(b"Server: pdudo_web_sites\r\n") client. send(b"Content-Type: text/html\r\n") client. send(("Content-Length: %s\r\n" % (len(bodyText) + 2)).encode()) client. send(b"\r\n") client. send(("%s\r\n" %(bodyText)).encode()) def main(): try: s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) s .bind(("127.0.0.1"8080)) s .listen() while True: client,addr = s.accept() t = threading.Thread(target=handle,args=(client,addr)) t.start() finally: s.close() if __name__ == '__main__': main()
最后我们使用curl
再来测试一下,是可以得到消息的。
以上是如何使用Python寫一個簡單的HTTP伺服器?的詳細內容。更多資訊請關注PHP中文網其他相關文章!