Skip to content

Latest commit

 

History

History
268 lines (194 loc) · 9.33 KB

File metadata and controls

268 lines (194 loc) · 9.33 KB

WSGI(web服务器网关接口)

一.服务器静态和动态

静态: 浏览器请求服务器数据,服务器不对数据做任何处理,就返回给浏览器。

动态: 浏览器请求服务器数据,服务器对数据做处理,返回给浏览器。

二.服务器动态资源请求

1.浏览器请求动态页面过程

1.浏览器向web服务器发起http请求动态资源

2.web服务器通过wsgi连接应用程序框架

3.应用程序框架通过引用调用web服务器的方法,设置返回的状态和头信息

4.web服务器保存刚刚设置的信息,并再次调用应用程序框架

5.应用程序框架查询数据库,生成动态页面的body信息

6.应用程序框架把生成的body信息返回给web服务器调用

7.web服务器吧数据返回给浏览器

2.WSGI

怎么在你刚建立的Web服务器上运行一个Django应用Flask应用,如何不做任何改变而适应不同的web架构呢?

在以前,选择 Python web 架构会受制于可用的web服务器,反之亦然。如果架构和服务器可以协同工作,那就好了。但有可能面对(或者曾有过)下面的问题,当要把一个服务器和一个架构结合起来时,却发现他们不是被设计成协同工作的。怎么可以不修改服务器和架构代码而确保可以在多个架构下运行web服务器呢?答案就是 Python Web Server Gateway Interface (web服务器网关接口(或简称 WSGI,读作“wizgy”))。

**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请求。我们来看一个最简单的Web版本的“Hello World!”:

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服务器项目的目的就是做一个既能解析静态网页还可以解析动态网页的服务器

应用程序实例:

demo:

import time

def application(environ, start_response):
    status = '200 OK'
    response_headers = [('Content-Type', 'text/html')]
    start_response(status, response_headers)
    return str(environ) + '==Hello world from a simple WSGI application!--->%s\n' % time.ctime()

三.Web动态服务器-基本实现

1.文件结构

├── web_server.py
├── web
│   └── mini_web.py
└── html
    └── index.html
    .....

2.web/mini_web.py

import time

def application(environ, start_response):
    status = '200 OK'
    response_headers = [('Content-Type', 'text/html')]
    start_response(status, response_headers)
    return str(environ) + '==Hello world from a simple WSGI application!--->%s\n' % time.ctime()

3.web_server.py

# -*- coding:utf-8 -*-
import re
import time
import socket
import multiprocessing
from demo01.web import mini_web


class WsgiServer(object):
    def __init__(self, server_addr):
        """基本信息"""
        # 用来存储所有的新链接的socket
        self.g_socket_list = list()

        # 1.创建服务器socket
        self.server_soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 2.允许立即使用上次绑定的port
        self.server_soc.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        # 3.绑定ip地址和端口号
        self.server_soc.bind(server_addr)

        # 4.设置为被动套接字,并制定长度
        self.server_soc.listen()

    def server_run(self):
        """运行web服务器,等待客户端的链接并为客户端服务"""
        while True:
            new_client_soc, new_client_addr = self.server_soc.accept()
            new_process = multiprocessing.Process(target=self.request_handler, args=(new_client_soc,))
            new_process.start()
            new_client_soc.close()

    def request_handler(self, sock):
        """
        处理请求
        """
        try:
            # 接收网络请求消息
            request_data = sock.recv(1024).decode('utf-8')
            print(request_data)

        except Exception as e:
            print(e)
            print("数据编码错误")

        else:
            # 判断消息是否有数据
            if not request_data:  # 没有消息
                print("client is disconnected。。。。")
            else:
                self.deal_data(request_data, sock)

            sock.close()

    def deal_data(self, request_data, sock):
        """"
        数据处理
        """

        # 切割请求头
        request_header_lines = request_data.splitlines()

        # 打印测试
        for request_header_line in request_header_lines:
            print(request_header_line)

        # 获取请求行行信息:因为它包含请求的资源
        http_request_line = request_header_lines[0]

        # 正则切割出请求资源的文件名,eg:GET / HTTP/1.1
        request_file_name = re.match('[^/]+(/[^ ]*)', http_request_line).group(1)
        print("_" * 100)
        print(request_file_name)

        #  判断是静态还是动态
        if request_file_name.endswith(".py"):  # 动态
            print("动态开始")
            url_dict = dict()
            web = mini_web.application(url_dict, self.set_response_headers)

            # 合并header和body
            response_header = "HTTP/1.1 {status}\r\n".format(status=self.headers[0])
            response_header += "Content-Type: text/html; charset=utf-8\r\n"
            response_header += "Content-Length: %d\r\n" % len(web)
            for temp_head in self.headers[1]:
                response_header += "{0}:{1}\r\n".format(*temp_head)

            response = response_header + "\r\n"
            response += web

            sock.send(response.encode('utf-8'))
            print(web)
            print("动态开始")

        # 静态
        else:
            request_file_name = g_static_document_root + request_file_name
            print(request_file_name)

            # 读取对应文件并返回
            try:
                f = open(request_file_name, 'rb')
            except IOError:
                # 404表示没有这个界面
                # 响应行
                response_header = 'HTTP/1.1 404  not found\r\n'
                # 响应内容
                response_content = '====sorry ,file not found===='.encode('utf-8')
                # 空行
                response_header += '\r\n'

            else:
                # 404表示没有这个界面
                # 响应行
                response_header = 'HTTP/1.1 200 OK\r\n'

                # 响应内容
                response_content = f.read()
                f.close()

            # 响应头
            response_header += 'Server: PythonWebServer1.0\r\n'
            # 解决长连接问题
            response_header += "Content-Type: text/html; charset=utf-8\r\n"
            response_header += "Content-Length:%d\r\n" % len(response_content)

            # 空行
            response_header += '\r\n'

            # 拼接发送
            print(response_header)
            response_data = (response_header.encode('utf-8')) + response_content
            sock.send(response_data)

    def set_response_headers(self, status, headers):
        """这个方法,会在 web框架中被默认调用"""
        response_header_default = [
            ("Data", time.ctime()),
            ("Server", "Test-python mini web server")
        ]

        # 将状态码/相应头信息存储起来
        # [字符串, [xxxxx, xxx2]]
        self.headers = [status, response_header_default + headers]
        print(self.headers)


# 设置服务器端口和ip
SERVER_ADDR = ("", 8080)
# 设置静态资源路径
g_static_document_root = './html/html'

# 设置动态资源访问路径
g_dynamic_document_root = "./web"


def main():
    """程序运行入口"""
    httpd = WsgiServer(SERVER_ADDR)
    httpd.server_run()


if __name__ == '__main__':
    main()