目錄
HTTP協議
1.HTTP協議簡介
HTTP協議中現如今廣泛使用的一個版本是HTTP 1.1。
HTTP是一個客戶端終端(使用者)和伺服器端(網站)請求和應答的標準。
HTTP協議是基於TCP/IP協議之上的應用層協議。
1.通常,由HTTP客戶端發起一個請求,建立一個到伺服器指定埠(預設是80埠)的TCP連線。
2.HTTP伺服器則在那個埠監聽客戶端的請求。
3.一旦收到請求,伺服器會向客戶端返回一個狀態,比如"HTTP/1.1 200 OK",以及返回的內容,如請求的檔案、錯誤訊息、或者其它資訊。
HTTP協議定義了兩件事:
1.Web客戶端如何從Web伺服器請求Web頁面
2.以及伺服器如何把Web頁面傳送給客戶端。
HTTP協議採用了請求/響應模型:
1.客戶端向伺服器傳送一個請求報文,請求報文包含請求的方法、URL、協議版本、請求頭部和請求資料。
2.伺服器以一個狀態行作為響應,響應的內容包括協議的版本、成功或者錯誤程式碼、伺服器資訊、響應頭部和響應資料。
以下是 HTTP 請求/響應的步驟:
1.客戶端連線到Web伺服器
一個HTTP客戶端,通常是瀏覽器,與Web伺服器的HTTP埠(預設為80)建立一個TCP套接字連線。
2.客戶端傳送HTTP請求
通過TCP套接字,客戶端向Web伺服器傳送一個文字的請求報文,一個請求報文由請求行、請求頭部、空行和請求資料4部分組成。
3.伺服器接受請求並返回HTTP響應
Web伺服器解析請求,定位請求資源。伺服器將資源複本寫到TCP套接字,由客戶端讀取。一個響應由狀態行、響應頭部、空行和響應資料4部分組成。
4.釋放連線TCP連線
若connection 模式為close,則伺服器主動關閉TCP連線,客戶端被動關閉連線,釋放TCP連線
若connection 模式為keepalive,則該連線會保持一段時間,在該時間內可以繼續接收請求;
5.客戶端瀏覽器解析HTML內容
1.客戶端瀏覽器首先解析狀態行,檢視錶明請求是否成功的狀態程式碼。
2.然後解析每一個響應頭,響應頭告知以下為若干位元組的HTML文件和文件的字符集。
3.客戶端瀏覽器讀取響應資料HTML,根據HTML的語法對其進行格式化,並在瀏覽器視窗中顯示。
例如:在瀏覽器位址列鍵入URL,按下回車之後會經歷以下流程:
1.瀏覽器向 DNS 伺服器請求解析該 URL 中的域名所對應的 IP 地址;
2.解析出 IP 地址後,根據該 IP 地址和預設埠 80,和伺服器建立TCP連線;
3.瀏覽器發出讀取檔案(URL 中域名後面部分對應的檔案)的HTTP 請求,該請求報文作為 TCP 三次握手的第三個報文的資料傳送給伺服器;
4.伺服器對瀏覽器請求作出響應,並把對應的 html 文字傳送給瀏覽器;
5.釋放 TCP連線;
6.瀏覽器將該 html 文字並顯示內容;
GET:讀取資料
POST:提交資料
-
GET提交的資料會放在URL之後,也就是請求行裡面,以?分割URL和傳輸資料,引數之間以&相連,如EditBook?name=test1&id=123456.(請求頭裡面那個content-type做的這種引數形式,後面講) POST方法是把提交的資料放在HTTP包的請求體中.
- GET提交的資料大小有限制(因為瀏覽器對URL的長度有限制),而POST方法提交的資料沒有限制.
所有HTTP響應的第一行都是狀態行,依次是當前HTTP版本號,3位數字組成的狀態程式碼,以及描述狀態的短語,彼此由空格分隔。
狀態程式碼的第一個數字代表當前響應的型別:
-
1xx訊息——請求已被伺服器接收,繼續處理
-
2xx成功——請求已成功被伺服器接收、理解、並接受
-
3xx重定向——需要後續操作才能完成這一請求
-
4xx請求錯誤——請求含有詞法錯誤或者無法被執行
-
5xx伺服器錯誤——伺服器在處理某個正確請求時發生錯誤
雖然 RFC 2616 中已經推薦了描述狀態的短語,例如"200 OK","404 Not Found",但是WEB開發者仍然能夠自行決定採用何種短語,用以顯示本地化的狀態描述或者自定義資訊。
超文字傳輸協議(HTTP)的統一資源定位符將從因特網獲取資訊的五個基本元素包括在一個簡單的地址中:
-
傳送協議。http
-
層級URL標記符號(為[//],固定不變) /
-
訪問資源需要的憑證資訊(可省略)無
-
伺服器。(通常為域名,有時為IP地址)www.luffycity.com
-
埠號。(以數字方式表示,若為HTTP的預設值“:80”可省略)80
-
路徑。(以“/”字元區別路徑中的每一個目錄名稱)/news/index.html
-
查詢。(GET模式的窗體引數,以“?”字元為起點,每個引數以“&”隔開,再以“=”分開引數名稱與資料,通常以UTF8的URL編碼,避開字元衝突的問題)?id=250&page=1
-
片段。以“#”字元為起點 無
上面是以http://www.luffycity.com:80/news/index.html?id=250&page=1 為例
URL包含:/index/index2?a=1&b=2;路徑和引數都在這裡。
請求頭裡面的內容舉個例子:這個length表示請求體裡面的資料長度,其他的請求頭裡面的這些鍵值對,知道一下就可以了,其中有一個user-agent,就是告訴你的服務端,我是用什麼給你發送的請求。
自定義web框架
第一版
import socket sk = socket.socket() IP_PORT = ('127.0.0.1',8001) sk.bind(IP_PORT) sk.listen() while True: conn,addr = sk.accept() from_browser_msg = conn.recv(1024) print(from_browser_msg) path = from_browser_msg.decode('utf-8').split(' ')[1] conn.send(b'HTTP/1.1 200 ok\r\na:1\r\n\r\n') if path == '/': with open('home.html','rb') as f: data = f.read() conn.send(data) conn.close() elif path == '/test.ico': with open('test.ico', 'rb') as f: data = f.read() conn.send(data) conn.close() elif path == '/test.png': with open('test.png', 'rb') as f: data = f.read() conn.send(data) conn.close() elif path == '/test.js': with open('test.js', 'rb') as f: data = f.read() conn.send(data) conn.close() elif path == '/test.css': with open('test.css', 'rb') as f: data = f.read() conn.send(data) conn.close()
第二版(函式版)
import socket server = socket.socket() TP_PORT = ('127.0.0.1', 8001) server.bind(TP_PORT) server.listen() def html(conn): with open('home.html', 'rb') as f: data = f.read() conn.send(data) conn.close() def png(conn): with open('test.png', 'rb') as f: data = f.read() conn.send(data) conn.close() def css(conn): with open('test.css', 'rb') as f: data = f.read() conn.send(data) conn.close() def js(conn): with open('test.js', 'rb') as f: data = f.read() conn.send(data) conn.close() def ico(conn): with open('test.ico', 'rb') as f: data = f.read() conn.send(data) conn.close() while 1: conn, addr = server.accept() from_browser_msg = conn.recv(1024) print(from_browser_msg) path = from_browser_msg.decode('utf-8').split(' ')[1] conn.send(b'HTTP/1.1 200 ok\r\na:1\r\n\r\n') if path == '/': html(conn) elif path == '/test.ico': ico(conn) elif path == '/test.jpg': png(conn) elif path == '/test.js': js(conn) elif path == '/test.css': css(conn)
第三版(程式版)
import socket from threading import Thread server = socket.socket() TP_PORT = ('127.0.0.1', 8001) server.bind(TP_PORT) server.listen() def html(conn): with open('home.html', 'rb') as f: data = f.read() conn.send(data) conn.close() def png(conn): with open('test.png', 'rb') as f: data = f.read() conn.send(data) conn.close() def css(conn): with open('test.css', 'rb') as f: data = f.read() conn.send(data) conn.close() def js(conn): with open('test.js', 'rb') as f: data = f.read() conn.send(data) conn.close() def ico(conn): with open('test.ico', 'rb') as f: data = f.read() conn.send(data) conn.close() urlpatterns = [ ('/', html), ('/test.ico', ico), ('/test.css', css), ('/test.js', js), ('/test.png', png), ] while 1: conn, addr = server.accept() from_browser_msg = conn.recv(1024) print(from_browser_msg) path = from_browser_msg.decode('utf-8').split(' ')[1] conn.send(b'HTTP/1.1 200 ok\r\na:1\r\n\r\n') for url in urlpatterns: if url[0] == path: t = Thread(target=url[1],args=(conn,)) t.start()