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阅读 215,384评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,845评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,148评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,640评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,731评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,712评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,703评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,473评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,915评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,227评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,384评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,063评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,706评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,302评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,531评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,321评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,248评论 2 352

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,647评论 18 139
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,028评论 25 707
  • 一、温故而知新 1. 内存不够怎么办 内存简单分配策略的问题地址空间不隔离内存使用效率低程序运行的地址不确定 关于...
    SeanCST阅读 7,796评论 0 27
  • 随便瞎扯,博君一乐。 在我现在的三观里,正当的赚钱分如下高效、稳定、易用3个方向:1、高效型。也就是项目型赚钱方式...
    一一小知阅读 90评论 0 0
  • 返程的火车上,用一整天的时间刷完了《无证之罪》,不由得感叹,无论编剧还是制作,这部片子都良心得很。整部剧中我看到了...
    祥林二嫂阅读 3,364评论 14 14