4.1.1 協議概述
用途 : 網頁獲取,數據的傳輸
特點
應用層協議,使用tcp進行數據傳輸
簡單,靈活,很多語言都有HTTP專門接口
有豐富的請求類型
可以傳輸的數據類型眾多
4.1.2 網頁訪問流程
客戶端(浏覽器)通過tcp傳輸,發送http請求給服務端
服務端接收到http請求後進行解析
服務端處理請求內容,組織響應內容
服務端將響應內容以http響應格式發送給浏覽器
浏覽器接收到響應內容,解析展示
4.1.2 HTTP請求
請求行 : 具體的請求類別和請求內容
GET / HTTP/1.1 請求類別 請求內容 協議版本
請求類別:每個請求類別表示要做不同的事情
GET : 獲取網絡資源 POST :提交一定的信息,得到反饋 HEAD : 只獲取網絡資源的響應頭 PUT : 更新服務器資源 DELETE : 刪除服務器資源
請求頭:對請求的進一步解釋和描述(例如什麼時間用什麼浏覽器發給服務器的)
eg.Accept-Encoding: gzip 一行行鍵值對構成
空行
請求體: 請求參數或者提交內容
4.1.3 HTTP響應
響應行 : 反饋基本的響應情況
HTTP/1.1 200 OK 版本信息 響應碼 附加信息
響應碼 :
1xx 提示信息,表示請求被接收 2xx 響應成功 3xx 響應需要進一步操作,即二次處理重定向 4xx 客戶端錯誤 5xx 服務器錯誤
響應頭:對響應內容的描述 ----詳細解釋給你的響應是什麼格式,大小等信息
eg.Content-Type: text/html ---以網頁格式解析
空行
響應體:響應的主體內容信息
""" http 請求和響應 """ from socket import * # 創建tcp套接字 sock = socket() sock.bind(("0.0.0.0", 8888)) sock.listen(5) # 等待浏覽器連接 connfd, addr = sock.accept() print("Connect from", addr) # 接收HTTP請求 request = connfd.recv(1024) print(request.decode()) # 組織響應格式 response = """HTTP/1.1 200 OK Content-Type:text/html Hello World """ connfd.send(response.encode()) # 發送響應 connfd.close() sock.close()
""" 練習: 根據所學http協議完成 通過浏覽器訪問服務端,請求根 則會 顯示一張圖片在浏覽器上 ,圖片自選 Content-Type:image/jpeg """ from socket import * def get_response(filename): response = "HTTP/1.1 200 OK\r\n" response += "Content-Type:image/jpeg\r\n" response += "\r\n" with open(filename, 'rb') as file: response = response.encode() + file.read() return response # 字節串 def main(): sock = socket() sock.bind(("0.0.0.0", 8000)) sock.listen(5) while True:#浏覽器可以多個頁面循環訪問 connfd, addr = sock.accept() print("Connect from", addr) # 接收請求 request = connfd.recv(1024) print(request.decode()) response = get_response("yaya.jpeg")#前端工程師提供圖像信息 connfd.send(response) # 字節串 if __name__ == '__main__': main()
主要功能 : 【1】 接收客戶端(浏覽器)請求
【2】 解析客戶端發送的請求
【3】 根據請求組織數據內容
【4】 將數據內容形成http響應格式返回給浏覽器
主要特點 :
【1】 采用IO並發,可以滿足多個客戶端同時發起請求情況
【2】 通過類接口形式進行功能封裝
【3】 做基本的請求解析,根據具體請求返回具體內容,同時可以滿足客戶端的網頁效果加載
""" web sever 服務 """ from socket import * from select import select import os # 具體處理http請求 class Handle: def __init__(self, html): self.html = html def _response(self, status, filename): response = "HTTP/1.1 %s\r\n" % status response += "Content-Type:text/html\r\n" response += "\r\n" with open(filename, 'rb') as file: response = response.encode() + file.read() return response def _send_response(self, connfd, info): # 請求的是 首頁 還是 其他網頁 if info == '/': filename = self.html + "/index.html" else: filename = self.html + info # 判斷是否存在 True / False if os.path.exists(filename): data = self._response("200 OK", filename) else: data = self._response("404 Not Found", self.html + "/404.html") connfd.send(data) # 發送響應 def handle(self, connfd): # 接收HTTP請求 request = connfd.recv(1024).decode() if not request: return # 獲取請求內容 info = request.split(" ")[1] print('請求內容:', info) # 組織響應並發送 self._send_response(connfd, info) class WebServer: # 實例化對象過程中做好准備工作 def __init__(self, host='', port=0, html=None): self.host = host self.port = port self.address = (host, port) self.html = html # 用戶提供的網頁 self.rlist = [] self.handle = Handle(html) self.sock = self._create_socket() # 創建套接字 def _create_socket(self): sock = socket() sock.bind(self.address) sock.setblocking(False) # 非阻塞 return sock # 連接浏覽器 def _connect(self): connfd, addr = self.sock.accept() connfd.setblocking(False) self.rlist.append(connfd) # 增加關注 # start啟動函數過程中 搭建服務 IO並發模型 def start(self): self.sock.listen(5) print("Listen the port %d" % self.port) self.rlist.append(self.sock) # 初始關注監聽套接字 # 循環接收監控IO發生 while True: rs, ws, xs = select(self.rlist, [], []) for r in rs: if r is self.sock: self._connect() # 處理連接 else: try: self.handle.handle(r) # 處理http請求 except: pass self.rlist.remove(r) # 短連接場景,處理完即關閉 r.close() if __name__ == '__main__': # 先想一下怎麼用 # 什麼需要用戶決定: 服務器地址 內容 httpd = WebServer(host="0.0.0.0", port=8888, html="./static") httpd.start() # 啟動服務
衡量高並發的關鍵指標
響應時間(Response Time) : 接收請求後處理的時間
同時在線用戶數量:同時連接服務器的用戶的數量
每秒查詢率QPS(Query Per Second): 每秒接收請求的次數
每秒事務處理量TPS(Transaction Per Second):每秒處理請求的次數(包含接收,處理,響應)
吞吐量(Throughput): 響應時間+QPS+同時在線用戶數量
多大的並發量算是高並發
沒有最高,只要更高
比如在一個小公司可能QPS2000+就不錯了,在一個需要頻繁訪問的門戶網站可能要達到QPS5W+
C10K問題
早先服務器都是單純基於進程/線程模型的,新到來一個TCP連接,就需要分配1個進程(或者線程)。而進程占用操作系統資源多,一台機器無法創建很多進程。如果是C10K就要創建1萬個進程,那麼單機而言操作系統是無法承受的,這就是著名的C10k問題。創建的進程線程多了,數據拷貝頻繁, 進程/線程切換消耗大, 導致操作系統崩潰,這就是C10K問題的本質!
為了解決C10K問題,現在高並發的實現已經是一個更加綜合的架構藝術。涉及到進程線程編程,IO處理,數據庫處理,緩存,隊列,負載均衡等等,這些我們在後面的階段還會學習。此外還有硬件的設計,服務器集群的部署,服務器負載,網絡流量的處理等。
實際工作中,應對更龐大的任務場景,網絡並發模型的使用有時也並不單一。比如多進程網絡並發中每個進程再開辟線程,或者在每個進程中也可以使用多路復用的IO處理方法。
前情回顧
1. IO多路復用
利用系統內核的IO監控功能 ,哪個IO就緒就處理哪個IO
對象,形成多個IO可以 "同時" 處理多個IO
IO多路是單進程,資源占用少,但是只能處理簡單的IO操作
2. select epoll
select: 平台支持好,但是同時監控IO數量有限,效率沒有
epoll高
epoll : 效率高,支持同時監控IO數量多,但是只用於Linux
3. 使用IO多路復用實現網絡並發
4. HTTP 協議
應用層 tcp傳輸 簡單方便 請求類型多
HTTP協議使用: 傳輸數據 訪問網站
網頁訪問流程
練習: 根據所學http協議完成
通過浏覽器訪問服務端,請求根 則會
顯示一張圖片在浏覽器上 ,圖片自選
Content-Type:image/jpeg
webserver 模塊
需求分析
接收客戶端(浏覽器)請求
解析客戶端發送的請求
根據請求組織數據內容
將數據內容形成http響應格式返回給浏覽器
技術 : IO多路復用並發網絡 : select
TCP
模塊封裝 : 類
協議 : HTTP協議
類接口設計 :
socket : 生成對象,使用對象靈活的調用各種方法
組合,就會產生不同的功能效果
Process : 生成對象,調用方法啟動功能 (如果多
調用方法也是為主體功能服務的),能為用戶決定就幫他決定
不能替用戶決定需要用戶傳參
核心編程
1. 網絡編程
網絡基礎知識:
1. TCP/IP模型和OSI七層模型
2. 三次握手和四次揮手過程
3. tcp和udp的區別
4. http協議的特點和請求響應格式
socket 模塊
tcp套接字編程 (重點)
udp套接字編程 (重點)
2. 進程和線程
基礎概念
1. 進程與線程的特點和區別
2. 理解什麼是進程和進程狀態
多進程 (包括自定義進程類) 重點,難點
multiprocessing
多線程 (包括同步互斥方法) 重點
threading
GIL
3. IO模型
阻塞IO
非阻塞IO
IO多路復用 (重點,難點)
select
epoll
4. 網絡並發模型
多進程多線程並發 (重點)
IO多路復用並發 (重點)
5. 綜合代碼
學生管理系統 :
文件 正則 函數編程
群聊聊天室 :
udp 多進程 函數編程
通信協議請求 "總分結構"
ftp文件服務器
tcp 多線程並發模型 面向對象
通信協議響應 請求應答的模式
webserver
tcp IO多路復用並發模型 設計類接口
類的設計思路
<核心編程 第三版>
<圖解TCP/IP>
<圖解HTTP>
作業: 1. webserver自己寫一下
2. github.com
gitee.com