我們可以這樣理解:所有的Web應用本質上就是一個socket服務端,而用戶的瀏覽器就是一個socket客服端。
這樣我們就可以自己實現Web框架了:
我們可以這樣理解:所有的Web應用本質上就是一個socket服務端,而用戶的瀏覽器就是一個socket客服端。
這樣我們就可以自己實現Web框架了:
from socket import *
sk = socket(AF_INET, SOCK_STREAM)
sk.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
sk.bind(('127.0.0.1', 8080))
sk.listen()
while True:
conn, addr = sk.accept()
data = conn.recv(9000)
conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 響應狀態行
conn.send(b'Hello,world!')
conn.close()
可以說Web服務本質上都是在這十幾行代碼基礎上擴展出來的,這段代碼就是它們的祖宗。
用戶的瀏覽器一輸入網址,會給服務端發送數據,那瀏覽器會發送什么數據?怎么發?這個誰來定? 你這個網站是這個規定,他那個網站按照他那個規定,這互聯網還能玩么?
所以,必須有一個統一的規則,讓大家發送消息、接收消息的時候有個格式依據,不能隨便寫。
這個規則就是HTTP協議,以后瀏覽器發送請求信息也好,服務器回復響應信息也罷,都要按照這個規則來。
HTTP協議主要規定了客戶端和服務器之間的通信格式,那HTTP協議是怎么規定消息格式的呢?
讓我們首先打印下我們在服務端接收到的消息是什么:
from socket import *
sk = socket(AF_INET, SOCK_STREAM)
sk.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
sk.bind(('127.0.0.1', 8080))
sk.listen()
while True:
conn, addr = sk.accept()
data = conn.recv(9000)
print(data) # 將瀏覽器發來的消息打印出來
conn.send(b'HTTP/1.1 200 OK\r\n\r\n')
conn.send(b'Hello,world!')
conn.close()
輸出如下:
b’GET / HTTP/1.1\r\nHost: 127.0.0.1:8080\r\nConnection: keep-alive\r\nPragma: no-cache\r\nCache-Control: no-cache\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,/;q=0.8\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: zh-CN,zh;q=0.9,en;q=0.8\r\n\r\n’
然后我們再看一個我們訪問博客官網時瀏覽器收到的響應信息是什么,響應的相關信息可以在瀏覽器調試窗口的network標簽頁中看到:
我們發現收發的消息需要按照一定的格式來,這里就需要了解一下HTTP協議了。
每個HTTP請求和響應都遵循相同的格式,一個HTTP包含Header和Body兩部分,其中Body是可選的。HTTP響應的Header中有一個Content-Type表明響應的內容格式。如text/html表示HTML網頁.
HTTP GET請求的格式:
HTTP響應的格式:
思路:從請求相關數據里面拿到請求URL的路徑,然后拿路徑做一個判斷.
from socket import *
sk = socket(AF_INET, SOCK_STREAM)
sk.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
sk.bind(('127.0.0.1', 8080))
sk.listen()
while True:
conn, addr = sk.accept()
data = conn.recv(8096)
# 把收到的字節類型的數據轉換成字符串
data_str = str(data, encoding='UTF-8')
first_line = data_str.split('\r\n')[0] # 按\r\n分隔
url = first_line.split()[1] # 在按空格切割
conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 響應狀態行
if url == '/index':
response = 'index'
elif url == '/home':
response = 'home'
else:
response = '404 not found!'
conn.send(bytes(response, encoding='UTF-8'))
conn.close()
上面的代碼解決了對于不同URL路徑返回不同內容的需求。
但是問題又來了,如果有很多的路徑要判斷怎么辦呢?難道要挨個寫if判斷?
答案是否定的,我們有更聰明的辦法:
from socket import *
sk = socket(AF_INET, SOCK_STREAM)
sk.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
sk.bind(('127.0.0.1', 8080))
sk.listen()
# 將返回不同的內容部分封裝成函數
def index(url):
s = "這是%s頁面!" % url
return bytes(s, encoding='UTF-8')
def home(url):
s = "這是%s頁面!" % url
return bytes(s, encoding='UTF-8')
while True:
conn, addr = sk.accept()
data = conn.recv(8096)
# 把收到的字節類型的數據轉換成字符串
data_str = str(data, encoding='UTF-8')
first_line = data_str.split('\r\n')[0] # 按\r\n分隔
url = first_line.split()[1] # 在按空格切割
conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 響應狀態行
if url == '/index':
response = index(url)
elif url == '/home':
response = home(url)
else:
response = b'404 not found!'
conn.send(response)
conn.close()
看起來上面的代碼還是要挨個寫if判斷,怎么辦?
只要思想不滑坡,方法總比問題多,走著!
from socket import *
sk = socket(AF_INET, SOCK_STREAM)
sk.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
sk.bind(('127.0.0.1', 8080))
sk.listen()
# 將返回不同的內容部分封裝成函數
def index(url):
s = "這是%s頁面!" % url
return bytes(s, encoding='UTF-8')
def home(url):
s = "這是%s頁面!" % url
return bytes(s, encoding='UTF-8')
# 定義一個url和實際要執行的函數的對應關系
url_list = [
('/index', index),
('/home', home)
]
while True:
conn, addr = sk.accept()
data = conn.recv(8096)
# 把收到的字節類型的數據轉換成字符串
data_str = ?tr(data, encoding='UTF-8')
first_line = data_str.split('\r\n')[0] # 按\r\n分隔
url = first_line.split()[1] # 在按空格切割
conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 響應狀態行
# -------關鍵就在于這一坨代碼-------
func = None
for i in url_list:
if url == i[0]:
func = i[1]
break
response = func(url) if func else b'404 not found!'
conn.send(response)
conn.close()
完美解決了不同URL返回不同內容的問題。
但是我們不僅僅是想返回幾個字符串,我們想給瀏覽器返回完整的HTML內容,這又該怎么辦呢?接著走!
首先我們要知道:不管是什么內容,最后都是轉換成字節數據發送出去的.
那么,我們可以以二進制形式打開HTML文件,讀取文件的數據,然后再發送給瀏覽器.
from socket import *
sk = socket(AF_INET, SOCK_STREAM)
sk.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
sk.bind(('127.0.0.1', 8080))
sk.listen()
# 將返回不同的內容部分封裝成函數
def index(url):
with open('index.html', 'rb') as f: return f.read()
def home(url):
with open('home.html', 'rb') as f: return f.read()
# 定義一個url和實際要執行的函數的對應關系
url_list = [
('/index', index),
('/home', home)
]
while True:
# ------------ 建立連接 接收消息 ------------
conn, addr = sk.accept()
data = conn.recv(8096)
# ------------ 對客服端發來的消息做處理 ------------
# 把收到的字節類型的數據轉換成字符串
data_str = str(data, encoding='UTF-8')
first_line = data_str.split('\r\n')[0] # 按\r\n分隔
url = first_line.split()[1] # 在按空格切割
# ------------ 業務邏輯處理部分 ------------
func = None
for i in url_list:
if url == i[0]:
func = i[1]
break
response = func(url) if func else b'404 not found!'
# ----------- 回復響應消息 -----------
conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 響應狀態行
conn.send(response)
conn.close()
沒錯,還有問題!
這網頁是能夠顯示出來了,但都是靜態的啊,頁面的內容不會變化,我們想要的是動態網站!
思路:使用字符串替換功能來實現這個需求.
這里我們是使用當前時間來模擬動態的數據
login函數對應文件:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta http-equiv="content-Type" charset="UTF-8">
<meta http-equiv="x-ua-compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>login</title>
</head>
<body>
<h1>登錄頁面</h1>
<h1>This is login page!</h1>
<a href="https://blog.csdn.net/qq_41964425/article/category/8083068" target="_blank">CSDN</a>
<p>時間:@@xx@@</p> <!--提前定義好特殊符號-->
</body>
</html>
Python代碼:
from time import strftime
from socket import *
sk = socket(AF_INET, SOCK_STREAM)
sk.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
sk.bind(('127.0.0.1', 8080))
sk.listen()
# 將返回不同的內容部分封裝成函數
def index(url):
with open('index.html', 'rb') as f: return f.read()
def home(url):
with open('home.html', 'rb') as f: return f.read()
# 實現動態網頁
def login(url):
now_time = str(strftime('%F %T'))
with open('login.html', 'r', encoding='UTF-8') as f:
s = f.read()
# 在網頁中定義好特殊符號,用動態的數據去替換定義好的特殊符號
s = s.replace('@@xx@@', now_time)
return bytes(s, encoding='UTF-8')
# 定義一個url和實際要執行的函數的對應關系
url_list = [
('/index', index),
('/home', home),
('/login', login),
]
while True:
# ------------ 建立連接 接收消息 ------------
conn, addr = sk.accept()
data = conn.recv(8096)
# ------------ 對客服端發來的消息做處理 ------------
# 把收到的字節類型的數據轉換成字符串
data_str = str(data, encoding='UTF-8')
first_line = data_str.split('\r\n')[0] # 按\r\n分隔
url = first_line.split()[1] # 在按空格切割
# ------------ 業務邏輯處理部分 ------------
func = None
for i in url_list:
if url == i[0]:
func = i[1]
break
response = func(url) if func else b'404 not found!'
# ----------- 回復響應消息 -----------
conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 響應狀態行
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開發環境正是使用這個模塊來做服務器的.
使用wsgiref模塊來替換我們自己寫的Web框架的socket server部分:
login函數對應文件:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta http-equiv="content-Type" charset="UTF-8">
<meta http-equiv="x-ua-compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>login</title>
</head>
<body>
<h1>登錄頁面</h1>
<h1>This is login page!</h1>
<a href="https://blog.csdn.net/qq_41964425/article/category/8083068" target="_blank">CSDN</a>
<p>時間:@@xx@@</p> <!--提前定義好特殊符號-->
</body>
</html>
Python代碼:
from time import strftime
from wsgiref.simple_server import make_server
# 將返回不同的內容部分封裝成函數
def index(url):
with open('index.html', 'rb') as f: return f.read()
def home(url):
with open('home.html', 'rb') as f: return f.read()
def login(url):
"""實現動態網頁"""
now_time = str(strftime('%F %T')) # 獲取當前格式化時間
with open('login.html', 'r', encoding='UTF-8') as f:
s = f.read()
# 在網頁中定義好特殊符號,用動態的數據去替換定義好的特殊符號
s = s.replace('@@xx@@', now_time)
return bytes(s, encoding='UTF-8')
# 定義一個url和實際要執行的函數的對應關系
url_list = [
('/index', index),
('/home', home),
('/login', login),
]
def run_server(environ, start_response):
# 設置HTTP響應的狀態碼和頭消息
start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ])
url = environ['PATH_INFO'] # 取到用戶輸入的url
func = None
for i in url_list:
if url == i[0]:
func = i[1]
break
response = func(url) if func else b'404 not found!'
return [response, ]
if __name__ == '__main__':
httpd = make_server('127.0.0.1', 8080, run_server)
print('Wsgiref has started...')
httpd.serve_forever()
Django基礎—Web框架 MVC和MTV框架 MVC 把Web應用分為模型(M)、視圖(V)、控制器(C)三層,他們之間以一種插件式的,松耦合的方式聯系在一起。模型負責業務對象與數據庫的映射(ORM),視圖負責與用戶的交互(頁面),控制器接收用戶的輸入調用模型和視圖完成用戶的請求。 MTV Django的MTV模式本質上和MVC是一樣的,也是為了各組件間保持松耦合關系,只是定義上有...
WEB框架與Django簡介 HTTP協議: ? 超文本傳輸協議:規定了客戶端與服務端消息傳輸的格式 四大特性: 數據格式之請求: 數據格式之響應: 響應狀態碼: 動靜態網頁: 靜態網頁: ? 頁面上的數據都是寫死的,萬年不變 動態網頁: ? 頁面上的數據是從后端動態獲取的 模板渲染 后端生成的數據直接傳遞給前端頁面使用(并且前端頁面可以靈活的操作修改數據) 模板語法需要依賴于第三方模塊jinja...
本節內容 HTTP協議 wsgi協議和wsgiref模塊 Djano基本流程和配置 Django的路由系統 Django靜態文件 template模板語言 一 HTTP協議 1 HTTP簡介 HTTP是Hyper Text Transfer Protocol(超文本傳輸協議)的縮寫。它的發展是萬維網協會(World Wide Web Consortium)和Internet工作小組IE...
通過上節課的學習,我們已經對Django有了簡單的了解,現在來深入了解下~ 1. 路由系統 1.1 單一路由對應 1.2 基于正則的路由 找到urls.py文件,修改路由規則 在views.py文件創建對應方法 1.3 url分組 在url.py增加對應路徑 在views.py文件創建對應方法 1.4 為路由映射名稱 在templates目錄下的index.html 1.5 根據app對路由分類 ...
1. 制作模板 2. 獲取模板,并將所獲取的數據加載生成html文件 2. 生成PDF文件 其中由兩個地方需要注意,都是關于獲取文件路徑的問題,由于項目部署的時候是打包成jar包形式,所以在開發過程中時直接安照傳統的獲取方法沒有一點文件,但是當打包后部署,總是出錯。于是參考網上文章,先將文件讀出來到項目的臨時目錄下,然后再按正常方式加載該臨時文件; 還有一個問題至今沒有解決,就是關于生成PDF文件...
Docker 很占用空間,每當我們運行容器、拉取鏡像、部署應用、構建自己的鏡像時,我們的磁盤空間會被大量占用。 如果你也被這個問題所困擾,咱們就一起看一下 Docker 是如何使用磁盤空間的,以及如何回收。 docker 占用的空間可以通過下面的命令查看: TYPE 列出了docker 使用磁盤的 4 種類型: Images:所有鏡像占用的空間,包括拉取下來的鏡像,和本地構建的。 Con...
http://www.1ppt.com/moban/ 可以免費的下載PPT模板,當然如果要人工一個個下,還是挺麻煩的,我們可以利用requests輕松下載 訪問這個主頁,我們可以看到下面的樣式 點每一個PPT模板的圖片,我們可以進入到詳細的信息頁面,翻到下面,我們可以看到對應的下載地址 點擊這個下載的按鈕,我們便可以下載對應的PPT壓縮包 那我們就開始做吧 首先,查看網頁的源代碼,我們可以看到每一...
互斥鎖 互斥鎖也是屬于線程之間處理同步互斥方式,有上鎖/解鎖兩種狀態。 互斥鎖函數接口 1)初始化互斥鎖 pthread_mutex_init() man 3 pthread_mutex_init (找不到的情況下首先 sudo apt-get install glibc-doc sudo apt-get install manpages-posix-dev) 動態初始化 int pthread_...