[TOC]
>[success] # 仿照網站請求
~~~
1.處理url,通過url 獲取 http/https host port path 四個參數
2.例如 http://www.hmoore.net/book, 首先獲取http/https,在吧獲取的連接
www.hmoore.net/book,在進行拆分,獲取域名和端口
~~~
>[info] ## 編寫客戶端
>[danger] ##### 處理url
~~~
1.socket 仿照瀏覽器處理,獲取服務器請求
2.對url拆分,成協議,host,端口,和文件目錄
3.利用find,來進行目錄拆分,獲取位置信息后,進行切片獲取
4.將固定的東西放在字典中,省去if判斷邏輯
~~~
~~~
def parsed_url(url):
"""
:param url: 獲取網址的url
:return: 返回所需要的 http/https host port path 四個參數
"""
# 獲取http 或者https
protocol = "http"
if url[:7] == "http://":
u = url.split("://")[1]
if url[:8] == "https://":
u = url.split("://")[1]
protocol = "https"
else:
u = url
# 把端口加域名 和 網站的文件目錄分離
i = u.find("/")
if i == -1:
host = u
path = "/"
else:
host = u[:i]
path = u[i:]
# 處理host
port_dict = {
'http': 80,
'https': 443,
}
port = port_dict[protocol]
if ":" in host:
h = host.split(":")
host = h[0]
port = int(h[1])
return protocol, host, port, path
~~~
>[danger] ##### 嘗試編寫單元測試
~~~
def test_parsed_url():
http = 'http'
https = 'https'
host = 'g.cn'
path = '/'
test_items = [
('http://g.cn', (http, host, 80, path)),
('http://g.cn/', (http, host, 80, path)),
('http://g.cn:90', (http, host, 90, path)),
('http://g.cn:90/', (http, host, 90, path)),
#
('https://g.cn', (https, host, 443, path)),
('https://g.cn:233/', (https, host, 233, path)),
]
for t in test_items:
url, expected = t
u = parsed_url(url)
e = "parsed_url ERROR, ({}) ({}) ({})".format(url, u, expected)
assert u == expected, e
~~~
>[danger] ##### 編寫client
~~~
1.拆分一個做協議判斷的方法socket_by_protocol
2.一個循環獲得服務器返回所有信息的方法response_by_socket
3.一個解析 header body 請求狀態碼的方法,巧妙利用元組可以被多個元素接受
4.在整體的大方法中,我們也可以用列表存儲代替if判斷
~~~
~~~
import socket,ssl
def socket_by_protocol(protocol):
"""
判斷使用http 還是https 協議
"""
if protocol == "http":
s = socket.socket()
else:
s = ssl.wrap_socket(socket.socket())
return s
def response_by_socket(s):
"""
參數是一個 socket 實例
返回這個 socket 讀取的所有數據
"""
response = b''
buffer_size = 1024
while True:
r = s.recv(buffer_size)
if len(r) == 0:
break
response += r
return response
def parsed_response(r):
"""
把 response 解析出 狀態碼 headers body 返回
狀態碼是 int
headers 是 dict
body 是 str
"""
header, body = r.split('\r\n\r\n', 1)
h = header.split('\r\n')
status_code = h[0].split()[1]
status_code = int(status_code)
headers = {}
for line in h[1:]:
k, v = line.split(': ')
headers[k] = v
return status_code, headers, body
def get(url):
protocol, host, port, path = parsed_url(url)
s = socket_by_protocol(protocol)
s.connect((host, port))
# 不用持續連接Connection: close
request = 'GET {} HTTP/1.1\r\nHost: {}\r\nConnection: close\r\n\r\n'.format(path, host)
s.send(request.encode("utf-8"))
response = response_by_socket(s)
r = response.decode("utf-8")
parsed_response(r)
status_code, headers, body = parsed_response(r)
if status_code in [301, 302]:
url = headers['Location']
return get(url)
return status_code, headers, body
~~~
>[danger] ##### 服務端 -- server.py
~~~
1.利用 with 去做socket 連接,為了當端口用完進行關閉
2.接受到客戶端訪問服務器的請求頭,對請求頭的路徑拆分出來
3.通過路由,將請求頭 傳遞到路由映射函數
4.response_for_path 路由映射的方法,通過字典存儲和路徑對應的,處理函數,
其中k 保存的是函數地址,這樣減少執行函數
5.在路由函數中利用字典get 的屬性返回404 函數
6.將數據返回給客戶端
~~~
~~~
def log(*args, **kwargs):
"""
用這個 log 替代 print
"""
print('log', *args, **kwargs)
def route_index():
"""
主頁的處理函數, 返回主頁的響應
"""
header = 'HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n'
body = '<h1>Hello Gua</h1><img src="/doge.gif">'
r = header + '\r\n' + body
return r.encode(encoding='utf-8')
def page(name):
with open(name, encoding='utf-8') as f:
return f.read()
def route_msg():
"""
msg 頁面的處理函數
"""
header = 'HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n'
body = page('html_basic.html')
r = header + '\r\n' + body
return r.encode(encoding='utf-8')
def route_image():
"""
圖片的處理函數, 讀取圖片并生成響應返回
"""
with open('doge.gif', 'rb') as f:
header = b'HTTP/1.1 200 OK\r\nContent-Type: image/gif\r\n'
img = header + b'\r\n' + f.read()
return img
def error(code=404):
"""
根據 code 返回不同的錯誤響應
目前只有 404
"""
# 之前上課我說過不要用數字來作為字典的 key
# 但是在 HTTP 協議中 code 都是數字似乎更方便所以打破了這個原則
e = {
404: b'HTTP/1.1 404 NOT FOUND\r\n\r\n<h1>NOT FOUND</h1>',
}
return e.get(code, b'')
def response_for_path(path):
"""
根據 path 調用相應的處理函數
沒有處理的 path 會返回 404
"""
r = {
'/': route_index,
'/doge.gif': route_image,
'/msg': route_msg,
}
response = r.get(path, error)
return response()
def run(host='', port=3000):
"""
啟動服務器
"""
# 初始化 socket 套路
# 使用 with 可以保證程序中斷的時候正確關閉 socket 釋放占用的端口
with socket.socket() as s:
s.bind((host, port))
# 無限循環來處理請求
while True:
# 監聽 接受 讀取請求數據 解碼成字符串
s.listen(5)
connection, address = s.accept()
request = connection.recv(1024)
log('raw, ', request)
request = request.decode('utf-8')
log('ip and request, {}\n{}'.format(address, request))
try:
# 因為 chrome 會發送空請求導致 split 得到空 list
# 所以這里用 try 防止程序崩潰
path = request.split()[1]
# 用 response_for_path 函數來得到 path 對應的響應內容
response = response_for_path(path)
# 把響應發送給客戶端
connection.sendall(response)
except Exception as e:
log('error', e)
# 處理完請求, 關閉連接
connection.close()
def main():
# 生成配置并且運行程序
config = dict(
host='',
port=3000,
)
# 如果不了解 **kwargs 的用法, 群里問或者看書/搜索 關鍵字參數
run(**config)
if __name__ == '__main__':
main()
~~~
- 網絡原理
- 為搭建框架做準備
- 簡單認識網路
- 自定義模擬網站案例
- 優化最終框架
- 數據存儲 -- data
- 用戶個人信息存儲 -- User.txt
- 路由映射 -- routes
- 處理用戶信息 -- routes_static.py
- 保存靜態文件 -- static
- templates -- html 集中處理模塊
- 首頁 -- index.html
- 登陸 -- login.html
- 用戶注冊頁面 -- register
- 日志模塊 -- log.gua.txt
- 啟動文件--server.py
- orm處理 -- model.py
- 日志模塊 -- utils.py
- 兩種數據庫類型
- 傳統數據庫了解篇
- 前端快速入門
- JS簡單使用入門
- css簡單快速入門
- DJANGO
- virtualenv-創建虛擬環境
- 項目結構
- django-admin中文配置
- django-打印sql語句
- django-基礎
- 認識MVC和MTV
- Django--初識
- Django--初識案例
- Django-FBV/CBV
- Django--常用input 交互
- Django-url
- Django-url.py 配置
- Django-include 使用
- Django-url name
- Django-ORM
- ORM-數據庫配置
- ORM-model字段
- ORM-model字段解釋
- ORM-字段選項
- ORM-查詢
- ORM-四種常用查詢方法
- ORM-三種獲取數據
- ORM-其他查詢方式
- ORM-條件查詢雙線
- ORM-Q和F條件使用
- ORM-三種數據庫交互
- 案例 -- 一對多
- ORM-技巧/常見問題
- ORM-N+1 問題
- ORM-并發的處理
- ORM-數量查詢、
- ORM-正向反向查詢
- ORM-基礎案例一
- ORM-基礎一對多案例
- Django-templates
- Django-模板的繼承
- Django-模板的過濾
- Django-自定義模板的過濾
- Django-cookie
- Django-cookies 裝飾器
- Djang-session
- Django-CSRF
- Django-中間件 -- 后續了解
- Django- 緩存 -- 沒有深入了解
- Django-form
- From-ajax
- form-內部驗證處理
- form-屬性
- form-常用的標簽字段
- form-常用的下拉和選擇
- form-widget速查
- Django-ajax序列化
- Django-多種ajax寫法
- ajax-原生寫法
- ajax-$寫法
- ajax-ifram
- Django-ajax圖片上傳
- ajax-原始寫法
- ajax-正常寫法
- iframe+form
- 實戰寫法
- Django-常用自編寫組件
- Django-雙菜單組合搜索
- Django - 多菜單組合搜索
- Django-分頁
- django-綜合基礎
- 綜合基礎-render
- django-admin
- admin-頁面配置
- admin-字段配置
- admin-編輯頁面
- admin-forms驗證
- admin-創建抽象類
- django-驗證碼
- 驗證碼-第三方生成庫
- 驗證碼-view.py使用
- 驗證碼-注意引入Monaco.ttf
- django-用戶注冊
- 注冊-form 模塊
- 注冊-views 模塊
- 注冊-html模塊
- 注冊-model模塊
- django-用戶登錄三種
- session登錄
- form-session 寫法
- view-寫法
- Html-寫法
- model-寫法
- 繼承類登錄
- 外鍵關聯登錄
- django-簡單的student 管理案例
- app-urls.py
- app-models.py配置
- admin-admin.py配置
- app-form.py 和數據庫關聯的寫法
- app-FBV_views.py
- app-CBV_views.py
- templates-index.html
- django-博客系統
- APP目錄-models.py 創建
- APP目錄-基礎展示數據分析
- APP目錄-基礎數據展示cls
- ListView
- DetailView
- FormView
- 額外功能拓建
- 添加文章搜索/用戶文章查詢功能
- 增加一個友情鏈接
- 增加一個評論模塊
- App-利用Bootstrap4 搭建樣式
- 項目crm
- 思維導圖
- perfectCRM-項目名字
- settings.py-配置
- view.py-登陸/登出函數
- crm-app文件
- model.py-表的創建
- admin.py-注冊后臺
- view.py-視圖層
- static-靜態文件
- css
- bootstrap.min.css
- dashboard.css
- ie10-viewport-bug-workaround.css
- signin.css
- fonts
- imgs
- js
- jquery.js
- bootstrap.min.js
- holeder.js
- ie10-viewport-bug-workaround.js
- ie-emulation-modes-warning.js
- plugins
- html模板文件-templates
- crm
- index.html-首頁模板