建立自己的 python web 开发知识模型

本人使用 python flask 框架进行 web 开发已经有段时间了,而且也将自己的博客 —— 《藕丝空间》 源码开源到了github —— https://eastossifrage.github.io/pyblog/。但是,随着开发的项目的增多,总是感觉力不从心,感觉 web 的基础知识还有所不足。本文的主要目的就是为了总结知识,建立自己的知识模型。

1 HTTP 协议

1.1 HTTP 介绍

HTTP 协议(HyperText Transfer Protocol,超文本传输协议)是用于从 WWW 服务器传输超文本到本地浏览器的传送协议。它可以使浏览器更加高效,使网络传输减少。它不仅保证计算机正确快速地传输超文本文档,还确定传输文档中的哪一部分,以及哪部分内容首先显示(如文本先于图形)等。

HTTP是一个应用层协议,由请求和响应构成,是一个标准的客户端服务器模型。HTTP是一个无状态的协议。

提示: 说到底,Web应用本质上也是一个socket服务器,用户的浏览器就是一个socket客户端。 只是使用 HTTP 协议对传输的行为进行了限制。

1.2 HTTP 的请求响应模型

HTTP 协议永远都是客户端发起请求(request),服务器回送响应(response)。见下图:

image

这样的结果,就对传输的行为进行了限制,无法实现在客户端没有发起请求的时候,服务器将消息推送给客户。 在 HTML 5 中,websocket 协议突破了该限制。

HTTP协议是一个无状态的协议,同一个客户端的这次请求和上次请求是没有对应关系

1.3 工作流程

一次 HTTP 操作称为一个事务,其工作过程可分为四步:

  1. 首先客户端与服务器需要建立联系,只要单击某个超链接,HTTP 开始工作。
  2. 建立连接后,客户端发送一个请求给服务器,请求方式的格式为:统一资源标示(URL)、协议版本号,后边是 MIME 信息,包括请求修饰符、客户机信息和可能的内容。
  3. 服务器接到请求后,给予相应的响应信息,其格式为一个状态行,包括信息的协议版本号、一个成功或错误的代码,后边是一个 MIME 信息,包括服务器信息、实体信息和可能的内容。
  4. 客户端接收服务器所返回的信息,通过浏览器显示在用户的显示屏上,然后客户机与服务器断开连接。

如果在以上的过程中的某一步出现错误,那么产生错误的信息将返回到客户端,由显示屏输出。对于用户来说,这些过程是由 HTTP 自己完成的,用户只要用鼠标点击,等待信息显示就可以了。

1.4 模拟 web 服务器

正如开篇的 HTTP 介绍中的提示所示,Web应用本质上也是一个socket服务器,用户的浏览器就是一个socket客户端。我们可以使用 python 的 socket 编程来实现一个简单的 web 服务器。

1.4.1 代码

# -*- coding:utf-8 -*-
__author__ = '东方鹗'
__blog__ = 'www.os373.cn'


import socket

def handle_request(client):
    buf = client.recv(1024)
    print("2、客户端发过来的请求内容 %s \r\n" % buf)
    res_header = "HTTP/1.1 200 OK\r\n\r\n"  # 返回客户端HTTP头信息
    client.send(('%s' % res_header).encode())
    msg = "服务器返回的响应内容 >>> %s" % "Hello, World!\r\n"
    client.send(('%s' % msg).encode())

def main():
    ip_port = ("localhost", 64055)
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.bind(ip_port)
    sock.listen(5)
    print("1、web 服务器正在 {} 端口运行......".format(ip_port))

    while True:
        conn, addr = sock.accept()
        handle_request(conn)
        conn.close()

if __name__ == "__main__":
    main()

上述代码中,main()函数就是服务器函数,handle_request()就是用来处理客户端信息的应用程序。

1.4.2 测试代码

代码运行环境是 Ubuntu 17.10。

image

