WSGI 协议实现与 Python socket 编程补充说明

2017-11-06 阅读量

偶然看到有人翻译的“一起写一个 web 服务器”系列[1][2][3],感觉挺不错的,这里做一些归纳和 Python socket 编程的补充说明。

WSGI 的工作原理

常见的 Web server 无法与 Web application(Flask, Django, Tornado) 直接通信,需要 WSGI server 作为桥梁。

WSGI 工作原理分为两方面

服务器层

将来自 socket 的数据包解析为 http,调用 application,给应用提供环境信息 environ (包含 host, post, process模式, 客户端的 header, body 等)。同时给应用提供一个 start_response 的回调函数,主要在应用程序层进行响应信息处理。

应用程序层

在 server 提供的 start_response 生成 header, body, status,将这些信息返回给客户端。

WSGI server 代码结构

这部分我想根据 Web 服务器处理发给应用的请求的流程,来说说完整的 server 代码结构:
  • 首先,服务器启动并加载一个由 Web 框架/应用提供的 application - def set_app()
  • 启动 WSGI server 服务,不停监听并获取 socket 数据 - def serve_forever
  • 处理请求:def handle_one_request()
      - 对 socket 数据包进行解析 def parse_request()
      - 创建 environ 字典,提供环境信息 def get_environ()
      - 提供 start_response 回调函数 def start_response()
      - 使用 environ 和 start_response 作为参数调用 application,并拿到返回的响应体
      - 解析一次请求后,关闭 socket 端口,同时将 application 返回的数据返回至客户端 def finish_response()
  • 完整服务器代码

    WSGI app 代码结构

    简单的说就是设置 status, header 等,通过 start_response 传给服务器,构造 http 响应

    代码比较简单:

    def app(environ, start_response):
        status = '200 OK'
        response_headers = [('Content-Type', 'text/plain')]
        start_response(status, response_headers)
        return ['Hello world from a simple WSGI application!\n']
    

    补充:Python socket 编程

    这里主要是对 WSGI_server 中所用的的 socket 模块进行补充
    Socket 模块提供了标准的 BSD Socket API

    Socket 类型

    套接字格式:socket(family, type[,protocal]) 使用给定的套接族,套接字类型,协议编号(默认为0)来创建套接字
    socket.AF_INET : 用于服务器与服务器之间的网络通信
    socket.SOCK_STREAM : 基于 TCP 的流式 socket 通信

    创建 TCP Socket:

    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    

    Socket 函数

    服务端 Socket 函数

    s.listen(backlog) - 开始监听TCP传入连接,backlog指定在拒绝链接前,操作系统可以挂起的最大连接数,该值最少为1,大部分应用程序设为5就够用了

    s.bind(address) - 将套接字绑定到地址,在AF_INET下,以tuple(host, port)的方式传入

    公共 Socket 函数

    s.getsockname() - 返回套接字自己的地址,返回值通常是一个tuple(ipaddr, port)

    s.setsockopt(level,optname,value) - 设置给定套接字选项的值。level 定义了哪个选项将被使用,通常情况下是 SOL_SOCKET。

    s.getfqdn - 返回一个name对应的完全合格的域名。如果name被忽略,将会被解释为本地主机。


    可能将持续补充