WSGI, 可能很多做python web 开发同学的都听过, 但是 WSGI 是什么, 用来干什么,今天我说一些自己的理解,有错误的地方或者描述不当的地方,望指正。
webserver 顾名思义 就是提供web服务, 包括静态页面和动态页面。那么当服务器接受到来自client的请求之后,他是如何和我们的web 应用进行通信的 ,这就是WSGI的作用。 接受HTTP请求 解析HTTP 发送 响应 这些比较底层的工作 就是WSGI的工作。于是 python 就有了WSGI接口 ,那些工作都交给WSGI处理, 而我们更专注用python 来实现业务层面的逻辑。
话不多说,先上一段代码 利用socket 实现一个简易的服务器
import socket
HOST, PORT = 'localhost', 8080
listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
listen_socket.bind((HOST, PORT))
listen_socket.listen(20)
while 1:
connection, address = listen_socket.accept()
request = connection.recv(1024)
line = request.splitlines()
line = line[0].strip('\r\n')
print line
http_response = """\
HTTP/1.1 200 OK
Hello, World!
"""
connection.sendall(http_response)
connection.close()
做过web开发的同学 应该都对HTTP协议有一定的了解, 我在这里只做简单描述
当我们在浏览器输入例如 http://loalhost:5000/test 这样的url 他其实是有 protocol:host_name:port/path 这样的形式组成的
在你的浏览器能够发送 HTTP 请求之前,它需要与 Web 服务器建立一个 TCP 连接。然后会在 TCP 连接中发送 HTTP 请求,并等待服务器返回 HTTP 响应
接下来看看wsgi
# *-* coding: utf-8 *-*
import socket
import StringIO
class WSGIServer(object):
address_family = socket.AF_INET
socket_type = socket.SOCK_STREAM
request_queue_size = 5
def __init__(self, address):
self.socket = socket.socket(self.address_family, self.socket_type)
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.socket.bind(address)
self.socket.listen(self.request_queue_size)
self.host, self.port = self.socket.getsockname()
self.server_name = socket.getfqdn(self.host)
def set_app(self, application):
self.application = application
def server_forever(self):
print "start listting"
while 1:
self.connection, client_address = self.socket.accept()
print "recive a request!"
self.request_handler()
def request_handler(self):
self.request_data = self.connection.recv(1024)
self.parse_request()
env = self.get_env()
result = self.application(env, self.start_response)
self.finish_response(result)
def parse_request(self):
request_data = self.request_data.splitlines()
request_line = request_data[0].strip('\r\n')
self.request_method, self.request_path, self.request_version = request_line.split()
def get_env(self):
env = {}
# WSGI必要参数
env['wsgi.version'] = (1, 0)
env['wsgi.url_scheme'] = 'http'
env['wsgi.input'] = StringIO.StringIO(self.request_data) #返回一个
env['wsgi.errors'] = sys.stderr
env['wsgi.multithread'] = False
env['wsgi.multiprocess'] = False
env['wsgi.run_once'] = False
### CGI 必需变量
env['REQUEST_METHOD'] = self.request_method # GET
env['PATH_INFO'] = self.request_path # 请求路径
env['SERVER_NAME'] = self.server_name # localhost
env['SERVER_PORT'] = str(self.port) # 8888
return env
def start_response(self, status, response_headers):
'''
this function is used to set response_headers and status
'''
self.headers = [status, response_headers]
def finish_response(self, result):
try:
status, headers = self.headers
response = 'HTTP/1.1 {status}\r\n'.format(status=status)
for h in headers:
response += '%s:%s\r\n'%(h)
response += '\r\n'
for data in result:
print data
response += data
self.connection.sendall(response)
finally:
self.connection.close()
ADDRESS = ('localhost', 8888)
if __name__ == '__main__':
import sys
if len(sys.argv) < 2:
sys.exit('must give module:callable')
app_path = sys.argv[1]
path, app = app_path.split(':')
module = __import__(path)
app = getattr(module, app)
server = WSGIServer(ADDRESS)
server.set_app(app)
server.server_forever()
对于这个请求 他的工作 流程大概是这样子的:
或者更详细一点: