兩個(gè)亮點(diǎn):
- 將 request 傳遞進(jìn)路由函數(shù)(以及表驅(qū)動(dòng)法)
- 多線程
server.py
import socket
import urllib.parse
from utils import log
from routes import route_static
from routes import route_dict
class Request(object):
def __init__(self):
self.method = 'GET'
self.path = ''
self.query = {}
self.header = ''
self.body = ''
def form(self):
body = urllib.parse.unquote(self.body)
args = body.split('&')
f = {}
for arg in args:
k, v = arg.split('=')
f[k] = v
log('form() 字典', f)
return f
def header_dict(self):
header = self.header
header_list = header.split('\r\n')
h = {}
for ele in header_list:
(k, v) = ele.split(':')
h[k] = v
return h
解析請(qǐng)求行,存儲(chǔ)請(qǐng)求數(shù)據(jù)
- request 類解析請(qǐng)求行揣云,用屬性存儲(chǔ)請(qǐng)求行中的數(shù)據(jù)(方法银受、路徑、get 方法提交的數(shù)據(jù))敛纲;
- from 方法存儲(chǔ)表單提交的數(shù)據(jù)(因?yàn)楸韱翁峤坏臄?shù)據(jù)存儲(chǔ)在 body 中)晌纫;
- urllib.parse.uniquote_plus() 能夠處理漢字和特殊字符
- header_dict 方法存儲(chǔ)請(qǐng)求首部字段的建和值;
def error(code=404):
e = {
404: b'HTTP/1.1 404 NOT FOUND\r\n\r\n<h1>NOT FOUND</h1>',
}
return e.get(code, b'')
def parsed_path(path):
index = path.find('?')
if index == -1:
return path, {}
else:
path, query_string = path.split('?', 1)
args = query_string.split('&')
query = {}
for arg in args:
k, v = arg.split('=')
query[k] = v
return path, query
def response_for_path(path):
path, query = parsed_path(path)
request.path = path
request.query = query
r = {
'/static': route_static,
}
r.update(route_dict)
response = r.get(path, error)
return response(request)
這個(gè)是根據(jù)解析的路徑分發(fā)路由
- 字典的鍵一般不用數(shù)字境肾,但是 HTTP 狀態(tài)碼就是鍵
- 字符串的 find() 方法剔难,找不到,返回 -1
- 表驅(qū)動(dòng)發(fā)分發(fā)路由
- 字典的 update() 方法能將兩個(gè)字典合并奥喻,參數(shù)為另一個(gè)字典
- 將 request 傳遞進(jìn)路由函數(shù)
import _thread
def run(host='', port=3000):
with socket.socket() as s:
s.bind((host, port))
s.listen()
while True:
connection, address = s.accept()
_thread.start_new_thread(process_request, (address, connection))
def process_request(address, connection):
r = connection.recv(1024)
r = r.decode()
if len(r) > 0:
request = Request()
header, request.body = r.split('\r\n\r\n', 1)
h = header.split('\r\n')
parts = h[0].split()
request.header = h[1:]
path = parts[1]
request.method = parts[0]
response = response_for_path(path)
connection.sendall(response)
else:
log('接收到了一個(gè)空請(qǐng)求')
connection.close()
if __name__ == '__main__':
config = dict(
host='127.0.0.1',
port=3000,
)
run(**config)
routes.py
from utils import log
from models import Message
from models import User
message_list = []
def template(name):
path = 'templates/' + name
with open(path, 'r', encoding='utf-8') as f:
return f.read()
def route_index(request):
header = 'HTTP/1.1 210 VERY OK\r\nContent-Type: text/html\r\n'
body = template('index.html')
r = header + '\r\n' + body
return r.encode()
def route_login(request):
header = 'HTTP/1.1 210 VERY OK\r\nContent-Type: text/html\r\n'
if request.method == 'POST':
form = request.form()
u = User.new(form)
if u.validate_login():
result = '登錄成功'
else:
result = '用戶名或者密碼錯(cuò)誤'
else:
result = ''
body = template('login.html')
body = body.replace('{{result}}', result)
r = header + '\r\n' + body
# 有中文的時(shí)候最好用 utf-8 進(jìn)行編碼
return r.encode(encoding='utf-8')
def route_register(request):
header = 'HTTP/1.1 210 VERY OK\r\nContent-Type: text/html\r\n'
if request.method == 'POST':
form = request.form()
# 將表單提交的數(shù)據(jù)數(shù)實(shí)例化钥飞,也就是將字典變?yōu)閷傩? u = User.new(form)
if u.validate_register():
u.save()
#log('u的id為:', u.id)
result = '注冊(cè)成功<br> <pre>{}</pre>'.format(User.all())
else:
result = '用戶名或者密碼長(zhǎng)度必須大于2'
else:
result = ''
body = template('register.html')
body = body.replace('{{result}}', result)
r = header + '\r\n' + body
return r.encode(encoding='utf-8')
def route_message(request):
log('本次請(qǐng)求的 method', request.method)
if request.method == 'POST':
data = request.form()
else:
data = request.query
if len(data) > 0:
msg = Message.new(data)
log('post', data)
# 應(yīng)該在這里保存 message_list
message_list.append(msg)
header = 'HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n'
body = template('html_basic.html')
ms = '<br>'.join([str(m) for m in message_list])
body = body.replace('{{messages}}', ms)
r = header + '\r\n' + body
return r.encode()
def route_static(request):
filename = request.query.get('file', 'doge.gif')
path = 'static/' + filename
# 通過(guò)Content-Type指定傳輸文件類型(比較松散)
with open(path, 'rb') as f:
header = b'HTTP/1.1 200 OK\r\nContent-Type: image/gif\r\n'
r = header + b'\r\n' + f.read()
return r
route_dict = {
'/': route_index,
'/login': route_login,
'/register': route_register,
'/messages': route_message,
}
多線程
單線程:不處理完當(dāng)前請(qǐng)求,就無(wú)法處理下一個(gè)請(qǐng)求衫嵌,也就是一次處理一個(gè)鏈接。
每個(gè)程序(qq/知乎)是一個(gè)進(jìn)程彻秆,相當(dāng)于一條大馬路楔绞,這個(gè)大馬路有很多車道,那么就是每個(gè)進(jìn)程有很多線程唇兑,進(jìn)程的子線程崩潰了不會(huì)導(dǎo)致這個(gè)主進(jìn)程崩潰酒朵。