Python高级 -- 10 WSGI、mini_frame(web框架)

一、模拟浏览器访问指定页面


1、使用多线程实现动态访问指定页面


server-web.py


# coding=UTF-8
import socket
import re
import multiprocessing


class WSGIServer(object):
    def __init__(self):
        # 1.创建套接字对象
        self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        # 2.绑定端口号
        self.tcp_server_socket.bind(("", 8080))

        # 3.变为监听套接字
        self.tcp_server_socket.listen(128)

    def service_client(self, new_socket):
        """为这个客户端返回数据"""

        # 1.接收浏览器发送的http请求
        request = new_socket.recv(1024).decode("utf-8")

        request_lines = request.splitlines()
        #print("")
        #print(">" * 20)
        #print(request_lines)

        file_name = ""
        ret = re.match(r"[^/]+(/[^ ]*)", request_lines[0])
        #print(ret)
        if ret:
            file_name = ret.group(1)
            print(file_name)
            if file_name == "/":
                file_name = "/index.html"

        # 2.返回http格式的数据给浏览器
        try:
            f = open("./html" + file_name, "rb")
        except:
            response = "HTTP/1.1 404 NOT FOUND\r\n"
            response += "\r\n"
            response += "-----------file not found -------------"
            new_socket.send(response.encode("utf-8"))
        else:
            html_content = f.read()
            f.close()
            # 设置响应头
            response = "HTTP/1.1 200 OK \r\n"
            response += "\r\n"
            # 设置响应体
            new_socket.send(response.encode("utf-8"))
            new_socket.send(html_content)

        # 关闭套接字
        new_socket.close()

    def run_forever(self):
        """用来完成整体的控制"""
        while True:
            # 等待新的客户端进行链接
            new_socket, client_addr = self.tcp_server_socket.accept()

            # 为这个客户端服务
            p = multiprocessing.Process(target=self.service_client, args=(new_socket,))
            p.start()

            new_socket.close()

        # 关闭监听套接字
        self.tcp_server_socket.close()
            


def main():
    """控制整体,创建一个web服务器对象,调用对象中的run_forever方法运行"""
    wsgi_server = WSGIServer()
    wsgi_server.run_forever()


if __name__ == "__main__":
    main()


2、实现web服务器返回动态页面


server-web.py


# coding=UTF-8
import socket
import re
import multiprocessing


class WSGIServer(object):
    def __init__(self):
        # 1.创建套接字对象
        self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        # 2.绑定端口号
        self.tcp_server_socket.bind(("", 8080))

        # 3.变为监听套接字
        self.tcp_server_socket.listen(128)

    def service_client(self, new_socket):
        """为这个客户端返回数据"""

        # 1.接收浏览器发送的http请求
        request = new_socket.recv(1024).decode("utf-8")

        request_lines = request.splitlines()
        #print("")
        #print(">" * 20)
        #print(request_lines)

        file_name = ""
        ret = re.match(r"[^/]+(/[^ ]*)", request_lines[0])
        #print(ret)
        if ret:
            file_name = ret.group(1)
            print(file_name)
            if file_name == "/":
                file_name = "/index.html"

        # 2.返回http格式的数据给浏览器
        # 2.1判断请求名是否是以.py结尾的,如果不是,就说明访问的是静态资源(html css png ...)
        if not file_name.endswith(".py"):
            try:
                f = open("./html" + file_name, "rb")
            except:
                response = "HTTP/1.1 404 NOT FOUND\r\n"
                response += "\r\n"
                response += "-----------file not found -------------"
                new_socket.send(response.encode("utf-8"))
            else:
                html_content = f.read()
                f.close()
                # 设置响应头
                response = "HTTP/1.1 200 OK \r\n"
                response += "\r\n"
                # 设置响应体
                new_socket.send(response.encode("utf-8"))
                new_socket.send(html_content)
        else:
            # 2.2 如果是以.py结尾,那么就认为是动态资源的请求
            header = "HTTP/1.1 200 OK \r\n"
            header += "\r\n"
            body = "give you back"

            response = header + body
            # 发送给浏览器
            new_socket.send(response.encode("utf-8"))

        # 关闭套接字
        new_socket.close()

    def run_forever(self):
        """用来完成整体的控制"""
        while True:
            # 等待新的客户端进行链接
            new_socket, client_addr = self.tcp_server_socket.accept()

            # 为这个客户端服务
            p = multiprocessing.Process(target=self.service_client, args=(new_socket,))
            p.start()

            new_socket.close()

        # 关闭监听套接字
        self.tcp_server_socket.close()
            


