ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Python实现简易HTTP服务器与MINI WEB框架(利用WSGI实现服务器与框架解耦)

2019-12-01 16:04:08  阅读:284  来源: 互联网

标签:MINI socket 框架 server new msg 服务器 recv response


本文描述如果简单实现自定义Web服务器与自定义简易框架,并且不断进行版本迭代,从而清晰的展现服务器与Web框架之间是如何结合、如何配合工作的。以及WSGI是什么。

 

一、选取一个自定义的服务器版本

参照 https://www.cnblogs.com/leokale-zz/p/11957768.html 中的各种服务器实现版本,我们选择比较简单的多进程版本作为演示版本

代码如下:

import socket
import re
import multiprocessing


def handle_request(new_socket):
    # 接收请求
    recv_msg = ""
    recv_msg = new_socket.recv(1024).decode("utf-8")
    if recv_msg == "":
        print("recv null")
        new_socket.close()
        return

    # 从请求中解析出URI
    recv_lines = recv_msg.splitlines()
    print(recv_lines.__len__())
    # 使用正则表达式提取出URI
    ret = re.match(r"[^/]+(/[^ ]*)", recv_lines[0])
    if ret:
        # 获取URI字符串
        file_name = ret.group(1)
        # 如果URI是/,则默认返回index.html的内容
        if file_name == "/":
            file_name = "/index.html"

    try:
        # 根据请求的URI,读取相应的文件
        fp = open("." + file_name, "rb")
    except:
        # 找不到文件,响应404
        response_msg = "HTTP/1.1 404 NOT FOUND\r\n"
        response_msg += "\r\n"
        response_msg += "<h1>----file not found----</h1>"
        new_socket.send(response_msg.encode("utf-8"))
    else:
        html_content = fp.read()
        fp.close()
        # 响应正确 200 OK
        response_msg = "HTTP/1.1 200 OK\r\n"
        response_msg += "\r\n"

        # 返回响应头
        new_socket.send(response_msg.encode("utf-8"))
        # 返回响应体
        new_socket.send(html_content)

    # 关闭该次socket连接
    new_socket.close()


def main():
    # 创建TCP SOCKET实例
    tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # # 设置重用地址
    # tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    # 绑定地址(默认本机IP)和端口
    tcp_server_socket.bind(("", 7890))
    # 监听
    tcp_server_socket.listen(128)
    # 循环接收客户端连接
    while True:
        new_socket, client_addr = tcp_server_socket.accept()
        # 启动一个子进程来处理客户端的请求
        sub_p = multiprocessing.Process(target=handle_request, args=(new_socket,))
        sub_p.start()
        # 这里要关闭父进程中的new_socket,因为创建子进程会复制一份new_socket给子进程
        new_socket.close()

    # 关闭整个SOCKET
    tcp_server_socket.close()


if __name__ == "__main__":
    main()

 

二、将代码用面向对象思想改写

import socket
import re
import multiprocessing


class WSGIServer(object):
    def __init__(self):
        # 创建socket实例
        self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 设置资源重用
        self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        # 绑定IP端口
        self.tcp_server_socket.bind(("", 7890))
        # 开始监听
        self.tcp_server_socket.listen(128)

    # 请求处理函数
    def handle_request(self, new_socket):
        # 接收请求
        recv_msg = ""
        recv_msg = new_socket.recv(1024).decode("utf-8")
        if recv_msg == "":
            print("recv null")
            new_socket.close()
            return

        # 从请求中解析出URI
        recv_lines = recv_msg.splitlines()
        # 使用正则表达式提取出URI
        ret = re.match(r"[^/]+(/[^ ]*)", recv_lines[0])
        if ret:
            # 获取URI字符串
            file_name = ret.group(1)
            # 如果URI是/,则默认返回index.html的内容
            if file_name == "/":
                file_name = "/index.html"

        try:
            # 根据请求的URI,读取相应的文件
            fp = open("." + file_name, "rb")
        except:
            # 找不到文件,响应404
            response_msg = "HTTP/1.1 404 NOT FOUND\r\n"
            response_msg += "\r\n"
            response_msg += "<h1>----file not found----</h1>"
            new_socket.send(response_msg.encode("utf-8"))
        else:
            html_content = fp.read()
            fp.close()
            # 响应正确 200 OK
            response_msg = "HTTP/1.1 200 OK\r\n"
            response_msg += "\r\n"

            # 返回响应头
            new_socket.send(response_msg.encode("utf-8"))
            # 返回响应体
            new_socket.send(html_content)

        # 关闭该次socket连接
        new_socket.close()

    # 开始无限循环,接受请求
    def run_forever(self):
        while True:
            new_socket, client_addr = self.tcp_server_socket.accept()
            # 启动一个子进程来处理客户端的请求
            sub_p = multiprocessing.Process(target=self.handle_request, args=(new_socket,))
            sub_p.start()
            # 这里要关闭父进程中的new_socket,因为创建子进程会复制一份new_socket给子进程
            new_socket.close()

        # 关闭整个SOCKET
        tcp_server_socket.close()


