超文本傳輸協議(hyper Text Transfer Protocol :HTTP),是一種用於分佈式,協做式和超媒體信息系統的應用層協議.html
HTTP是萬維網的數據通訊的基礎,http的有不少的應用,但最著名的是用於web瀏覽器和web服務區之間的雙工通訊(雙工:即雙通道)python
http協議使用最普遍的一個版本 --HTTP1.1 , 2015年http2.0版本發佈web
# 1. Http是一個客戶端 和 服務端 ,請求和應答的標準. # 2. 客戶端發起一個http請求到服務端,服務端向客戶端返回一些存放在服務器上的資源. # 3. http協議存在'應用層',http須要可靠的傳輸,http是基於TCP/IP傳輸之上的協議
由HTTP客戶端發起一個請求,建立一個到服務器指定端口(默認是80端口)的TCP鏈接。HTTP服務器則在那個端口監聽客戶端的請求。一旦收到請求,服務器會向客戶端返回一個狀態,好比"HTTP/1.1 200 OK",以及返回的內容,如請求的文件、錯誤消息、或者其它信息。django
1.client端鏈接到WebServer端(創建一個基於tcp的套接字鏈接)瀏覽器
2.客戶端發起http請求(經過tcp套接字,向服務端發送一個文本的請求報文.報文包含:請求行,請求頭,空行和請求數據)服務器
3.Web服務器接收請求並返回HTTP響應(web服務器解析請求,定位請求資源. 經過tcp套接字向客戶端發送響應,響應包含:響應狀態行,響應頭部,空行和響應數據)網絡
4.釋放tcp鏈接app
# 若connection 模式爲close,則服務器主動關閉TCP鏈接,客戶端被動關閉鏈接,釋放TCP鏈接;若connection 模式爲keepalive,則該鏈接會保持一段時間,在該時間內能夠繼續接收請求;
5.客戶端瀏覽器解析html數據框架
# 客戶端瀏覽器首先解析狀態行,查看代表請求是否成功的狀態代碼。而後解析每個響應頭,響應頭告知如下爲若干字節的HTML文檔和文檔的字符集。客戶端瀏覽器讀取響應數據HTML,根據HTML的語法對其進行格式化,並在瀏覽器窗口中顯示
瀏覽器 url請求經歷的事情socket
### 瀏覽器 url請求經歷的事情 # 1. 瀏覽器向 DNS 服務器請求解析該 URL 中的域名所對應的 IP 地址; # 2. 解析出 IP 地址後,根據該 IP 地址和默認端口 80,和服務器創建TCP鏈接; # 3. 瀏覽器發出讀取文件(URL 中域名後面部分對應的文件)的HTTP 請求,該請求報文做爲 TCP 三次握手的第三個報文的數據發送給服務器; # 4. 服務器對瀏覽器請求做出響應,並把對應的 html 文本發送給瀏覽器; # 5. 釋放 TCP鏈接; # 6. 瀏覽器將該 html 文本內容顯示;
http/1.1協議定義了八種方法,最經常使用post和get方法
# 1. get 獲取數據 # 2. post 提交數據,數據封裝在請求體裏面 # 3. head 與GET方法同樣,都是向服務器發出指定資源的請求。只不過服務器將不傳回資源的本文部分。它的好處在於,使用這個方法能夠在沒必要傳輸所有內容的狀況下,就能夠獲取其中「關於該資源的信息」(元信息或稱元數據)。 # 4. put 向指定資源位置上傳其最新內容。 # 5. delete 請求服務器刪除Request-URI所標識的資源 # 6. trace 回顯服務器收到的請求,主要用於測試或診斷 # 7. options 這個方法可以使服務器傳回該資源所支持的全部HTTP請求方法。用'*'來代替資源名稱,向Web服務器發送OPTIONS請求,能夠測試服務器功能是否正常運做。 # 8. connect HTTP/1.1協議中預留給可以將鏈接改成管道方式的代理服務器。一般用於SSL加密服務器的連接(經由非加密的HTTP代理服務器) 注意事項: I. 方法名稱是區分大小寫的。當某個請求所針對的資源不支持對應的請求方法的時候,服務器應當返回狀態碼405(Method Not Allowed),當服務器不認識或者不支持對應的請求方法的時候,應當返回狀態碼501(Not Implemented)。 II. HTTP服務器至少應該實現GET和HEAD方法,其餘方法都是可選的。固然,全部的方法支持的實現都應當匹配下述的方法各自的語義定義。此外,除了上述方法,特定的HTTP服務器還可以擴展自定義的方法。例如PATCH(由 RFC 5789 指定的方法)用於將局部修改應用到資源。
http響應的第一行都是狀態行,3位數字組成的狀態碼.狀態碼和狀態描述端語由空格分隔
# 1XX信息---請求已被服務器接收 # 2XX成功---請求成功,請求已被服務器接收,解析,並響應 # 3XX重定向---從新請求別的url # 4XX請求錯誤---請求含有錯誤,或資源不存在. # 5XX服務器錯誤---服務器處理錯誤,或者是服務宕機,網速慢等影響
統一資源定位符
# 1. 傳送協議。 # 2. 層級URL標記符號(爲[//],固定不變) # 3. 訪問資源須要的憑證信息(可省略) # 4. 服務器。(一般爲域名,有時爲IP地址) # 5. 端口號。(以數字方式表示,若爲HTTP的默認值「:80」可省略) # 6. 路徑。(以「/」字符區別路徑中的每個目錄名稱) # 7. 查詢。(GET模式的窗體參數,以「?」字符爲起點,每一個參數以「&」隔開,再以「=」分開參數名稱與數據,一般以UTF8的URL編碼,避開字符衝突的問題) # 8. 片斷。以「#」字符爲起點
例子
// http://www.baidu.com:80/news/index.html?id=250&page=1 爲例, 其中: http,是協議; www.baidu.com,是服務器; 80,是服務器上的網絡端口號; /news/index.html,是路徑; ?id=250&page=1,是查詢。
1.cmd安裝
# 使用 pip 安裝 pip3 install django==1.11.23 -i https://pypi.tuna.tsinghua.edu.cn/simple
2.pycharm
# 1. 選擇File # 2. 選擇settings配置 # 3. 選擇 Project: PythonDjango, # 4. 選擇 Project Interpreter----> 選中使用的解釋器---->點擊 '+' 號 ---->搜索django,specify version 選擇版本---->點擊Install Package
1.cmd
# 1. 在某文件下,進入cmd命令行 # 2. 輸入: django-admin startproject 項目名 , 如:django-admin startproject mySite # 3. 建立項目成功,進入mySite # 4. 啓動項目: python manage.py runserver ip 端口 如: >python manage.py runserver 192.168.XXX.XXX:8001
2.pycharm
# 1. 選擇Django項目 # 2. 填寫此項目存在地址和項目名稱 # 3. 建立成功 # 4. 點擊綠色三角啓動按鈕
TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, "template")], #重點: template文件夾位置 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ]
# settings文件配置 STATIC_URL = '/static/' # HTML中使用的靜態文件夾前綴 STATICFILES_DIRS = [ os.path.join(BASE_DIR, "static"), # 靜態文件存放位置 ]
靜態資源配置過程以下圖:👇
# 導入語法 from django.shortcuts import HttpResponse, render, redirect # HttpReponse:這是一個類,把字符串參數傳入,實例化一個對象. def index(request): # 業務邏輯代碼 return HttpResponse("OK") # render:除request參數外還接受一個待渲染的模板文件和一個保存具體數據的字典參數 def index(request): # 業務邏輯代碼 return render(request, "index.html", {"name": "alex", "hobby": ["燙頭", "泡吧"]}) # redirect:接受一個URL參數,表示跳轉到指定的URL。 def index(request): # 業務邏輯代碼 return redirect("/home/")
全部的Web應用本質上就是一個socket服務端,而用戶的瀏覽器就是一個socket客戶端。 這樣咱們就能夠本身實現Web框架了。
## socket 服務端 import socket sk = socket.socket() sk.bind(("127.0.0.1", 80)) sk.listen() while True: conn, addr = sk.accept() data = conn.recv(8096) print(data) # 將瀏覽器發來的消息打印出來 conn.send(b"OK") conn.close() ### 輸出的結果 GET / HTTP/1.1 Host: 127.0.0.1:8080 Connection: keep-alive Cache-Control: max-age=0 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3355.4 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8 Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh;q=0.9 Cookie: csrftoken=CtHePYARJOKNx5oNVwxIteOJXpNyJ29L4bW4506YoVqFaIFFaHm0EWDZqKmw6Jm8
import socket sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.bind(('127.0.0.1', 8000)) sock.listen() while True: conn, addr = sock.accept() data = conn.recv(8096) # 給回覆的消息加上響應狀態行 conn.send(b"HTTP/1.1 200 OK\r\n\r\n") conn.send(b"OK") conn.close()
""" 根據URL中不一樣的路徑返回不一樣的內容 """ import socket sk = socket.socket() sk.bind(("127.0.0.1", 8080)) # 綁定IP和端口 sk.listen() # 監聽 while True: # 等待鏈接 conn, add = sk.accept() data = conn.recv(8096) # 接收客戶端發來的消息 # 從data中取到路徑 data = str(data, encoding="utf8") # 把收到的字節類型的數據轉換成字符串 # 按\r\n分割 data1 = data.split("\r\n")[0] url = data1.split()[1] # url是咱們從瀏覽器發過來的消息中分離出的訪問路徑 conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 由於要遵循HTTP協議,因此回覆的消息也要加狀態行 # 根據不一樣的路徑返回不一樣內容 if url == "/index/": response = b"index" elif url == "/home/": response = b"home" else: response = b"404 not found!" conn.send(response) conn.close()
""" 根據URL中不一樣的路徑返回不一樣的內容--函數版 """ import socket sk = socket.socket() sk.bind(("127.0.0.1", 8080)) # 綁定IP和端口 sk.listen() # 監聽 # 將返回不一樣的內容部分封裝成函數 def func(url): s = "這是{}頁面!".format(url) return bytes(s, encoding="utf8") while True: # 等待鏈接 conn, add = sk.accept() data = conn.recv(8096) # 接收客戶端發來的消息 # 從data中取到路徑 data = str(data, encoding="utf8") # 把收到的字節類型的數據轉換成字符串 # 按\r\n分割 data1 = data.split("\r\n")[0] url = data1.split()[1] # url是咱們從瀏覽器發過來的消息中分離出的訪問路徑 conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 由於要遵循HTTP協議,因此回覆的消息也要加狀態行 # 根據不一樣的路徑返回不一樣內容,response是具體的響應體 if url == "/index/": response = func(url) elif url == "/home/": response = func(url) else: response = b"404 not found!" conn.send(response) conn.close()
""" 根據URL中不一樣的路徑返回不一樣的內容--函數進階版 返回獨立的HTML頁面 """ import socket sk = socket.socket() sk.bind(("127.0.0.1", 8080)) # 綁定IP和端口 sk.listen() # 監聽 # 將返回不一樣的內容部分封裝成不一樣的函數 def index(url): # 讀取index.html頁面的內容 with open("index.html", "r", encoding="utf8") as f: s = f.read() # 返回字節數據 return bytes(s, encoding="utf8") def home(url): with open("home.html", "r", encoding="utf8") as f: s = f.read() return bytes(s, encoding="utf8") # 定義一個url和實際要執行的函數的對應關係 list1 = [ ("/index/", index), ("/home/", home), ] while True: # 等待鏈接 conn, add = sk.accept() data = conn.recv(8096) # 接收客戶端發來的消息 # 從data中取到路徑 data = str(data, encoding="utf8") # 把收到的字節類型的數據轉換成字符串 # 按\r\n分割 data1 = data.split("\r\n")[0] url = data1.split()[1] # url是咱們從瀏覽器發過來的消息中分離出的訪問路徑 conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 由於要遵循HTTP協議,因此回覆的消息也要加狀態行 # 根據不一樣的路徑返回不一樣內容 func = None # 定義一個保存將要執行的函數名的變量 for item in list1: if item[0] == url: func = item[1] break if func: response = func(url) else: response = b"404 not found!" # 返回具體的響應消息 conn.send(response) conn.close()
""" 根據URL中不一樣的路徑返回不一樣的內容--函數進階版 返回獨立的HTML頁面 """ import socket sk = socket.socket() sk.bind(("127.0.0.1", 8080)) # 綁定IP和端口 sk.listen() # 監聽 # 將返回不一樣的內容部分封裝成不一樣的函數 def index(url): # 讀取index.html頁面的內容 with open("index.html", "r", encoding="utf8") as f: s = f.read() # 返回字節數據 return bytes(s, encoding="utf8") def home(url): with open("home.html", "r", encoding="utf8") as f: s = f.read() return bytes(s, encoding="utf8") def timer(url): import time with open("time.html", "r", encoding="utf8") as f: s = f.read() s = s.replace('@@time@@', time.strftime("%Y-%m-%d %H:%M:%S")) return bytes(s, encoding="utf8") # 定義一個url和實際要執行的函數的對應關係 list1 = [ ("/index/", index), ("/home/", home), ("/time/", timer), ] while True: # 等待鏈接 conn, add = sk.accept() data = conn.recv(8096) # 接收客戶端發來的消息 # 從data中取到路徑 data = str(data, encoding="utf8") # 把收到的字節類型的數據轉換成字符串 # 按\r\n分割 data1 = data.split("\r\n")[0] url = data1.split()[1] # url是咱們從瀏覽器發過來的消息中分離出的訪問路徑 conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 由於要遵循HTTP協議,因此回覆的消息也要加狀態行 # 根據不一樣的路徑返回不一樣內容 func = None # 定義一個保存將要執行的函數名的變量 for item in list1: if item[0] == url: func = item[1] break if func: response = func(url) else: response = b"404 not found!" # 返回具體的響應消息 conn.send(response) conn.close()
python web程序來講,通常會分爲兩部分:服務器程序和應用程序。
服務器程序負責對socket服務端進行封裝,並在請求到來時,對請求的各類數據進行整理。 應用程序則負責具體的邏輯處理。爲了方便應用程序的開發,就出現了衆多的Web框架,例如:Django、Flask、web.py 等。不一樣的框架有不一樣的開發方式,可是不管如何,開發出的應用程序都要和服務器程序配合,才能爲用戶提供服務。
WSGI(Web Server Gateway Interface)就是一種規範,它定義了使用Python編寫的web應用程序與web服務器程序之間的接口格式,實現web應用程序與web服務器程序間的解耦。
經常使用的WSGI服務器有uWSGI、Gunicorn。而Python標準庫提供的獨立WSGI服務器叫wsgiref,Django開發環境用的就是這個模塊來作服務器。
""" 根據URL中不一樣的路徑返回不一樣的內容--函數進階版 返回HTML頁面 讓網頁動態起來 wsgiref模塊版 """ from wsgiref.simple_server import make_server # 將返回不一樣的內容部分封裝成函數 def index(url): # 讀取index.html頁面的內容 with open("index.html", "r", encoding="utf8") as f: s = f.read() # 返回字節數據 return bytes(s, encoding="utf8") def home(url): with open("home.html", "r", encoding="utf8") as f: s = f.read() return bytes(s, encoding="utf8") def timer(url): import time with open("time.html", "r", encoding="utf8") as f: s = f.read() s = s.replace('@@time@@', time.strftime("%Y-%m-%d %H:%M:%S")) return bytes(s, encoding="utf8") # 定義一個url和實際要執行的函數的對應關係 list1 = [ ("/index/", index), ("/home/", home), ("/time/", timer), ] def run_server(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ]) # 設置HTTP響應的狀態碼和頭信息 url = environ['PATH_INFO'] # 取到用戶輸入的url func = None for i in list1: if i[0] == url: func = i[1] break if func: response = func(url) else: response = b"404 not found!" return [response, ] if __name__ == '__main__': httpd = make_server('127.0.0.1', 8090, run_server) print("我在8090等你哦...") httpd.serve_forever()
HTML模板渲染數據,本質上就是HTML內容中利用一些特殊的符號來替換要展現的數據
安裝:pip install jinja2
// index2.html頁面 <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta http-equiv="x-ua-compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Title</title> </head> <body> <h1>姓名:{{name}}</h1> <h1>愛好:</h1> <ul> {% for hobby in hobby_list %} <li>{{hobby}}</li> {% endfor %} </ul> </body> </html>
from wsgiref.simple_server import make_server from jinja2 import Template def index(url): # 讀取HTML文件內容 with open("index2.html", "r", encoding="utf8") as f: data = f.read() template = Template(data) # 生成模板文件 ret = template.render({'name': 'alex', 'hobby_list': ['抽菸', '喝酒', '燙頭']}) # 把數據填充到模板中 return bytes(ret, encoding="utf8") def home(url): with open("home.html", "r", encoding="utf8") as f: s = f.read() return bytes(s, encoding="utf8") # 定義一個url和實際要執行的函數的對應關係 list1 = [ ("/index/", index), ("/home/", home), ] def run_server(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ]) # 設置HTTP響應的狀態碼和頭信息 url = environ['PATH_INFO'] # 取到用戶輸入的url func = None for i in list1: if i[0] == url: func = i[1] break if func: response = func(url) else: response = b"404 not found!" return [response, ] if __name__ == '__main__': httpd = make_server('127.0.0.1', 8090, run_server) print("我在8090等你哦...") httpd.serve_forever()
模板的原理就是字符串替換,咱們只要在HTML頁面中遵循jinja2的語法規則寫上,其內部就會按照指定的語法進行相應的替換,從而達到動態的返回內容。