美文网首页
server(3)

server(3)

作者: 马梦里 | 来源:发表于2018-01-28 14:40 被阅读0次

    两个亮点:

    • 将 request 传递进路由函数(以及表驱动法)
    • 多线程

    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
    

    解析请求行,存储请求数据

    • request 类解析请求行,用属性存储请求行中的数据(方法、路径、get 方法提交的数据);
    • from 方法存储表单提交的数据(因为表单提交的数据存储在 body 中);
      • urllib.parse.uniquote_plus() 能够处理汉字和特殊字符
    • header_dict 方法存储请求首部字段的建和值;
    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)
    

    这个是根据解析的路径分发路由

    • 字典的键一般不用数字,但是 HTTP 状态码就是键
    • 字符串的 find() 方法,找不到,返回 -1
    • 表驱动发分发路由
      • 字典的 update() 方法能将两个字典合并,参数为另一个字典
      • 将 request 传递进路由函数
    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('接收到了一个空请求')
        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 = '用户名或者密码错误'
        else:
            result = ''
        body = template('login.html')
        body = body.replace('{{result}}', result)
        r = header + '\r\n' + body
        # 有中文的时候最好用 utf-8 进行编码
        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()
            # 将表单提交的数据数实例化,也就是将字典变为属性
            u = User.new(form)
            if u.validate_register():
                u.save()
                #log('u的id为:', u.id)
                result = '注册成功<br> <pre>{}</pre>'.format(User.all())
            else:
                result = '用户名或者密码长度必须大于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('本次请求的 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)
            # 应该在这里保存 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
        #  通过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,
    }
    

    多线程
    单线程:不处理完当前请求,就无法处理下一个请求,也就是一次处理一个链接。
    每个程序(qq/知乎)是一个进程,相当于一条大马路,这个大马路有很多车道,那么就是每个进程有很多线程,进程的子线程崩溃了不会导致这个主进程崩溃。

    相关文章

      网友评论

          本文标题:server(3)

          本文链接:https://www.haomeiwen.com/subject/erzaaxtx.html