def main():
    wsgi_server = WSGIServer()
    wsgi_server.run_forever()


if __name__ == "__main__":
    main()

在构造函数内创建监听socket实例,然后使用run_forever开始运行。

 

三、静态资源和动态资源

静态资源:例如html页面、css文件、js文件、图片等都属于静态资源。

动态资源:每次请求返回的数据都不一样,例如从数据库中获取的数据,或者变化的时间等,都叫动态资源。


如下图所示:

 

 

解释:

1.当服务器收到请求,判断请求内容为xxx.html、xxxx.css、xxxx.js、xxxx.png等,则直接从磁盘获取静态文件,读取并返回。

2.当服务器收到请求,判断请求内容为xxx.py(或其他自定义的特殊形式),则会调用web框架中的函数来获取数据。

3.HTTP服务器除了返回静态数据,以及从web框架获取reponse header和reponse body,将两者组装起来返回给客户端,他不做其他事情。

 

四、给HTTP服务器加上处理动态请求的逻辑

import socket
import re
import multiprocessing


class WSGIServer(object):
    def __init__(self):
        # 创建socket实例
        self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 设置资源重用
        self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        # 绑定IP端口
        self.tcp_server_socket.bind(("", 7890))
        # 开始监听
        self.tcp_server_socket.listen(128)

    # 请求处理函数
    def handle_request(self, new_socket):
        # 接收请求
        recv_msg = ""
        recv_msg = new_socket.recv(1024).decode("utf-8")
        if recv_msg == "":
            print("recv null")
            new_socket.close()
            return

        # 从请求中解析出URI
        recv_lines = recv_msg.splitlines()
        # 使用正则表达式提取出URI
        ret = re.match(r"[^/]+(/[^ ]*)", recv_lines[0])
        if ret:
            # 获取URI字符串
            file_name = ret.group(1)
            # 如果URI是/,则默认返回index.html的内容
            if file_name == "/":
                file_name = "/index.html"

        if not file_name.endswith(".py"):
            try:
                # 根据请求的URI,读取相应的文件
                fp = open("." + file_name, "rb")
            except:
                # 找不到文件,响应404
                response_msg = "HTTP/1.1 404 NOT FOUND\r\n"
                response_msg += "\r\n"
                response_msg += "<h1>----file not found----</h1>"
                new_socket.send(response_msg.encode("utf-8"))
            else:
                html_content = fp.read()
                fp.close()
                # 响应正确 200 OK
                response_msg = "HTTP/1.1 200 OK\r\n"
                response_msg += "\r\n"

                # 返回响应头
                new_socket.send(response_msg.encode("utf-8"))
                # 返回响应体
                new_socket.send(html_content)
        else:
            header = "HTTP/1.1 200 OK\r\n"
            header += "\r\n"
            body = "dynamic request" + "  %s" % time.ctime()
            response = header + body
            new_socket.send(response.encode("utf-8"))

        # 关闭该次socket连接
        new_socket.close()

    # 开始无限循环,接受请求
    def run_forever(self):
        while True:
            new_socket, client_addr = self.tcp_server_socket.accept()
            # 启动一个子进程来处理客户端的请求
            sub_p = multiprocessing.Process(target=self.handle_request, args=(new_socket,))
            sub_p.start()
            # 这里要关闭父进程中的new_socket,因为创建子进程会复制一份new_socket给子进程
            new_socket.close()

        # 关闭整个SOCKET
        tcp_server_socket.close()


