WSGI(Web Server Gateway Interface):Web服务网关接口,是Python中定义的服务器程序和应用程序之间的接口。
Web程序开发中,一般分为服务器程序和应用程序。服务器程序负责对socket服务的数据进行封装和整理,而应用程序则负责对Web请求进行逻辑处理。
Web应用本质上也是一个socket服务器,用户的浏览器就是一个socket客户端。
我们先用socket编程实现一个简单的Web服务器:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
import socket
def handle_request(client):
buf = client.recv( 1024 )
print (buf)
msg = "HTTP/1.1 200 OK\r\n\r\n" #HTTP头信息
client.send(( '%s' % msg).encode())
msg = "Hello, World!"
client.send(( '%s' % msg).encode())
def main():
ip_port = ( "localhost" , 8000 )
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(ip_port)
sock.listen( 5 )
while True :
conn, addr = sock.accept()
handle_request(conn)
conn.close()
if __name__ = = "__main__" :
main()
|
上述代码中,main()函数就是服务器函数,handle_request()就是应用程序。
下面我们再用python的wsgiref模块来实现跟上述代码一样的Web服务器:
1
2
3
4
5
6
7
8
9
10
11
|
from wsgiref.simple_server import make_server
def handle_request(env, res):
res( "200 OK" ,[( "Content-Type" , "text/html" )])
body = "<h1>Hello World!</h1>"
return [body.encode( "utf-8" )]
if __name__ = = "__main__" :
httpd = make_server("", 8000 ,handle_request)
print ( "Serving http on port 80000" )
httpd.serve_forever()
|
上面两份代码实现的效果是一样的,调用wsgiref模块则明显节省了代码量,是整个程序更加简洁。
wsgiref模块封装了socket服务端的代码,只留下一个调用的接口,省去了程序员的麻烦,程序员可以将精力放在Web请求的逻辑处理中。
以上述的代码为例,详细看一下wsgiref模块的源码中一些关键的地方:
1
2
3
4
|
if __name__ = = "__main__" :
httpd = make_server("", 8000 ,handle_request)
print ( "Serving http on port 80000" )
httpd.serve_forever()
|
1、整个程序的入口为make_server()函数:
1
2
3
4
5
|
def make_server(host, port, app, server_class = WSGIServer, handler_class = WSGIRequestHandler):
"""Create a new WSGI server listening on `host` and `port` for `app`"""
server = server_class((host, port), handler_class) #默认创建一个WSGIServer类
server.set_app(app) #将应用程序,即逻辑处理函数传给类
return server
|
2、make_server()函数默认生成一个WSGIServer类:
class WSGIServer(HTTPServer):
class HTTPServer(socketserver.TCPServer):
class TCPServer(BaseServer):
WSGIServer,HTTPServer两个类没有初始化函数,调用父类的初始化函数,TCPServer类的__init__()函数拓展了BaseServer
类的__init__()函数:
1
2
3
4
5
6
7
|
#BaseServer类的__init__()函数:
def __init__( self , server_address, RequestHandlerClass):
"""Constructor. May be extended, do not override."""
self .server_address = server_address
self .RequestHandlerClass = RequestHandlerClass
self .__is_shut_down = threading.Event()
self .__shutdown_request = False
|
1
2
3
4
5
6
7
8
9
10
11
12
|
#TCPServer类的__init__()函数:
def __init__( self , server_address, RequestHandlerClass, bind_and_activate = True ):
"""Constructor. May be extended, do not override."""
BaseServer.__init__( self , server_address, RequestHandlerClass)
self .socket = socket.socket( self .address_family, self .socket_type)
if bind_and_activate:
try :
self .server_bind()
self .server_activate()
except :
self .server_close()
raise
|
TCPServer类的初始化函数还调用了server_bind(self),server_bind(self)两个函数:
1
2
3
4
5
6
7
8
9
|
def server_bind( self ):
"""Called by constructor to bind the socket.May be overridden."""
if self .allow_reuse_address:
self .socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1 )
self .socket.bind( self .server_address)
self .server_address = self .socket.getsockname()
def self .server_activate( self ):
"""Called by constructor to activate the server.May be overridden."""
self .socket.listen( self .request_queue_size)
|
可以看到server.bind()函数调用了socket.bind()函数,而server_activate()调用了socket.listen()函数:
3、server.set_app(app),此处传入Web请求的处理逻辑:
1
2
|
def set_app( self ,application):
self .application = application
|
4、httpd.serve_forever()函数调用BaseServer类的_handle_request_noblock()函数处理多路请求:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
def _handle_request_noblock( self ):
try :
request, client_address = self .get_request() #get_request()调用了socket.accept()函数
except OSError:
return
if self .verify_request(request, client_address):
try :
self .process_request(request, client_address)
except :
self .handle_error(request, client_address)
self .shutdown_request(request)
else :
self .shutdown_request(request)
|
1
2
3
4
5
6
7
|
def process_request( self , request, client_address):
self .finish_request(request, client_address)
self .shutdown_request(request) #shutdown_request()调用socket.close()关闭socket
def finish_request( self , request, client_address):
"""Finish one request by instantiating RequestHandlerClass."""
self .RequestHandlerClass(request, client_address, self )
|
5、process_request()函数调用了finish_request()函数,简介调用了make_server函数的默认参数WSGIRequestHandler类:
class WSGIRequestHandler(BaseHTTPRequestHandler):
class BaseHTTPRequestHandler(socketserver.StreamRequestHandler):
class StreamRequestHandler(BaseRequestHandler):
1
2
3
4
5
6
7
8
9
10
|
#调用BaseRequestHandler类的初始化函数:
def __init__( self , request, client_address, server):
self .request = request
self .client_address = client_address
self .server = server
self .setup()
try :
self .handle()
finally :
self .finish()
|
6、初始化函数调用之后调用WSGIRequestHandler类的handle()函数获取server的逻辑处理函数:
1
2
3
4
5
6
7
8
|
def handle( self ):
"""Handle a single HTTP request"""
try :
handler = ServerHandler( self .rfile, stdout, self .get_stderr(), self .get_environ())
handler.request_handler = self # backpointer for logging
handler.run( self .server.get_app()) #此处调用server的逻辑处理函数
finally :
stdout.detach()
|
7、BaseHandler类的handler.run()函数执行逻辑处理:
1
2
3
4
5
6
7
8
9
10
11
|
def run( self , application):
try :
self .setup_environ()
self .result = application( self .environ, self .start_response)
self .finish_response()
except :
try :
self .handle_error()
except :
self .close()
raise # ...and let the actual server figure it out.
|
self.environ:一个包含所有HTTP请求信息的dict对象
self.start_response:一个发送HTTP响应的函数。
在application函数中,调用:
1
|
res( "200 OK" ,[( "Content-Type" , "text/html" )])
|
这样就发送了HTTP响应的头信息
8、BaseHandler类的setup_environ()函数获取HTTP请求的头信息:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
|
def setup_environ( self ):
"""Set up the environment for one request"""
env = self .environ = self .os_environ.copy()
os_environ = read_environ()
read_environ()函数:
def read_environ():
"""Read environment, fixing HTTP variables"""
enc = sys.getfilesystemencoding()
esc = 'surrogateescape'
try :
' '.encode(' utf - 8 ', esc)
except LookupError:
esc = 'replace'
environ = {}
# Take the basic environment from native-unicode os.environ. Attempt to
# fix up the variables that come from the HTTP request to compensate for
# the bytes->unicode decoding step that will already have taken place.
for k, v in os.environ.items():
if _needs_transcode(k):
# On win32, the os.environ is natively Unicode. Different servers
# decode the request bytes using different encodings.
if sys.platform = = 'win32' :
software = os.environ.get( 'SERVER_SOFTWARE' , '').lower()
# On IIS, the HTTP request will be decoded as UTF-8 as long
# as the input is a valid UTF-8 sequence. Otherwise it is
# decoded using the system code page (mbcs), with no way to
# detect this has happened. Because UTF-8 is the more likely
# encoding, and mbcs is inherently unreliable (an mbcs string
# that happens to be valid UTF-8 will not be decoded as mbcs)
# always recreate the original bytes as UTF-8.
if software.startswith( 'microsoft-iis/' ):
v = v.encode( 'utf-8' ).decode( 'iso-8859-1' )
# Apache mod_cgi writes bytes-as-unicode (as if ISO-8859-1) direct
# to the Unicode environ. No modification needed.
elif software.startswith( 'apache/' ):
pass
# Python 3's http.server.CGIHTTPRequestHandler decodes
# using the urllib.unquote default of UTF-8, amongst other
# issues.
elif (
software.startswith( 'simplehttp/' )
and 'python/3' in software
):
v = v.encode( 'utf-8' ).decode( 'iso-8859-1' )
# For other servers, guess that they have written bytes to
# the environ using stdio byte-oriented interfaces, ending up
# with the system code page.
else :
v = v.encode(enc, 'replace' ).decode( 'iso-8859-1' )
# Recover bytes from unicode environ, using surrogate escapes
# where available (Python 3.1+).
else :
v = v.encode(enc, esc).decode( 'iso-8859-1' )
environ[k] = v
return environ
|
9、BaseHandler类的start_response()函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
def start_response( self , status, headers,exc_info = None ):
"""'start_response()' callable as specified by PEP 3333"""
if exc_info:
try :
if self .headers_sent:
# Re-raise original exception if headers sent
raise exc_info[ 0 ](exc_info[ 1 ]).with_traceback(exc_info[ 2 ])
finally :
exc_info = None # avoid dangling circular ref
elif self .headers is not None :
raise AssertionError( "Headers already set!" )
self .status = status
self .headers = self .headers_class(headers)
status = self ._convert_string_type(status, "Status" )
assert len (status)> = 4 , "Status must be at least 4 characters"
assert status[: 3 ].isdigit(), "Status message must begin w/3-digit code"
assert status[ 3 ] = = " " , "Status message must have a space after code"
if __debug__:
for name, val in headers:
name = self ._convert_string_type(name, "Header name" )
val = self ._convert_string_type(val, "Header value" )
return self .write
|
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。
原文链接:http://blog.csdn.net/Liu_Jack/article/details/53643245