def main():
    """控制整体,创建一个web服务器对象,调用对象中的run_forever方法运行"""
    wsgi_server = WSGIServer()
    wsgi_server.run_forever()


if __name__ == "__main__":
    main()


3、实现web服务器和处理逻辑代码分开的功能


server-web.py


# coding=UTF-8
import socket
import re
import multiprocessing
import mini_frame

class WSGIServer(object):
    def __init__(self):
        # 1.创建套接字对象
        self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        # 2.绑定端口号
        self.tcp_server_socket.bind(("", 8080))

        # 3.变为监听套接字
        self.tcp_server_socket.listen(128)

    def service_client(self, new_socket):
        """为这个客户端返回数据"""

        # 1.接收浏览器发送的http请求
        request = new_socket.recv(1024).decode("utf-8")

        request_lines = request.splitlines()
        #print("")
        #print(">" * 20)
        #print(request_lines)

        file_name = ""
        ret = re.match(r"[^/]+(/[^ ]*)", request_lines[0])
        #print(ret)
        if ret:
            file_name = ret.group(1)
            print(file_name)
            if file_name == "/":
                file_name = "/index.html"

        # 2.返回http格式的数据给浏览器
        # 2.1判断请求名是否是以.py结尾的,如果不是,就说明访问的是静态资源(html css png ...)
        if not file_name.endswith(".py"):
            try:
                f = open("./html/" + file_name, "rb")
            except:
                response = "HTTP/1.1 404 NOT FOUND\r\n"
                response += "\r\n"
                response += "-----------file not found -------------"
                new_socket.send(response.encode("utf-8"))
            else:
                html_content = f.read()
                f.close()
                # 设置响应头
                response = "HTTP/1.1 200 OK \r\n"
                response += "\r\n"
                # 设置响应体
                new_socket.send(response.encode("utf-8"))
                new_socket.send(html_content)
        else:
            # 2.2 如果是以.py结尾,那么就认为是动态资源的请求
            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"))

        # 关闭套接字
        new_socket.close()

    def run_forever(self):
        """用来完成整体的控制"""
        while True:
            # 等待新的客户端进行链接
            new_socket, client_addr = self.tcp_server_socket.accept()

            # 为这个客户端服务
            p = multiprocessing.Process(target=self.service_client, args=(new_socket,))
            p.start()

            new_socket.close()

        # 关闭监听套接字
        self.tcp_server_socket.close()
            


def main():
    """控制整体,创建一个web服务器对象,调用对象中的run_forever方法运行"""
    wsgi_server = WSGIServer()
    wsgi_server.run_forever()


if __name__ == "__main__":
    main()


mini_frame.py


def login():
    return "this is login"

def register():
    return "this is register"

def profile():
    return "this is profile"

def application(file_name):
    if file_name == "/login.py":
        return login()
    elif file_name == "/register.py":
        return register()
    else:
        return "the page you found is not exist"


二、WSGI


1、什么是WSGI协议


(1)、浏览器请求页面的过程



(2)、WSGI的好处


        WSGI允许开发者将选择web框架和web服务器分开。可以混合匹配web服务器和web框架,选择一个适合的配对。比如,可以在Gunicorn 或者 Nginx/uWSGI 或者 Waitress上运行 Django, Flask, 或 Pyramid。真正的混合匹配,得益于WSGI同时支持服务器和架构。


        web服务器必须具备WSGI接口,所有的现代Python Web框架都已具备WSGI接口,它让你不对代码作修改就能使服务器和特点的web框架协同工作。


        WSGI由web服务器支持,而web框架允许你选择适合自己的配对,但它同样对于服务器和框架开发者提供便利使他们可以专注于自己偏爱的领域和专长而不至于相互牵制。其他语言也有类似接口:java有Servlet API,Ruby 有 Rack。