从上图所示并结合代码,我们分析一下这个用 python 代码模拟的 http 请求过程。

  1. 客户端发送请求,发送的内容为 b'GET / HTTP/1.1\r\nHost: localhost:64055\r\nUser-Agent: curl/7.55.1\r\nAccept: /\r\n\r\n'。此处使用 curl 来代替浏览器。
  2. 服务器收到客户端的信息,先返回固定格式的头信息 "HTTP/1.1 200 OK\r\n\r\n",再返回自定义的内容。

个人理解:

  1. 客户端发送固定格式的内容至服务器,服务器首先进行判定客户端是否是浏览器。
  2. 如果是浏览器,服务器则返回固定格式的响应头内容,浏览器会判定返回响应的服务器是否是 web 服务器,如果是,则继续接收服务器返回的响应内容。

本代码实例在服务器不返回固定格式的响应头内容的情况下,curl 客户端将检测到头信息有问题。

image

服务器:注释掉 client.send(('%s' % res_header).encode()) 代码。
客户端:通过 curl -I http://localhost:64055 命令,用于显示服务器端响应的头信息,返回的结果是 curl: (8) Weird server reply ,明显检测到头信息有问题。

如果是真正的浏览器,将会判断服务器端并不是真正的 web 服务器,进而不在显示服务器端返回的响应内容。

1.5 http 协议的局限

在现实需求中,往往需要一种交互式的方案,也就是说,客户端裹挟着特殊的请求,这些请求需要应用程序来甄别处理,然后将响应返回给 WEB 服务器,再由 WEB 服务器返回给客户端。

但是,http 协议仅仅定义了客户端发起请求,服务器端返回响应。WEB 服务器并不会处理客户端中裹挟的特殊请求,这需要由应用程序来实现,至于应用程序如何实现,如何编写,并没有明确的定义,实现起来也是千差万别,兼容性很成问题。

为了定义 Web 服务器和应用程序之间的交互过程,就形成了很多不同的规范。这种规范里最早的一个是 CGI,1993 年开发的。后来又出现了很多这样的规范。比如改进 CGI 性能的 FasgCGI,Java 专用的 Servlet 规范,还有 Python 专用的 WSGI 规范等。提出这些规范的目的就是为了定义统一的标准,提升程序的可移植性。在 WSGI 规范的最开始的 PEP-333 中一开始就描述了为什么需要WSGI规范。

2 CGI 协议

2.1 CGI 介绍

CGI(Common Gateway Interface) 规范允许 web 服务器执行外部程序,并将它们的输出发送给 web 浏览器, CGI 将 web 的一组简单的静态超文本文档编程一个完整的新的交互式媒体。

提示: http 协议仅仅是简单的实现了静态页面的展示功能。

2.2 CGI 功能

绝大多数 CGI 程序被用来解释处理来自表单的输入信息,并在服务器产生相应的处理,或将相应的信息反馈贵浏览器。

CGI 程序使网页具有了交互功能。

对于一个 CGI 程序,做的工作其实只有: 从环境变量(environment variables)标准输入(standard input)中读取数据,处理数据,向标准输出(standard output)输出数据。

环境变量中存储的叫做(Request Meta-Variables),也就是诸如 QUERY_STRING、PATH_INFO 之类的变量,这些由 Web Server 通过环境变量传递给 CGI 程序,CGI 程序也是从环境变量读取的。

标准输入中存放的往往是用户通过 PUTS 或 POST 提交的数据。

一句话总结,CGI 是一个标准,定义了客户端与服务器之间如何传递数据。

3 WSGI 协议

3.1 WSGI 介绍

WSGI 是基于现存的 CGI 标准而设计的。
Web服务器网关接口(Python Web Server Gateway Interface,缩写为WSGI)是为 Python 语言定义的 Web 服务器和 Web 应用程序或框架之间的一种简单而通用的接口。具体的来说,WSGI 是一个规范,定义了 Web 服务器如何与 Python 应用程序进行交互,使得使用 Python 写的 Web 应用程序可以和 Web 服务器对接起来。

