首页 > 其他分享 >WSGI协议

WSGI协议

时间:2022-12-02 20:22:55浏览次数:54  
标签:协议 WSGI socket 应用程序 headers environ response conn

一、socket服务器

(一)Web服务器

socket是在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用已实现进程在网络中通信。

socket是应用层和TCP/IP协议中间通信的软件层,它是一组接口,在设计模式中,socket其实就是一个门面模式,它把复杂的TCP/IP协议封装隐藏在socket接口后,让socket去组织数据,以符合指定协议,所以只需遵循socket规定去编程就可以。

如:

  • server.py
import socket

sk = socket.socket()

sk.bind(("127.0.0.1", 9600))

sk.listen()

while True:
    conn, addr = sk.accept()
    data = conn.recv(1024)
    conn.send(b"world")
    conn.close()
  • client.py
import socket

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(("127.0.0.1", 9600))

sock.send(b"hello")
sock.close()

从上面可以看到是基于TCP/IP协议来完成的,而Web服务器本质就是socket服务器,然后浏览器来进行访问,但是浏览器发送什么数据,如何发送,况且浏览器有很多,需要有一个统一的协议来规定,这个就是HTTP协议。

规定了首先返回状态行:

import socket

sk = socket.socket()

sk.bind(("127.0.0.1", 9600))

sk.listen()

while True:
    # 等待连接
    conn, addr = sk.accept()
    # 接收数据
    data = conn.recv(1024)
    # 返回状态行
    conn.send(b"HTTP/1.1 200 OK\r\n\r\n")
    # 返回体
    conn.send(b"world")
    # 关闭连接
    conn.close()

当浏览器访问 http://127.0.0.1:9600/ 后页面就会收到服务器的响应体:world。

(二)模板文件

上述的socket web服务器返回的内容可以写在一个文件中,然后读取文件的内容进行访问:

  • index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>hello world!</h1>
</body>
</html>
  • server.py
import socket

sk = socket.socket()

sk.bind(("127.0.0.1", 9600))

sk.listen()

while True:
    # 等待连接
    conn, addr = sk.accept()
    # 接收数据
    data = conn.recv(1024)
    # 返回状态行
    conn.send(b"HTTP/1.1 200 OK\r\n\r\n")
    # 返回体,读取文件内容
    with open("index.html", mode="rb") as f:
        res = f.read()
    conn.send(res)
    # 关闭连接
    conn.close()

(三)路由系统

import socket

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('127.0.0.1', 9600))
sock.listen()


def index():
    return b"index"


def home():
    return b"home"


urlpattern = [
    ("/index", index),
    ("/home", home)
]

while True:
    conn, addr = sock.accept()
    data = conn.recv(1024)
    data = str(data, encoding="utf8")
    url = data.split("\r\n")[0].split()[1]
    print(url)
    func = None
    for path_tuple in urlpattern:
        if path_tuple[0] == url:
            func = path_tuple[1]
            break
    if func:
        response = func()
    else:
        response = b"404 not found!"
    # 返回响应状态行
    conn.send(b"HTTP/1.1 200 OK\r\n\r\n")
    # 返回响应体
    conn.send(response)
    conn.close()

二、服务器程序和应用程序

(一)WSGI协议

web框架本质就是一个socket服务端。

web框架功能:

  • socket收发消息
  • 根据不同的路径返回不同的内容
  • 可以返回页面

对于真实开发中的python web程序来说,一般会分为两部分:服务器程序和应用程序。

服务器程序负责对socket服务器进行封装,并在请求到来时,对请求的各种数据进行整理。

应用程序则负责具体的逻辑处理。为了方便应用程序的开发,就出现了众多的Web框架,例如:Django、Flask、web.py 等。不同的框架有不同的开发方式,但是无论如何,开发出的应用程序都要和服务器程序配合,才能为用户提供服务。

这样,服务器程序就需要为不同的框架提供不同的支持。这样混乱的局面无论对于服务器还是框架,都是不好的。对服务器来说,需要支持各种不同框架,对框架来说,只有支持它的服务器才能被开发出的应用使用。