(3)、WSGI接口的定义


WSGI接口定义非常简单,它只要求Web开发者实现一个函数,就可以响应HTTP请求。接口Demo如下:


def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    return 'Hello World!'


函数解释:上面的application()函数就是符合WSGI标准的一个HTTP处理函数,它接收两个参数:


        environ:一个包含所有HTTP请求信息的dict对象;


        start_response:一个发送HTTP响应的函数。


        整个application()函数本身没有涉及到任何解析HTTP的部分,也就是说,把底层web服务器解析部分和应用程序逻辑部分进行了分离,这样开发者就可以专心做一个领域了。


        这个application()函数怎么调用?如果我们自己调用,两个参数environ和start_response我们没法提供,返回的str也没法发给浏览器。所以application()函数必须由WSGI服务器来调用。有很多符合WSGI规范的服务器。而我们此时的web服务器项目的目的就是做一个既能解析静态网页还可以解析动态网页的服务器


2、实现引入WSGI协议的web服务器功能


web-server.py


# coding=UTF-8
import socket
import re
import multiprocessing
import mini_frame

class WSGIServer(object):
    def __init__(self):
        # 1.创建套接字对象
        self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        # 2.绑定端口号
        self.tcp_server_socket.bind(("", 8080))

        # 3.变为监听套接字
        self.tcp_server_socket.listen(128)

    def service_client(self, new_socket):
        """为这个客户端返回数据"""

        # 1.接收浏览器发送的http请求
        request = new_socket.recv(1024).decode("utf-8")

        request_lines = request.splitlines()
        #print("")
        #print(">" * 20)
        #print(request_lines)

        file_name = ""
        ret = re.match(r"[^/]+(/[^ ]*)", request_lines[0])
        #print(ret)
        if ret:
            file_name = ret.group(1)
            print(file_name)
            if file_name == "/":
                file_name = "/index.html"

        # 2.返回http格式的数据给浏览器
        # 2.1判断请求名是否是以.py结尾的,如果不是,就说明访问的是静态资源(html css png ...)
        if not file_name.endswith(".py"):
            try:
                f = open("./html/" + file_name, "rb")
            except:
                response = "HTTP/1.1 404 NOT FOUND\r\n"
                response += "\r\n"
                response += "-----------file not found -------------"
                new_socket.send(response.encode("utf-8"))
            else:
                html_content = f.read()
                f.close()
                # 设置响应头
                response = "HTTP/1.1 200 OK \r\n"
                response += "\r\n"
                # 设置响应体
                new_socket.send(response.encode("utf-8"))
                new_socket.send(html_content)
        else:
            # 2.2 如果是以.py结尾,那么就认为是动态资源的请求
            evn = dict()
            body = mini_frame.application(evn, self.set_response_header)

            header = "HTTP/1.1 %s \r\n" % self.status

            for temp in self.headers:
                header += "%s:%s\r\n" % (temp[0], temp[1])

            header += "\r\n"

            response = header + body
            # 发送给浏览器
            new_socket.send(response.encode("utf-8"))

        # 关闭套接字
        new_socket.close()
    
    def set_response_header(self, status, headers):
        self.status = status
        self.headers = headers

    def run_forever(self):
        """用来完成整体的控制"""
        while True:
            # 等待新的客户端进行链接
            new_socket, client_addr = self.tcp_server_socket.accept()

            # 为这个客户端服务
            p = multiprocessing.Process(target=self.service_client, args=(new_socket,))
            p.start()

            new_socket.close()

        # 关闭监听套接字
        self.tcp_server_socket.close()
            


def main():
    """控制整体,创建一个web服务器对象,调用对象中的run_forever方法运行"""
    wsgi_server = WSGIServer()
    wsgi_server.run_forever()


if __name__ == "__main__":
    main()


mini_frame.py


def application(environ, start_response):
    start_response('200 OK', [('Content-Type','text/html')])
    return "Hello World!!!"


