server(3)

兩個(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)程崩潰酒朵。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市扎附,隨后出現(xiàn)的幾起案子蔫耽,更是在濱河造成了極大的恐慌,老刑警劉巖留夜,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件匙铡,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡碍粥,警方通過(guò)查閱死者的電腦和手機(jī)鳖眼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)嚼摩,“玉大人钦讳,你說(shuō)我怎么就攤上這事矿瘦。” “怎么了愿卒?”我有些...
    開(kāi)封第一講書人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵缚去,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我琼开,道長(zhǎng)易结,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任稠通,我火速辦了婚禮衬衬,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘改橘。我一直安慰自己滋尉,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布飞主。 她就那樣靜靜地躺著狮惜,像睡著了一般。 火紅的嫁衣襯著肌膚如雪碌识。 梳的紋絲不亂的頭發(fā)上碾篡,一...
    開(kāi)封第一講書人閱讀 51,688評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音筏餐,去河邊找鬼开泽。 笑死,一個(gè)胖子當(dāng)著我的面吹牛魁瞪,可吹牛的內(nèi)容都是我干的穆律。 我是一名探鬼主播,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼导俘,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼峦耘!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起旅薄,我...
    開(kāi)封第一講書人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤辅髓,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后少梁,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體洛口,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年猎莲,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了绍弟。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡著洼,死狀恐怖樟遣,靈堂內(nèi)的尸體忽然破棺而出而叼,到底是詐尸還是另有隱情,我是刑警寧澤豹悬,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布葵陵,位于F島的核電站,受9級(jí)特大地震影響瞻佛,放射性物質(zhì)發(fā)生泄漏脱篙。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一伤柄、第九天 我趴在偏房一處隱蔽的房頂上張望绊困。 院中可真熱鬧,春花似錦适刀、人聲如沸秤朗。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)取视。三九已至,卻和暖如春常挚,著一層夾襖步出監(jiān)牢的瞬間作谭,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工奄毡, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留折欠,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓吼过,卻偏偏與公主長(zhǎng)得像怨酝,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子那先,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容