这时候,标准化就变得尤为重要。我们可以设立一个标准,只要服务器程序支持这个标准,框架也支持这个标准,那么他们就可以配合使用。一旦标准确定,双方各自实现。这样,服务器可以支持更多支持标准的框架,框架也可以使用更多支持标准的服务器。

WSGI(Web Server Gateway Interface)就是一种规范,它定义了使用Python编写的web应用程序与web服务器程序之间的接口格式,实现web应用程序与web服务器程序间的解耦。

那么WSGI定义的究竟是一种什么样的规范呢?

  • 应用程序/框架
  • 服务器/网关

1、应用程序/框架

应用程序对象是一个接受两个参数的可调用对象。函数、方法、类或带有__call__方法的实例都可以用作应用程序对象。应用程序对象必须能够被多次调用,因为几乎所有服务器/网关(CGI 除外)都会发出此类重复请求。

这是两个示例应用程序对象;一个是函数,另一个是类:

def simple_app(environ, start_response):
    """Simplest possible application object"""
    status = '200 OK'
    response_headers = [('Content-type', 'text/plain')]
    start_response(status, response_headers)
    return ['Hello world!\n']


class AppClass:
    """Produce the same output, but using a class

    (Note: 'AppClass' is the "application" here, so calling it
    returns an instance of 'AppClass', which is then the iterable
    return value of the "application callable" as required by
    the spec.

    If we wanted to use *instances* of 'AppClass' as application
    objects instead, we would have to implement a '__call__'
    method, which would be invoked to execute the application,
    and we would need to create an instance for use by the
    server or gateway.
    """

    def __init__(self, environ, start_response):
        self.environ = environ
        self.start = start_response

    def __iter__(self):
        status = '200 OK'
        response_headers = [('Content-type', 'text/plain')]
        self.start(status, response_headers)
        yield "Hello world!\n"

2、服务器/网关

服务器或网关针对它从 HTTP 客户端接收到的每个针对应用程序的请求调用一次可调用应用程序。为了说明这一点,这里有一个简单的 CGI 网关,实现为采用应用程序对象的函数。如果服务器中触发可调用应用程序,就是可符合WSGI协议的WSGI服务器。

import os, sys

def run_with_cgi(application):

    environ = dict(os.environ.items())
    environ['wsgi.input']        = sys.stdin
    environ['wsgi.errors']       = sys.stderr
    environ['wsgi.version']      = (1, 0)
    environ['wsgi.multithread']  = False
    environ['wsgi.multiprocess'] = True
    environ['wsgi.run_once']     = True

    if environ.get('HTTPS', 'off') in ('on', '1'):
        environ['wsgi.url_scheme'] = 'https'
    else:
        environ['wsgi.url_scheme'] = 'http'

    headers_set = []
    headers_sent = []

    def write(data):
        if not headers_set:
             raise AssertionError("write() before start_response()")

        elif not headers_sent:
             # Before the first output, send the stored headers
             status, response_headers = headers_sent[:] = headers_set
             sys.stdout.write('Status: %s\r\n' % status)
             for header in response_headers:
                 sys.stdout.write('%s: %s\r\n' % header)
             sys.stdout.write('\r\n')

        sys.stdout.write(data)
        sys.stdout.flush()

    def start_response(status, response_headers, exc_info=None):
        if exc_info:
            try:
                if headers_sent:
                    # Re-raise original exception if headers sent
                    raise exc_info[0], exc_info[1], exc_info[2]
            finally:
                exc_info = None     # avoid dangling circular ref
        elif headers_set:
            raise AssertionError("Headers already set!")

        headers_set[:] = [status, response_headers]
        return write

    result = application(environ, start_response) # 触发应用程序
    try:
        for data in result:
            if data:    # don't send headers until body appears
                write(data)
        if not headers_sent:
            write('')   # send headers now if body was empty
    finally:
        if hasattr(result, 'close'):
            result.close()

(二)WSGI服务器

WSGI协议定义了应用程序和服务器之间的规范,符合这个规范即可。

常用的WSGI服务器有uwsgi、Gunicorn。而Python标准库提供的独立WSGI服务器叫wsgiref,Django开发环境用的就是这个模块来做服务器。

 符合WSGI协议的服务器会回调start_response方法:

 result = application(environ, start_response) # 触发应用程序

 总的来说WSGI协议主要有三点:

  • 接受environ 和start_response两个参数
  • 内部调用 start_respons生成header
  • 返回一个可迭代的响应体