3、web服务器与逻辑代码分离的动态访问指定页面


web-server.py


# coding=UTF-8
import socket
import re
import multiprocessing
import mini_frame

class WSGIServer(object):
    def __init__(self):
        # 1.创建套接字对象
        self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        # 2.绑定端口号
        self.tcp_server_socket.bind(("", 8080))

        # 3.变为监听套接字
        self.tcp_server_socket.listen(128)

    def service_client(self, new_socket):
        """为这个客户端返回数据"""

        # 1.接收浏览器发送的http请求
        request = new_socket.recv(1024).decode("utf-8")

        request_lines = request.splitlines()
        #print("")
        #print(">" * 20)
        #print(request_lines)

        file_name = ""
        ret = re.match(r"[^/]+(/[^ ]*)", request_lines[0])
        #print(ret)
        if ret:
            file_name = ret.group(1)
            print(file_name)
            if file_name == "/":
                file_name = "/index.html"

        # 2.返回http格式的数据给浏览器
        # 2.1判断请求名是否是以.py结尾的,如果不是,就说明访问的是静态资源(html css png ...)
        if not file_name.endswith(".py"):
            try:
                f = open("./html/" + file_name, "rb")
            except:
                response = "HTTP/1.1 404 NOT FOUND\r\n"
                response += "\r\n"
                response += "-----------file not found -------------"
                new_socket.send(response.encode("utf-8"))
            else:
                html_content = f.read()
                f.close()
                # 设置响应头
                response = "HTTP/1.1 200 OK \r\n"
                response += "\r\n"
                # 设置响应体
                new_socket.send(response.encode("utf-8"))
                new_socket.send(html_content)
        else:
            # 2.2 如果是以.py结尾,那么就认为是动态资源的请求
            evn = dict()

            # 将用户访问的页面信息放入字典中,传递给框架
            evn['PATH_INFO'] = file_name

            body = mini_frame.application(evn, self.set_response_header)

            header = "HTTP/1.1 %s \r\n" % self.status

            for temp in self.headers:
                header += "%s:%s\r\n" % (temp[0], temp[1])

            header += "\r\n"

            response = header + body
            # 发送给浏览器
            new_socket.send(response.encode("utf-8"))

        # 关闭套接字
        new_socket.close()
    
    def set_response_header(self, status, headers):
        self.status = status
        self.headers = headers

    def run_forever(self):
        """用来完成整体的控制"""
        while True:
            # 等待新的客户端进行链接
            new_socket, client_addr = self.tcp_server_socket.accept()

            # 为这个客户端服务
            p = multiprocessing.Process(target=self.service_client, args=(new_socket,))
            p.start()

            new_socket.close()

        # 关闭监听套接字
        self.tcp_server_socket.close()
            


def main():
    """控制整体,创建一个web服务器对象,调用对象中的run_forever方法运行"""
    wsgi_server = WSGIServer()
    wsgi_server.run_forever()


if __name__ == "__main__":
    main()


mini_frame.py


# coding=UTF-8
def index():
    return "这是index页面"

def login():
    return "这是登录页面"

def application(env, start_response):
    start_response('200 OK', [('Content-Type','text/html;charset=utf-8')])
    
    file_name = env['PATH_INFO']

    if file_name == "/index.py":
        return index()
    elif file_name == "/login.py":
        return login()
    else:
        return "我爱你中国..."


4、mini_frame获取模版页面数据


静态资源文件位置:https://download.csdn.net/download/wingzhezhe/10295146




web-server.py


# coding=UTF-8
import socket
import re
import multiprocessing
import dynamic.mini_frame

