-
不完善的web服務端實例
-
根據不一樣的路徑返回不一樣的內容
-
返回html頁面
-
總結
1.不完善的web服務端實例
a.嘗試第一次鏈接:
#寫客戶端
import socket
#建立一個scoket實例對象
sk = socket.socket()
#綁定ip地址和端口
sk.bind(('127.0.0.1',8000))
#監聽
sk.listen()
#寫一個死循環,一直等待客戶端來鏈接
while 1:
#獲取與客戶端的鏈接
conn, _ = sk.accept()
#接收客戶端發來的消息
conn.recv(8888)
#給客戶端回覆消息
conn.send(b'successful')
#關閉
conn.close()
sk.close()
寫好以後,運行。在瀏覽器中輸入127.0.0.1:8000回車,發現該網頁沒法正常運做,第一次嘗試失敗。
b.爲了發現錯誤緣由,將接收到客戶端消息打印出來,再次運行
data = conn.recv(8888)
print(data)
返回如下錯誤:
b'GET / HTTP/1.1\r\nHost: 127.0.0.1:8001\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 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\r\n\r\n'
整理以上錯誤信息,獲得:
'
GET / HTTP/1.1
請求行
Host:127.0.0.1:8001
請求頭
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 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
'
沒有請求數據
緣由分析:瀏覽器給我發的消息我收到了,可是我回復的東西瀏覽器不認識(沒有聽從瀏覽器的協議),所以出現了問題
c.通過上述分析,加上如下內容,獲得正常的顯示
conn.send(b'http/1.1 200 OK\r\n\r\n') #這裏發送的是請求行,請求頭部
conn.send(b'successful!') #這裏發送的是請求數據
#瀏覽器和服務端通訊都要遵循一個HTTP協議
關於HTTP協議:
1.瀏覽器往服務端發的叫作 請求(request)
請求的消息格式:
請求方法 路徑 HTTP/1.1\r\n
k1:v1\r\n
k2:v2\r\n
\r\n
請求數據
2.服務端往瀏覽器發的叫作 響應(response)
響應的消息格式:
HTTP/1.1 狀態碼 狀態描述符\r\n
k1:v1\r\n
k2:v2\r\n
\r\n
響應正文 <---html的內容
調試:
conn.send(b'http/1.1 200 OK\r\n
content-type:text/html; charset=utf8\r\n\r\n')
conn.send(b'
<h1>successful
<h1>')
import socket
sk = socket.socket()
sk.bind(('127.0.0.1', 7777))
sk.listen(5)
while 1:
conn, addr = sk.accept()
print(conn, addr)
data = conn.recv(1024)
conn.send(b'HTTP/1.1 200 OK\r\n\r\n')
conn.send(b'<h1>hello world</h1>')
conn.close()
把收到的數據轉成字符串格式:
data_str =
str(data, encoding='utf8')
import socket
#建立一個scoket實例對象
sk = socket.socket()
#綁定ip地址和端口
sk.bind(('127.0.0.1',8001))
#監聽
sk.listen()
#寫一個死循環,一直等待客戶端來鏈接
while 1:
#獲取與客戶端的鏈接
conn, _ = sk.accept()
#接收客戶端發來的消息
data = conn.recv(8888)
#把收到的數據轉成字符串類型
data_str = str(data, encoding='utf8')
#用\r\n去切割上面的字符串
li = data_str.split('\r\n')
#print(li[0])
#按照空格切割上面的字符串
li2 = li[0].split()
path = li2[1]
conn.send(b'http/1.1 200 OK\r\ncontent-type:text/html; charset=utf8\r\n\r\n')
#根據不一樣的path返回不一樣的content
if path == '/':
conn.send(b'<h1>successful<h1>')
elif path == '/123/':
conn.send(b'<h1>nothing!<h1>')
else:
conn.send(b'<h1>404! not found!<h1>')
#關閉
conn.close()
sk.close()
優化上述代碼:
if path == '/111/':
response = b'<h1>successful<h1>'
elif path == '/123/':
response = b'<h1>nothing!<h1>'
else:
response = b'<h1>404! not found!<h1>'
conn.send(response)
再次優化,寫入函數:
def func1():
ret = 'hello {}'.format(url)
return bytes(ret, encoding='utf8')
def func2():
return b'<h1>nothing!<h1>'
if path == '/111/':
response =
func1()
elif path == '/123/':
response =
func2()
else:
response = b'<h1>404! not found!<h1>'
conn.send(response)
去掉if條件:
"""
完善的web服務端示例
函數版根據不一樣的路徑返回不一樣的內容
進階函數版 不寫if判斷了,用url名字去找對應的函數名
"""
import socket
# 生成socket實例對象
sk = socket.socket()
# 綁定IP和端口
sk.bind(("127.0.0.1", 8001))
# 監聽
sk.listen()
# 定義一個處理/yimi/的函數
def yimi(url):
ret = 'hello {}'.format(url)
return bytes(ret, encoding="utf-8")
# 定義一個處理/xiaohei/的函數
def xiaohei(url):
ret = 'hello {}'.format(url)
return bytes(ret, encoding="utf-8")
# 定義一個專門用來處理404的函數
def f404(url):
ret = "你訪問的這個{} 找不到".format(url)
return bytes(ret, encoding="utf-8")
url_func = [
("/yimi/", yimi),
("/xiaohei/", xiaohei),
]
# 寫一個死循環,一直等待客戶端來連我
while 1:
# 獲取與客戶端的鏈接
conn, _ = sk.accept()
# 接收客戶端發來消息
data = conn.recv(8096)
# 把收到的數據轉成字符串類型
data_str = str(data, encoding="utf-8") # bytes("str", enconding="utf-8")
# print(data_str)
# 用\r\n去切割上面的字符串
l1 = data_str.split("\r\n")
# print(l1[0])
# 按照空格切割上面的字符串
l2 = l1[0].split()
url = l2[1]
# 給客戶端回覆消息
conn.send(b'http/1.1 200 OK\r\ncontent-type:text/html; charset=utf-8\r\n\r\n')
# 想讓瀏覽器在頁面上顯示出來的內容都是響應正文
# 根據不一樣的url返回不一樣的內容
# 去url_func裏面找對應關係
for i in url_func:
if i[0] == url:
func = i[1]
break
# 找不到對應關係就默認執行f404函數
else:
func = f404
# 拿到函數的執行結果
response = func(url)
# 將函數返回的結果發送給瀏覽器
conn.send(response)
# 關閉鏈接
conn.close()
"""
完善的web服務端示例
函數版根據不一樣的路徑返回不一樣的內容
進階函數版 不寫if判斷了,用url名字去找對應的函數名
"""
import socket
# 生成socket實例對象
sk = socket.socket()
# 綁定IP和端口
sk.bind(("127.0.0.1", 8001))
# 監聽
sk.listen()
# 定義一個處理/yimi/的函數
def yimi(url):
with open('html1.html','rb') as f:
ret = f.read()
return ret
# 定義一個處理/xiaohei/的函數
def xiaohei(url):
with open('html2.html','rb') as f:
ret = f.read()
return ret
# 定義一個專門用來處理404的函數
def f404(url):
ret = "你訪問的這個{} 找不到".format(url)
return bytes(ret, encoding="utf-8")
url_func = [
("/yimi/", yimi),
("/xiaohei/", xiaohei),
]
# 寫一個死循環,一直等待客戶端來連我
while 1:
# 獲取與客戶端的鏈接
conn, _ = sk.accept()
# 接收客戶端發來消息
data = conn.recv(8096)
# 把收到的數據轉成字符串類型
data_str = str(data, encoding="utf-8") # bytes("str", enconding="utf-8")
# print(data_str)
# 用\r\n去切割上面的字符串
l1 = data_str.split("\r\n")
# print(l1[0])
# 按照空格切割上面的字符串
l2 = l1[0].split()
url = l2[1]
# 給客戶端回覆消息
conn.send(b'http/1.1 200 OK\r\ncontent-type:text/html; charset=utf-8\r\n\r\n')
# 想讓瀏覽器在頁面上顯示出來的內容都是響應正文
# 根據不一樣的url返回不一樣的內容
# 去url_func裏面找對應關係
for i in url_func:
if i[0] == url:
func = i[1]
break
# 找不到對應關係就默認執行f404函數
else:
func = f404
# 拿到函數的執行結果
response = func(url)
# 將函數返回的結果發送給瀏覽器
conn.send(response)
# 關閉鏈接
conn.close()
返回動態的html頁面
動態的html頁面本質上都是字符串在服務端的替換。
#插入動態事件戳
def yimi(url):
with open('html1.html','rb') as f:
ret = f.read()
import time
ret2 = ret.replace('@@xx@@', str(time.time()))
return bytes(ret2, encoding='utf-8')
4.總結:
1. web框架的本質:
socket服務端 與 瀏覽器的通訊
2. socket服務端功能劃分:
a. 負責與瀏覽器收發消息(socket通訊) --> wsgiref/uWsgi/gunicorn...
b. 根據用戶訪問不一樣的路徑執行不一樣的函數
c. 從HTML讀取出內容,而且完成字符串的替換 --> jinja2(模板語言)
3. Python中 Web框架的分類:
1. 按上面三個功能劃分:
1. 框架自帶a,b,c --> Tornado
2. 框架自帶b和c,使用第三方的a --> Django
3. 框架自帶b,使用第三方的a和c --> Flask
2. 按另外一個維度來劃分:
1. Django --> 大而全(你作一個網站能用到的它都有)
2. 其餘 --> Flask 輕量級