那么可以使用Python内置wsgiref模块来实现web服务器:

from wsgiref.simple_server import make_server


def index():
    return b"index"


def home():
    return b"home"


urlpattern = [
    ("/index", index),
    ("/home", home)
]


def simple_app(environ, start_response):
    status = "200 OK"
    response_headers = [('Content-Type', 'text/plain')]
    start_response(status, response_headers)
    current_path = environ.get("PATH_INFO")
    func = None
    for path_tuple in urlpattern:
        if path_tuple[0] == current_path:
            func = path_tuple[1]
            break
    if func:
        response = func()
    else:
        response = b"404 not found!"
    return [response, ]


if __name__ == '__main__':
    srv = make_server("127.0.0.1", 9000, simple_app)
    srv.serve_forever()

 

 

 

标签:协议,WSGI,socket,应用程序,headers,environ,response,conn
From: https://www.cnblogs.com/shenjianping/p/16937088.html

相关文章

  • 【干货】超详细!TPC7062封装MQTT协议教程
    【干货】超详细!TPC7062封装MQTT协议教程一.功能简介通过将报文分解为16进制格式的字符串(比如:101C00044D51545404C2),再通过TPC-7062进行组包,利用串口服务器的TCP/IP协议栈连接......
  • 车载测试系列:蓝牙协议栈
    蓝牙协议栈由主机+HCI(可选)+控制器三大块组成,其中对于单芯片方案是没用HCI的。主机(Host):主机部分由核心协议层(L2CAP、SDP、SMP、ATT)和核心规范(GAP、GATT)构成;......
  • 车载测试系列:蓝牙协议概述
    蓝牙(英语:Bluetooth),一种无线通讯技术标准,用来让固定与移动设备,在短距离间交换资料,以形成个人局域网(PAN)。其使用短波特高频(UHF)无线电波,经由2.4至2.485GHz的ISM频段来进行通......
  • Linux USB驱动分析之USB2.0协议分析
    一条USB传输线分别由地线、电源线、D+和D-四条线构成,D+和D-是差分输入线,它使用的是3.3V的电压(与CMOS的5V电平不同),而电源线和地线可向设备提供5V电压,最大电流为500mA(可以......
  • 车载测试系列:CAN协议之波特率
    位时序分解CAN协议把每一个数据位的时序都分解成了若干个段。其中包括SS、PTS、PBS1、PBS2段,这四段加起来即为一个CAN数据位的长度。分解后最小的时间单位为Tq,一个完整的......
  • HJ212_2017 协议字符串
    #!/usr/bin/python3#coding=utf-8importtime'''由数据字典生成HJ212_2017协议字符串'''defencode(DIC_HJ212_2017):_data=''forkey,val......
  • MODBUS协议下,能否实现MCGS触摸屏与FX5U之间无线通讯?
    在工厂里,触摸屏往往位于程控室内,作为控制多个不同位置PLC的主站设备。因为触摸屏和plc所处位置距离较为分散,重新铺设电缆线工期长,成本高,故采用无线方式解决触摸屏与PLC之间......
  • MODBUS协议下,能否实现MCGS触摸屏与FX5U之间无线通讯?
    在工厂里,触摸屏往往位于程控室内,作为控制多个不同位置PLC的主站设备。因为触摸屏和plc所处位置距离较为分散,重新铺设电缆线工期长,成本高,故采用无线方式解决触摸屏与PLC之间......
  • 物联网安全——本质上是UDP RCE漏洞:tddp 协议,https://www.anquanke.com/post/id/18320
    由一道工控路由器固件逆向题目看命令执行漏洞阅读量349885|评论7|发布时间:2019-08-0116:30:06 前言2019工控安全比赛第一场的一道固件逆向的题目,好像也比......
  • TCP协议
    TCP协议特点使用TCP协议,必须双方先建立连接,它是一种面向连接的可靠通信协议。传输前,采用“三次握手”方式建立连接,所以是可靠的。在连接中可进行大数据量的传输。连接、......