web server是运行在一个物理服务器上的网络服务,关心的是 HTTP 协议层面的传输和访问控制。服务端等待客户端请求,接收到一个请求,就会生成一个响应并回发给客户端。客户端和服务器使用HTTP协议通信。客户端可以是浏览器或者别的使用HTTP协议的软件。常见的web server有apache、nginx、gunicorn等。
import socket
HOST, PORT = '', 8888
listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
listen_socket.bind((HOST, PORT))
listen_socket.listen(1)
print 'Serving HTTP on port %s ...' % PORT
while True:
client_connection, client_address = listen_socket.accept()
request = client_connection.recv(1024)
print request
http_response = """
HTTP/1.1 200 OK
Hello, World!
"""
client_connection.sendall(http_response)
client_connection.close()
$ curl http:/127.0.0.1:8888
HTTP/1.1 200 OK
Hello, World!
Web服务器创建一个监听socket然后开始循环接受新连接。客户端初始化一个TCP连接,在连接成功后,客户端发送HTTP请求到服务器,服务器响应一个显示给用户的HTTP响应。客户端和服务器都使用socket建立TCP连接。
WSGI的全称是Web Server Gateway Interface,这是一个规范,约定了web服务器怎么调用web应用程序的代码、web应用如何处理请求。只要web应用程序和web服务器都遵守WSGI 协议,那么,web应用程序和web服务器就可以随意的组合。
WSGI接口定义非常简单,它只要求开发者实现一个函数,就可以响应HTTP请求。
def application(env, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
return 'Hello, World!
'
上面例子中application()函数就是符合WSGI标准的一个HTTP处理函数,它接收两个参数:
1. environ:一个包含所有HTTP请求信息的dict对象,包含了请求的所有信息。
2. start_response:start_response 是 application 处理完之后需要调用的函数,用于发送HTTP响应。在application()函数中,调用:
start_response('200 OK', [('Content-Type', 'text/html')])
就发送了HTTP响应的Header(注意Header只能发送一次,也就是只能调用一次start_response()函数)。这里start_response()函数接收两个参数,一个是HTTP响应码,一个是一组list表示的HTTP Header,每个Header用一个包含两个str的tuple表示。
函数的返回值
将作为HTTP响应的Body发送给浏览器。Hello, web!
有了WSGI,我们关心的就是如何从environ这个dict对象拿到HTTP请求信息,然后构造HTML,通过start_response()发送Header,最后返回Body。整个application()函数本身没有涉及到任何解析HTTP的部分,也就是说,底层代码不需要我们自己编写,我们只负责在更高层次上考虑如何响应请求就可以了。
那么,这个application()函数怎么调用?
如果我们自己调用,两个参数environ和start_response我们没法提供,返回的str也没法发给浏览器。所以application()函数必须由实现了WSGI的服务器来调用。Python中实现了WSGI的模块/库有 wsgiref (python内置)、 werkzeug.serving 、 twisted.web 等。wsgiref是Python内置了一个简单的WSGI服务器。
# coding: utf-8
# wsgi_demo.py
from wsgiref.simple_server import make_server
def application(env, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
return 'Hello, World!
'
httpd = make_server('', 8888, application)
print 'server on port 8888'
httpd.serve_forever()
WSGI函数来处理太底层,要处理很多细节。于是,在WSGI之上再抽象出了web framework,进一步简化Web开发。
Web框架的作用主要是方便我们开发 web应用程序,HTTP请求的动态数据就是由 web框架层来提供的。WSGI层是框架的开发者关心的层面,框架需要为服务器提供标准的接口。对于 web框架的使用者来说,不需要关心如何接收 HTTP 请求,也不用关心如何将请求路由到具体方法处理并将响应结果返回给用户。Web框架的使用者在大部分情况下,只需要关心如何实现业务的逻辑即可。
我们基于web framework开发 web application,server和 application 通过WSGI 的进行通信。常见的运行在WSGI之上的web框架有Bottle、Flask、Django等。
# coding: utf-8
from wsgiref.simple_server import make_server
from flask import Flask
from flask import Response
flask_app = Flask('flaskapp')
@flask_app.route('/hello')
def hello_world():
return Response(
'Hello world from Flask!\n',
mimetype='text/plain'
)
app = flask_app.wsgi_app
httpd = make_server('', 8888, app)
print 'server on port 8888'
httpd.serve_forever()
WSGI把Web服务器和Web框架结合起来:WSGI server所做的工作仅仅是将从客户端收到的请求传递给WSGI application,然后将WSGI application的返回值作为响应传给客户端。有了这一层抽象,服务器可以在不修改代码的情况下,使用不同的Web框架。