class WSGIServer(object):
    def __init__(self):
        # 1.创建套接字对象
        self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        # 2.绑定端口号
        self.tcp_server_socket.bind(("", 8080))

        # 3.变为监听套接字
        self.tcp_server_socket.listen(128)

    def service_client(self, new_socket):
        """为这个客户端返回数据"""

        # 1.接收浏览器发送的http请求
        request = new_socket.recv(1024).decode("utf-8")

        request_lines = request.splitlines()
        #print("")
        #print(">" * 20)
        #print(request_lines)

        file_name = ""
        ret = re.match(r"[^/]+(/[^ ]*)", request_lines[0])
        #print(ret)
        if ret:
            file_name = ret.group(1)
            print(file_name)
            if file_name == "/":
                file_name = "/index.html"

        # 2.返回http格式的数据给浏览器
        # 2.1判断请求名是否是以.py结尾的,如果不是,就说明访问的是静态资源(html css png ...)
        if not file_name.endswith(".py"):
            try:
                f = open("./static" + file_name, "rb")
            except:
                response = "HTTP/1.1 404 NOT FOUND\r\n"
                response += "\r\n"
                response += "-----------file not found -------------"
                new_socket.send(response.encode("utf-8"))
            else:
                html_content = f.read()
                f.close()
                # 设置响应头
                response = "HTTP/1.1 200 OK \r\n"
                response += "\r\n"
                # 设置响应体
                new_socket.send(response.encode("utf-8"))
                new_socket.send(html_content)
        else:
            # 2.2 如果是以.py结尾,那么就认为是动态资源的请求
            evn = dict()

            # 将用户访问的页面信息放入字典中,传递给框架
            evn['PATH_INFO'] = file_name

            body = dynamic.mini_frame.application(evn, self.set_response_header)

            header = "HTTP/1.1 %s \r\n" % self.status

            for temp in self.headers:
                header += "%s:%s\r\n" % (temp[0], temp[1])

            header += "\r\n"

            response = header + body
            # 发送给浏览器
            new_socket.send(response.encode(""))

        # 关闭套接字
        new_socket.close()
    
    def set_response_header(self, status, headers):
        self.status = status
        self.headers = headers

    def run_forever(self):
        """用来完成整体的控制"""
        while True:
            # 等待新的客户端进行链接
            new_socket, client_addr = self.tcp_server_socket.accept()

            # 为这个客户端服务
            p = multiprocessing.Process(target=self.service_client, args=(new_socket,))
            p.start()

            new_socket.close()

        # 关闭监听套接字
        self.tcp_server_socket.close()
            


def main():
    """控制整体,创建一个web服务器对象,调用对象中的run_forever方法运行"""
    wsgi_server = WSGIServer()
    wsgi_server.run_forever()


if __name__ == "__main__":
    main()


mini_frame.py


# coding=UTF-8
def index():
    with open("./templates/index.html") as f:
        return f.read()

def center():
    with open("./templates/center.html") as f:
        return f.read()

def application(env, start_response):
    start_response('200 OK', [('Content-Type','text/html;charset=utf-8')])
    
    file_name = env['PATH_INFO']

    if file_name == "/index.py":
        return index()
    elif file_name == "/center.py":
        return center()
    else:
        return "我爱你中国..."


5、替换模版中的数据


修改index.html这种的数据




修改mini_frame.py中的代码




访问浏览器显示效果




6、执行运行命令时动态指定服务器端口和要调用的框架名称


web-server.py中代码




# coding=UTF-8
import socket
import re
import sys
import multiprocessing
# import dynamic.mini_frame

