python筆記(4) 一個簡單的web服務器

前面的筆記里面談到了裝飾器和正則表達式,這里我要實現一個簡單的web服務器。具體到功能上,首先是可以通過瀏覽器訪問,還有一點就是有一個簡單的模版系統。

1. 網絡部分

網絡部分使用的twisted,這個代碼從twisted一本書中拷出來的。不要覺得很復雜照抄即可,我也不懂twisted。這里要說的是HTTP協議每行數據都是以\r\n結尾,所以這里協議繼承自basic.LineReceiver

from twisted.protocols import basic
from twisted.internet import protocol, reactor
from template import render_template
class HTTPProtocol(basic.LineReceiver):
    def __init__(self,factory):
        self.lines = []
        self.factory = factory

    def lineReceived(self, line):
        self.lines.append(line)
        if not line:
            # self.sendResponse(self.factory.app.dispatch(lines))
            self.sendResponse(self.factory.app.dispatch(self.lines))
    def sendResponse(self,data):
        self.sendLine(b"HTTP/1.1 200 OK")
        self.sendLine(b"")
        # responseBody = "You said:\r\n\r\n" + "\r\n".join(self.lines)
        responseBody = data.encode('utf-8')+b'\r\n'
        self.transport.write(responseBody)
        self.transport.loseConnection()

class HTTPFactory(protocol.ServerFactory):
    def __init__(self,app):
        self.app = app
    def buildProtocol(self, addr):
        return HTTPProtocol(self)

2 路徑裝飾器

前面我談到過裝飾器的問題,每個路徑對應的函數也可以使用裝飾器來實現。把路徑與帶路徑裝飾器的函數關聯起來,這里簡單使用dict這種數據結構。實現的代碼在下面列出,這里可以看到route函數是一個成員函數,最重要的一行代碼就是self.routes[url] = func。本來裝飾器只需要這行代碼就能工作,但是如果裝飾器不返回一個函數,這里說的是wrapper這個函數,那么就不能在一個函數上使用多個路徑裝飾器。

class Dongge():
    def __init__(self):
        self.routes = {}

    def dispatch(self,lines):
        line = lines[0].decode('utf-8')
        method,url,version = line.split(' ')
        print(line)
        print(self.routes)
        if url in self.routes:
            return self.routes[url]()
        return 'No such url resource'

    def route(self,url):
        def df(func):
            self.routes[url] = func
            def wrapper(*args, **kwargs):
                return func(*args,**kwargs)
            return wrapper
        return df

app = Dongge()

@app.route('/')
@app.route('/index')
def index():
    return 'index'

3 模板

模版在前面已經談到過這方面的內容,這里只需要對render_template封裝一下即可使用。

def view(self,tname,context):
        path = '{}/{}.html'.format(self.templatedir,tname)
        try:
            with open(path,'r') as f:
                html = f.read()
                print(html)
                return render_template(html,context)
        except Exception as e:
            print(e)
            return ''

4 補充

讓代碼運行起來,其實就是簡單的兩句代碼:

reactor.listenTCP(8000, HTTPFactory(app))
reactor.run()

如果你來跑這代碼會存在什么問題,當然代碼都運行過了肯定沒多大問題。但是這里還有個讓人很苦惱的事情,就是每次修改代碼之后總得重新運行服務器。也許是說,我需要一個東西能監控我代碼的改動,然后自動重啟服務器。這個問題很其實很簡單,看代碼,這代碼都是從網上拷的稍微修改了一下。

#start.py
# -*- coding:utf-8 -*-
from watchdog.observers import Observer
from watchdog.events import *
import time
from server import start
from multiprocessing import Process
class FileEventHandler(FileSystemEventHandler):
    def __init__(self):
        FileSystemEventHandler.__init__(self)

    def on_moved(self, event):
        if event.is_directory:
            print("directory moved from {0} to {1}".format(event.src_path,event.dest_path))
        else:
            print("file moved from {0} to {1}".format(event.src_path,event.dest_path))

    def on_created(self, event):
        if event.is_directory:
            print("directory created:{0}".format(event.src_path))
        else:
            print("file created:{0}".format(event.src_path))

    def on_deleted(self, event):
        if event.is_directory:
            print("directory deleted:{0}".format(event.src_path))
        else:
            print("file deleted:{0}".format(event.src_path))

    def on_modified(self, event):
        if event.is_directory:
            print("directory modified:{0}".format(event.src_path))
        else:
            print("file modified:{0}".format(event.src_path))
            #如果改動的文件不是start.py則不需要重啟服務器
            if event.src_path.find('start.py') == -1:
                self.startserver()
    '''
    啟動服務器需要開啟新進程,重啟和啟動代碼放在一起了
    '''
    def startserver(self):
        if hasattr(self,'p'):
            self.p.terminate()
            del self.p
        p = Process(target=start)
        p.start()
        self.p = p

if __name__ == "__main__":
    observer = Observer()
    event_handler = FileEventHandler()
    event_handler.startserver()
    observer.schedule(event_handler,"./",True)
    observer.start()
    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        observer.stop()
    observer.join()
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,362評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,577評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,486評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,852評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,600評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,944評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,944評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,108評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,652評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,385評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,616評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,111評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,798評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,205評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,537評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,334評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,570評論 2 379

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,828評論 18 139
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,752評論 25 708
  • 一、溫故而知新 1. 內存不夠怎么辦 內存簡單分配策略的問題地址空間不隔離內存使用效率低程序運行的地址不確定 關于...
    SeanCST閱讀 7,865評論 0 27
  • 隨便瞎扯,博君一樂。 在我現在的三觀里,正當的賺錢分如下高效、穩定、易用3個方向:1、高效型。也就是項目型賺錢方式...
    一一小知閱讀 98評論 0 0
  • 返程的火車上,用一整天的時間刷完了《無證之罪》,不由得感嘆,無論編劇還是制作,這部片子都良心得很。整部劇中我看到了...
    祥林二嫂閱讀 3,387評論 14 14