3.2 WSGI 如何工作

WSGI 相当于是 Web 服务器和 Python 应用程序之间的桥梁。那么这个桥梁是如何工作的呢?首先,我们明确桥梁的作用,WSGI 存在的目的有两个:

  1. 让 Web 服务器知道如何调用 Python 应用程序,并且把用户的请求告诉应用程序。
  2. 让 Python 应用程序知道用户的具体请求是什么,以及如何返回结果给 Web 服务器。

3.3 实现 WSGI 服务

编写一个新脚本,此处是用 python 已封装好的 wsgiref 模块来写 server 端。我们在此偷个懒,不再使用 socket 模块来重写 server 端,
如有兴趣,请查看 wsgiref 的源码。

# -*- coding:utf-8 -*-
__author__ = '东方鹗'
__blog__ = 'www.os373.cn'

# 首先实现 web 应用程序的 WSGI 处理函数
def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    body = '<h1>Hello, %s!</h1>' % (environ['PATH_INFO'][1:] or 'web')
    return[body.encode('utf-8')]

# 实现 WSGI 服务器
# 从wsgiref模块导入:
from wsgiref.simple_server import make_server

# 创建一个服务器,IP地址为空,端口是64055,处理函数是application:
httpd = make_server('0.0.0.0', 64055, application)
print("Serving HTTP on port 64055...")
# 开始监听HTTP请求:
httpd.serve_forever()
image

4 uWSGI

4.1 uWSGI 介绍

uWSGI 是一个 Web 服务器,它实现了 WSGI 协议、uwsgi、http 等协议。Nginx 中 HttpUwsgiModule 的作用是与 uWSGI 服务器进行交换,也就是说可以用 Nginx + uWSGI 的模式来部署 web 服务。

要注意 WSGI / uwsgi / uWSGI 这三个概念的区分。

  • WSGI 是一种通信协议。
  • uwsgi 也是一种通信协议,在此常用于在uWSGI服务器与其他网络服务器的数据通信。
  • 而 uWSGI 是实现了 uwsgi 和 WSGI 两种协议的 Web 服务器。

uwsgi 协议是一个 uWSGI 服务器自有的协议,它用于定义传输信息的类型(type of information),每一个 uwsgi packet 前 4byte 为传输信息类型描述,它与 WSGI 相比是两样东西。

5 reference

  1. http协议详解(超详细)
  2. HTTP 协议入门
  3. WSGI简介
  4. Web服务器网关接口
  5. Python/WSGI 应用快速入门
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,640评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,254评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,011评论 0 355
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,755评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,774评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,610评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,352评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,257评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,717评论 1 315
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,894评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,021评论 1 350
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,735评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,354评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,936评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,054评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,224评论 3 371
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,974评论 2 355

推荐阅读更多精彩内容

  • 谈论WEB编程的时候常说天天在写CGI,那么CGI是什么呢?可能很多时候并不会去深究这些基础概念,再比如除了CGI...
    __七把刀__阅读 2,197评论 2 11
  • 学过PHP的都了解,php的正式环境部署非常简单,改几个文件就OK,用FastCgi方式也是分分钟的事情。相比起...
    chen_000阅读 2,126评论 0 1
  • 学习 Flask,写完一个 Flask 应用需要部署的时候,就想着折腾自己的服务器。根据搜索的教程照做,对于原理一...
    Cocoa_Coder阅读 17,131评论 4 56
  • 首先,每天早上五点起是一件超牛的事情。正确的事情持续做,就会形成势能。一周读一本书是基本要求,应该被排到日程中去。...
    复苏森林阅读 194评论 0 1
  • 关于陈鹏《先锋意识,都市伦理与当代体验——论陈鹏近期小说创作》
    郑润良阅读 97评论 0 0