class WSGIServer(object):
    def __init__(self, port, app):
        # 1.创建套接字对象
        self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        # 2.绑定端口号
        self.tcp_server_socket.bind(("", port))

        # 3.变为监听套接字
        self.tcp_server_socket.listen(128)

        self.application = app

    def service_client(self, new_socket):
        """为这个客户端返回数据"""

        # 1.接收浏览器发送的http请求
        request = new_socket.recv(1024).decode("utf-8")

        request_lines = request.splitlines()
        #print("")
        #print(">" * 20)
        #print(request_lines)

        file_name = ""
        ret = re.match(r"[^/]+(/[^ ]*)", request_lines[0])
        #print(ret)
        if ret:
            file_name = ret.group(1)
            print(file_name)
            if file_name == "/":
                file_name = "/index.html"

        # 2.返回http格式的数据给浏览器
        # 2.1判断请求名是否是以.py结尾的,如果不是,就说明访问的是静态资源(html css png ...)
        if not file_name.endswith(".py"):
            try:
                f = open("./static" + file_name, "rb")
            except:
                response = "HTTP/1.1 404 NOT FOUND\r\n"
                response += "\r\n"
                response += "-----------file not found -------------"
                new_socket.send(response.encode("utf-8"))
            else:
                html_content = f.read()
                f.close()
                # 设置响应头
                response = "HTTP/1.1 200 OK \r\n"
                response += "\r\n"
                # 设置响应体
                new_socket.send(response.encode("utf-8"))
                new_socket.send(html_content)
        else:
            # 2.2 如果是以.py结尾,那么就认为是动态资源的请求
            evn = dict()

            # 将用户访问的页面信息放入字典中,传递给框架
            evn['PATH_INFO'] = file_name

            body = self.application(evn, self.set_response_header)

            header = "HTTP/1.1 %s \r\n" % self.status

            for temp in self.headers:
                header += "%s:%s\r\n" % (temp[0], temp[1])

            header += "\r\n"

            response = header + body
            # 发送给浏览器
            new_socket.send(response.encode("utf-8"))

        # 关闭套接字
        new_socket.close()
    
    def set_response_header(self, status, headers):
        self.status = status
        self.headers = headers

    def run_forever(self):
        """用来完成整体的控制"""
        while True:
            # 等待新的客户端进行链接
            new_socket, client_addr = self.tcp_server_socket.accept()

            # 为这个客户端服务
            p = multiprocessing.Process(target=self.service_client, args=(new_socket,))
            p.start()

            new_socket.close()

        # 关闭监听套接字
        self.tcp_server_socket.close()
            


def main():
    """控制整体,创建一个web服务器对象,调用对象中的run_forever方法运行"""

    # sys模块中的argv:获取python命令行中的数据,
    # 即:如果命令行是 python3 test.py aaa bbb  则,获取到的sys.argv为['test.py', 'aaa','bbb']
    
    if len(sys.argv) == 3:
        try:
            # 获取参数中的端口号
            port = int(sys.argv[1])

            # 获取参数中的框架名
            frame_py_name = sys.argv[2] # py_name:fun_name
        except Exception as e:
            print("端口格式有误")
            return
    else:
        print("输入的命令行格式不正确,请按照一下方式运行:")
        print("python3 py文件 端口号 框架的文件名:方法名")
        return 

    """使用正则获取框架名和方法名"""
    ret = re.match(r"([^:]+):(.*)", frame_py_name)
    if ret:
        frame_name = ret.group(1)
        fun_name = ret.group(2)
        

    else:
        print("输入的命令行格式不正确,请按照一下方式运行:")
        print("python3 py文件 端口号 框架的文件名:方法名")
        return 
    
    """定位到模块所在的文件夹"""
    sys.path.append("./dynamic")

    """ import 可以写在函数中 """

    frame = __import__(frame_name)  # 该方法返回值标记着导入的模块对象

    # 在模块对象中根据名称找到对应的方法
    app = getattr(frame, fun_name)


    wsgi_server = WSGIServer(port, app)
    wsgi_server.run_forever()


if __name__ == "__main__":
    main()


运行命令


root@ubuntu:~/Desktop/web/08-运行服务器时指定端口以及框架# python3 web-server.py 8080 mini_frame:application


7、让web服务器支持配置文件


在web-server.py同级文件夹中添加配置文件web-server.conf




修改web-server.py中的代码




# coding=UTF-8
import socket
import re
import sys
import multiprocessing
# import dynamic.mini_frame

