python网络-动态Web服务器案例(30)
一、浏览器请求html页面的过程
了解了http协议和html文档,其实就明白了一个web应用的本质就是:
-
浏览器发送一个http请求;
-
服务器收到请求,生成一个html文档;
-
服务器把html文档作为http响应的body发送给浏览器;
-
浏览器收到http响应,从http body取出html文档并显示。
二、浏览器请求动态页面的过程
三、wsgi
1、wsgi介绍
pythonweb服务器网关接口(python web server gateway interface,缩写为wsgi)是python应用程序或框架和web服务器之间的一种接口,
wsgi接口定义非常简单,它只要求web开发者实现一个函数,就可以响应http请求。
# wsgi 规范的函数
def application(environ, start_response): start_response('200 ok', [('content-type', 'text/html')]) return '<h1>hello, se7en_hou!</h1>'
上面的application()
函数就是符合wsgi标准的一个http处理函数,它接收两个参数:
-
-
environ:一个包含所有http请求信息的
dict
对象; -
start_response:一个发送http响应的函数。
-
在application()
函数中,调用:
start_response('200 ok', [('content-type', 'text/html')])
2、运行wsgi服务
1、 python内置了一个wsgi服务器,这个模块叫wsgiref,首先我们先实现一个hello.py文件,实现web应用程序的wsgi处理函数
def application(environ, start_response): start_response("200 ok",[("content-type","text/html")]) return "<h1>hello,se7en_hou!</h1>"
2、然后,再编写一个server.py
,负责启动wsgi服务器,加载application()
函数:
#coding:utf-8 # 导入wsgiref模块 from wsgiref.simple_server import make_server from hello import application # 创建一个服务器,ip地址为空,端口号为7788,处理的函数是application httpserver = make_server("", 7788, application) # 开始监听http请求 httpserver.serve_forever()
3、确保以上两个文件在同一个目录下,然后使用命令行输入python server.py
来启动wsgi服务器:
houleidemacpro:wsgi se7en_hou$ python server.py 127.0.0.1 - - [19/jun/2019 15:52:37] "get / http/1.1" 200 24 127.0.0.1 - - [19/jun/2019 15:52:37] "get /favicon.ico http/1.1" 200 24
4、 按ctrl+c
终止服务器。如果你觉得这个web应用太简单了,可以稍微改造一下,从environ
里读取path_info
,这样可以显示更加动态的内容:
def application(environ, start_response): start_response("200 ok",[("content-type","text/html")]) return "<h1>hello,%s</h1>"%(environ["path_info"][1:] or "se7en_hou")
5、你可以在地址栏输入用户名作为url的一部分,将返回hello, xxx
四、动态web服务器案例
1、daynamic.py
#coding=utf-8 import socket import sys from multiprocessing import process import re class wsgiserver(object): addressfamily = socket.af_inet sockettype = socket.sock_stream requestqueuesize = 5 def __init__(self, serveraddress): #创建一个tcp套接字 self.listensocket = socket.socket(self.addressfamily,self.sockettype) #允许重复使用上次的套接字绑定的port self.listensocket.setsockopt(socket.sol_socket, socket.so_reuseaddr, 1) #绑定 self.listensocket.bind(serveraddress) #变为被动,并制定队列的长度 self.listensocket.listen(self.requestqueuesize) self.servrname = "localhost" self.serverport = serveraddress[1] def serveforever(self): '循环运行web服务器,等待客户端的链接并为客户端服务' while true: #等待新客户端到来 self.clientsocket, client_address = self.listensocket.accept() #方法2,多进程服务器,并发服务器于多个客户端 newclientprocess = process(target = self.handlerequest) newclientprocess.start() #因为创建的新进程中,会对这个套接字+1,所以需要在主进程中减去依次,即调用一次close self.clientsocket.close() def setapp(self, application): '设置此wsgi服务器调用的应用程序入口函数' self.application = application def handlerequest(self): '用一个新的进程,为一个客户端进行服务' self.recvdata = self.clientsocket.recv(2014) requestheaderlines = self.recvdata.splitlines() for line in requestheaderlines: print(line) httprequestmethodline = requestheaderlines[0] getfilename = re.match("[^/]+(/[^ ]*)", httprequestmethodline).group(1) print("file name is ===>%s"%getfilename) #for test if getfilename[-3:] != ".py": if getfilename == '/': getfilename = documentroot + "/index.html" else: getfilename = documentroot + getfilename print("file name is ===2>%s"%getfilename) #for test try: f = open(getfilename) except ioerror: responseheaderlines = "http/1.1 404 not found\r\n" responseheaderlines += "\r\n" responsebody = "====sorry ,file not found====" else: responseheaderlines = "http/1.1 200 ok\r\n" responseheaderlines += "\r\n" responsebody = f.read() f.close() finally: response = responseheaderlines + responsebody self.clientsocket.send(response) self.clientsocket.close() else: #处理接收到的请求头 self.parserequest() #根据接收到的请求头构造环境变量字典 env = self.getenviron() #调用应用的相应方法,完成动态数据的获取 bodycontent = self.application(env, self.startresponse) #组织数据发送给客户端 self.finishresponse(bodycontent) def parserequest(self): '提取出客户端发送的request' requestline = self.recvdata.splitlines()[0] requestline = requestline.rstrip('\r\n') self.requestmethod, self.path, self.requestversion = requestline.split(" ") def getenviron(self): env = {} env['wsgi.version'] = (1, 0) env['wsgi.input'] = self.recvdata env['request_method'] = self.requestmethod # get env['path_info'] = self.path # /index.html return env def startresponse(self, status, response_headers, exc_info=none): serverheaders = [ ('date', 'tue, 31 mar 2016 10:11:12 gmt'), ('server', 'wsgiserver 0.2'), ] self.headers_set = [status, response_headers + serverheaders] def finishresponse(self, bodycontent): try: status, response_headers = self.headers_set #response的第一行 response = 'http/1.1 {status}\r\n'.format(status=status) #response的其他头信息 for header in response_headers: response += '{0}: {1}\r\n'.format(*header) #添加一个换行,用来和body进行分开 response += '\r\n' #添加发送的数据 for data in bodycontent: response += data self.clientsocket.send(response) finally: self.clientsocket.close() #设定服务器的端口 serveraddr = (host, port) = '', 8888 #设置服务器静态资源的路径 documentroot = './html' #设置服务器动态资源的路径 pythonroot = './wsgipy' def makeserver(serveraddr, application): server = wsgiserver(serveraddr) server.setapp(application) return server def main(): if len(sys.argv) < 2: sys.exit('请按照要求,指定模块名称:应用名称,例如 module:callable') #获取module:callable apppath = sys.argv[1] #根据冒号切割为module和callable module, application = apppath.split(':') print("module=%s"%module) #添加路径套sys.path sys.path.insert(0, pythonroot) #动态导入module变量中指定的模块 module = __import__(module) #获取module变量中制定的模块的application变量指定的属性 application = getattr(module, application) httpd = makeserver(serveraddr, application) print('wsgiserver: serving http on port {port} ...\n'.format(port=port)) httpd.serveforever() if __name__ == '__main__': main()
2、应用程序代码ctime.py
#coding:utf-8 import time def app(environ, start_response): status = "200 ok" response_headers = [ ("content-type", "text/plain") ] start_response(status, response_headers) return [str(environ)+'--->%s\n'%time.ctime()]
3、命令行执行代码
4、浏览器运行结果
上一篇: 实用:AI排版教程
下一篇: 【API】反转输入字符(Java)