def main():
    wsgi_server = WSGIServer()
    wsgi_server.run_forever()


if __name__ == "__main__":
    main()

添加判断分支,如果请求内容以.py结尾,则判断为动态数据,返回dynamic request + 时间。如下图:

 

 

 

 

五、服务器与动态请求解耦

另外实现一个py模块,叫mini_frame.py

import time


def login():
    return "----login page----\r\n %s" % time.ctime()


def register():
    return "----login page----\r\n %s" % time.ctime()


def application(file_name):
    if file_name == "/login.py":
        return login()
    elif file_name == "register.py":
        return register()
    else:
        return "Not Found You Page..."

服务器一旦判断收到的请求为动态数据请求,则调用application(file_name),并将请求交给mini_frame来处理。

服务器端代码:

import socket
import re
import multiprocessing
import time

import mini_frame


class WSGIServer(object):
    def __init__(self):
        # 创建socket实例
        self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 设置资源重用
        self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        # 绑定IP端口
        self.tcp_server_socket.bind(("", 7890))
        # 开始监听
        self.tcp_server_socket.listen(128)

    # 请求处理函数
    def handle_request(self, new_socket):
        # 接收请求
        recv_msg = ""
        recv_msg = new_socket.recv(1024).decode("utf-8")
        if recv_msg == "":
            print("recv null")
            new_socket.close()
            return

        # 从请求中解析出URI
        recv_lines = recv_msg.splitlines()
        # 使用正则表达式提取出URI
        ret = re.match(r"[^/]+(/[^ ]*)", recv_lines[0])
        if ret:
            # 获取URI字符串
            file_name = ret.group(1)
            # 如果URI是/,则默认返回index.html的内容
            if file_name == "/":
                file_name = "/index.html"

        if not file_name.endswith(".py"):
            try:
                # 根据请求的URI,读取相应的文件
                fp = open("." + file_name, "rb")
            except:
                # 找不到文件,响应404
                response_msg = "HTTP/1.1 404 NOT FOUND\r\n"
                response_msg += "\r\n"
                response_msg += "<h1>----file not found----</h1>"
                new_socket.send(response_msg.encode("utf-8"))
            else:
                html_content = fp.read()
                fp.close()
                # 响应正确 200 OK
                response_msg = "HTTP/1.1 200 OK\r\n"
                response_msg += "\r\n"

                # 返回响应头
                new_socket.send(response_msg.encode("utf-8"))
                # 返回响应体
                new_socket.send(html_content)
        else:
            header = "HTTP/1.1 200 OK\r\n"
            header += "\r\n"
            body = mini_frame.application(file_name)
            response = header + body
            new_socket.send(response.encode("utf-8"))

        # 关闭该次socket连接
        new_socket.close()

    # 开始无限循环,接受请求
    def run_forever(self):
        while True:
            new_socket, client_addr = self.tcp_server_socket.accept()
            # 启动一个子进程来处理客户端的请求
            sub_p = multiprocessing.Process(target=self.handle_request, args=(new_socket,))
            sub_p.start()
            # 这里要关闭父进程中的new_socket,因为创建子进程会复制一份new_socket给子进程
            new_socket.close()

        # 关闭整个SOCKET
        tcp_server_socket.close()


def main():
    wsgi_server = WSGIServer()
    wsgi_server.run_forever()


if __name__ == "__main__":
    main()

服务器只需调用mini_frame.application(file_name),获取body数据即可。

 

六、WSGI介绍

 

如上图所示:

 

 

 

 

标签:MINI,socket,框架,server,new,msg,服务器,recv,response
来源: https://www.cnblogs.com/leokale-zz/p/11966302.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有