class WSGIServer(object):
    def __init__(self, port, app, static_path):
        # 1.创建套接字对象
        self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        # 2.绑定端口号
        self.tcp_server_socket.bind(("", port))

        # 3.变为监听套接字
        self.tcp_server_socket.listen(128)

        self.application = app
        self.static_path = static_path

    def service_client(self, new_socket):
        """为这个客户端返回数据"""

        # 1.接收浏览器发送的http请求
        request = new_socket.recv(1024).decode("utf-8")

        request_lines = request.splitlines()
        #print("")
        #print(">" * 20)
        #print(request_lines)

        file_name = ""
        ret = re.match(r"[^/]+(/[^ ]*)", request_lines[0])
        #print(ret)
        if ret:
            file_name = ret.group(1)
            print(file_name)
            if file_name == "/":
                file_name = "/index.html"

        # 2.返回http格式的数据给浏览器
        # 2.1判断请求名是否是以.py结尾的,如果不是,就说明访问的是静态资源(html css png ...)
        if not file_name.endswith(".py"):
            try:
                f = open(self.static_path + file_name, "rb")
            except:
                response = "HTTP/1.1 404 NOT FOUND\r\n"
                response += "\r\n"
                response += "-----------file not found -------------"
                new_socket.send(response.encode("utf-8"))
            else:
                html_content = f.read()
                f.close()
                # 设置响应头
                response = "HTTP/1.1 200 OK \r\n"
                response += "\r\n"
                # 设置响应体
                new_socket.send(response.encode("utf-8"))
                new_socket.send(html_content)
        else:
            # 2.2 如果是以.py结尾,那么就认为是动态资源的请求
            evn = dict()

            # 将用户访问的页面信息放入字典中,传递给框架
            evn['PATH_INFO'] = file_name

            body = self.application(evn, self.set_response_header)

            header = "HTTP/1.1 %s \r\n" % self.status

            for temp in self.headers:
                header += "%s:%s\r\n" % (temp[0], temp[1])

            header += "\r\n"

            response = header + body
            # 发送给浏览器
            new_socket.send(response.encode("utf-8"))

        # 关闭套接字
        new_socket.close()
    
    def set_response_header(self, status, headers):
        self.status = status
        self.headers = headers

    def run_forever(self):
        """用来完成整体的控制"""
        while True:
            # 等待新的客户端进行链接
            new_socket, client_addr = self.tcp_server_socket.accept()

            # 为这个客户端服务
            p = multiprocessing.Process(target=self.service_client, args=(new_socket,))
            p.start()

            new_socket.close()

        # 关闭监听套接字
        self.tcp_server_socket.close()
            


def main():
    """控制整体,创建一个web服务器对象,调用对象中的run_forever方法运行"""

    # sys模块中的argv:获取python命令行中的数据,
    # 即:如果命令行是 python3 test.py aaa bbb  则,获取到的sys.argv为['test.py', 'aaa','bbb']
    
    if len(sys.argv) == 3:
        try:
            # 获取参数中的端口号
            port = int(sys.argv[1])

            # 获取参数中的框架名
            frame_py_name = sys.argv[2] # py_name:fun_name
        except Exception as e:
            print("端口格式有误")
            return
    else:
        print("输入的命令行格式不正确,请按照一下方式运行:")
        print("python3 py文件 端口号 框架的文件名:方法名")
        return 

    """使用正则获取框架名和方法名"""
    ret = re.match(r"([^:]+):(.*)", frame_py_name)
    if ret:
        frame_name = ret.group(1)
        fun_name = ret.group(2)
        
    else:
        print("输入的命令行格式不正确,请按照一下方式运行:")
        print("python3 py文件 端口号 框架的文件名:方法名")
        return 

    with open("./web-server.conf") as f:
        conf_info = eval(f.read())
        """
            从配置文件中读出来的是字典格式的数据,但不是字典类型
            用 eval( ) 函数将数据转换为原来的类型,即字典类型
            此时,conf_info 是一个字典类型的变量
            
        """
    
    """定位到模块所在的文件夹"""
    sys.path.append(conf_info['dynamic_path'])

    """ import 可以写在函数中 """

    frame = __import__(frame_name)  # 该方法返回值标记着导入的模块对象

    # 在模块对象中根据名称找到对应的方法
    app = getattr(frame, fun_name)


    wsgi_server = WSGIServer(port, app, conf_info['static_path'])
    wsgi_server.run_forever()


if __name__ == "__main__":
    main()


8、将运行服务器的命令写在shell脚本中




猜你喜欢

转载自blog.csdn.net/wingzhezhe/article/details/79604705