多进程面向对象服务器
这是一个静态服务器
-
通过正则得到请求的url -
然后找到这个url对应的文件,把文件内容读取后返回给浏览器
import socket
import re
import multiprocessing
class WSGIServer(object):
def __init__(self):
# 1. 创建套接字
self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 2. 绑定
self.tcp_server_socket.bind(("", 7890))
# 3. 变为监听套接字
self.tcp_server_socket.listen(128)
def service_client(self, new_socket):
"""为这个客户端返回数据"""
# 1. 接收浏览器发送过来的请求 ,即http请求
# GET / HTTP/1.1
# .....
request = new_socket.recv(1024).decode("utf-8")
# print(">>>"*50)
# print(request)
request_lines = request.splitlines()
print("")
print(">"*20)
print(request_lines)
# GET /index.html HTTP/1.1
# get post put del
file_name = ""
ret = re.match(r"[^/]+(/[^ ]*)", request_lines[0])
if ret:
file_name = ret.group(1)
# print("*"*50, file_name)
if file_name == "/":
file_name = "/index.html"
# 2. 返回http格式的数据,给浏览器
try:
f = open("./html" + file_name, "rb")
except:
response = "HTTP/1.1 404 NOT FOUNDrn"
response += "rn"
response += "------file not found-----"
new_socket.send(response.encode("utf-8"))
else:
html_content = f.read()
f.close()
# 2.1 准备发送给浏览器的数据---header
response = "HTTP/1.1 200 OKrn"
response += "rn"
# 2.2 准备发送给浏览器的数据---boy
# response += "hahahhah"
# 将response header发送给浏览器
new_socket.send(response.encode("utf-8"))
# 将response body发送给浏览器
new_socket.send(html_content)
# 关闭套接
new_socket.close()
def run_forever(self):
"""用来完成整体的控制"""
while True:
# 4. 等待新客户端的链接
new_socket, client_addr = self.tcp_server_socket.accept()
# 5. 为这个客户端服务
p = multiprocessing.Process(target=self.service_client, args=(new_socket,))
p.start()
new_socket.close()
# 关闭监听套接字
self.tcp_server_socket.close()
def main():
"""控制整体,创建一个web 服务器对象,然后调用这个对象的run_forever方法运行"""
wsgi_server = WSGIServer()
wsgi_server.run_forever()
if __name__ == "__main__":
main()
多进程面向对象的动态服务器
-
这里是根据正则得到的url中的请求 -
如果是 .py
结尾的,就动态返回当前时间 -
否则,就返回指定页面的内容 -
这里的程序有一个缺点:这是一个服务器,它集成了解析动态请求的功能,应该根据动态和静态去请求一个web框架,让web框架来返回动态信息
import socket
import re
import multiprocessing
import time
class WSGIServer(object):
def __init__(self):
# 1. 创建套接字
self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 2. 绑定
self.tcp_server_socket.bind(("", 7890))
# 3. 变为监听套接字
self.tcp_server_socket.listen(128)
def service_client(self, new_socket):
"""为这个客户端返回数据"""
# 1. 接收浏览器发送过来的请求 ,即http请求
# GET / HTTP/1.1
# .....
request = new_socket.recv(1024).decode("utf-8")
# print(">>>"*50)
# print(request)
request_lines = request.splitlines()
print("")
print(">"*20)
print(request_lines)
# GET /index.html HTTP/1.1
# get post put del
file_name = ""
ret = re.match(r"[^/]+(/[^ ]*)", request_lines[0])
if ret:
file_name = ret.group(1)
# print("*"*50, file_name)
if file_name == "/":
file_name = "/index.html"
# 2. 返回http格式的数据,给浏览器
# 2.1 如果请求的资源不是以.py结尾,那么就认为是静态资源(html/css/js/png,jpg等)
if not file_name.endswith(".py"):
try:
f = open("./html" + file_name, "rb")
except:
response = "HTTP/1.1 404 NOT FOUNDrn"
response += "rn"
response += "------file not found-----"
new_socket.send(response.encode("utf-8"))
else:
html_content = f.read()
f.close()
# 2.1 准备发送给浏览器的数据---header
response = "HTTP/1.1 200 OKrn"
response += "rn"
# 2.2 准备发送给浏览器的数据---boy
# response += "hahahhah"
# 将response header发送给浏览器
new_socket.send(response.encode("utf-8"))
# 将response body发送给浏览器
new_socket.send(html_content)
else:
# 2.2 如果是以.py结尾,那么就认为是动态资源的请求
header = "HTTP/1.1 200 OKrn"
header += "rn" # 这里必须增加一个换行,是响应体决定的
body = "hahahah %s " % time.ctime()
response = header+body
# 发送response给浏览器
new_socket.send(response.encode("utf-8"))
# 关闭套接
new_socket.close()
def run_forever(self):
"""用来完成整体的控制"""
while True:
# 4. 等待新客户端的链接
new_socket, client_addr = self.tcp_server_socket.accept()
# 5. 为这个客户端服务
p = multiprocessing.Process(target=self.service_client, args=(new_socket,))
p.start()
new_socket.close()
# 关闭监听套接字
self.tcp_server_socket.close()
def main():
"""控制整体,创建一个web 服务器对象,然后调用这个对象的run_forever方法运行"""
wsgi_server = WSGIServer()
wsgi_server.run_forever()
if __name__ == "__main__":
main()
实现简单的web框架
将web服务器和逻辑处理的代码分开
-
mini_freame.py
是中是一个方法,其返回值作为web服务器的响应体 -
web服务器中,根据 .py
的名称,动态去请求mini_freame.py
中的方法 -
该方式的缺点
在web服务器中,需要根据 if-elif来判断需要请求 mini_freame.py
中的哪个方法解耦不够明显
定义一个框架mini_freame.py
-
给框架就是一个返回不同内容的框架 -
共服务器来调用使用
import time
def login():
return "welcome hahahh to our website.......time:%s" % time.ctime()
def register():
return "register success"
web服务器
-
该web服务器,根据带 .py
结尾的请求不同,去请求mini_freame.py
中的不同方法,把其返回值当做body返回给浏览器 -
注意:这里需要在web服务器中判断,需要请求 mini_freame.py
中哪个方法
import socket
import re
import multiprocessing
import time
import mini_frame
class WSGIServer(object):
def __init__(self):
# 1. 创建套接字
self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 2. 绑定
self.tcp_server_socket.bind(("", 7890))
# 3. 变为监听套接字
self.tcp_server_socket.listen(128)
def service_client(self, new_socket):
"""为这个客户端返回数据"""
# 1. 接收浏览器发送过来的请求 ,即http请求
# GET / HTTP/1.1
# .....
request = new_socket.recv(1024).decode("utf-8")
# print(">>>"*50)
# print(request)
request_lines = request.splitlines()
print("")
print(">"*20)
print(request_lines)
# GET /index.html HTTP/1.1
# get post put del
file_name = ""
ret = re.match(r"[^/]+(/[^ ]*)", request_lines[0])
if ret:
file_name = ret.group(1)
# print("*"*50, file_name)
if file_name == "/":
file_name = "/index.html"
# 2. 返回http格式的数据,给浏览器
# 2.1 如果请求的资源不是以.py结尾,那么就认为是静态资源(html/css/js/png,jpg等)
if not file_name.endswith(".py"):
try:
f = open("./html" + file_name, "rb")
except:
response = "HTTP/1.1 404 NOT FOUNDrn"
response += "rn"
response += "------file not found-----"
new_socket.send(response.encode("utf-8"))
else:
html_content = f.read()
f.close()
# 2.1 准备发送给浏览器的数据---header
response = "HTTP/1.1 200 OKrn"
response += "rn"
# 2.2 准备发送给浏览器的数据---boy
# response += "hahahhah"
# 将response header发送给浏览器
new_socket.send(response.encode("utf-8"))
# 将response body发送给浏览器
new_socket.send(html_content)
else:
# 2.2 如果是以.py结尾,那么就认为是动态资源的请求
header = "HTTP/1.1 200 OKrn"
header += "rn"
# body = "hahahah %s " % time.ctime()
if file_name == "/login.py":
body = mini_frame.login()
elif file_name == "/register.py":
body = mini_frame.register()
response = header+body
# 发送response给浏览器
new_socket.send(response.encode("utf-8"))
# 关闭套接
new_socket.close()
def run_forever(self):
"""用来完成整体的控制"""
while True:
# 4. 等待新客户端的链接
new_socket, client_addr = self.tcp_server_socket.accept()
# 5. 为这个客户端服务
p = multiprocessing.Process(target=self.service_client, args=(new_socket,))
p.start()
new_socket.close()
# 关闭监听套接字
self.tcp_server_socket.close()
def main():
"""控制整体,创建一个web 服务器对象,然后调用这个对象的run_forever方法运行"""
wsgi_server = WSGIServer()
wsgi_server.run_forever()
if __name__ == "__main__":
main()
将web服务器和逻辑处理的代码分开-升级版
-
解决上一个版本的解耦不够彻底的问题
mini_freame.py
-
在该版本的框架中,有一个application方法,其参数就是需要请求的 .py
的请求
import time
def login():
return "i----login--welcome hahahh to our website.......time:%s" % time.ctime()
def register():
return "-----register---welcome hahahh to our website.......time:%s" % time.ctime()
def profile():
return "-----profile----welcome hahahh to our website.......time:%s" % time.ctime()
def application(file_name):
if file_name == "/login.py":
return login()
elif file_name == "/register.py":
return register()
else:
return "not found you page...."
web服务器
-
在当前服务器版本中,只需要调用 mini_freame.py
中的application
方法,传递当前的.py
结尾的文件名即可 -
在 mini_freame.py
中的application
方法根据其参数来处理请求即可
import socket
import re
import multiprocessing
import time
import mini_frame
class WSGIServer(object):
def __init__(self):
# 1. 创建套接字
self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 2. 绑定
self.tcp_server_socket.bind(("", 7890))
# 3. 变为监听套接字
self.tcp_server_socket.listen(128)
def service_client(self, new_socket):
"""为这个客户端返回数据"""
# 1. 接收浏览器发送过来的请求 ,即http请求
# GET / HTTP/1.1
# .....
request = new_socket.recv(1024).decode("utf-8")
# print(">>>"*50)
# print(request)
request_lines = request.splitlines()
print("")
print(">"*20)
print(request_lines)
# GET /index.html HTTP/1.1
# get post put del
file_name = ""
ret = re.match(r"[^/]+(/[^ ]*)", request_lines[0])
if ret:
file_name = ret.group(1)
# print("*"*50, file_name)
if file_name == "/":
file_name = "/index.html"
# 2. 返回http格式的数据,给浏览器
# 2.1 如果请求的资源不是以.py结尾,那么就认为是静态资源(html/css/js/png,jpg等)
if not file_name.endswith(".py"):
try:
f = open("./html" + file_name, "rb")
except:
response = "HTTP/1.1 404 NOT FOUNDrn"
response += "rn"
response += "------file not found-----"
new_socket.send(response.encode("utf-8"))
else:
html_content = f.read()
f.close()
# 2.1 准备发送给浏览器的数据---header
response = "HTTP/1.1 200 OKrn"
response += "rn"
# 2.2 准备发送给浏览器的数据---boy
# response += "hahahhah"
# 将response header发送给浏览器
new_socket.send(response.encode("utf-8"))
# 将response body发送给浏览器
new_socket.send(html_content)
else:
# 2.2 如果是以.py结尾,那么就认为是动态资源的请求
header = "HTTP/1.1 200 OKrn"
header += "rn"
# body = "hahahah %s " % time.ctime()
# if file_name == "/login.py":
# body = mini_frame.login()
# elif file_name == "/register.py":
# body = mini_frame.register()
body = mini_frame.application(file_name)
response = header+body
# 发送response给浏览器
new_socket.send(response.encode("utf-8"))
# 关闭套接
new_socket.close()
def run_forever(self):
"""用来完成整体的控制"""
while True:
# 4. 等待新客户端的链接
new_socket, client_addr = self.tcp_server_socket.accept()
# 5. 为这个客户端服务
p = multiprocessing.Process(target=self.service_client, args=(new_socket,))
p.start()
new_socket.close()
# 关闭监听套接字
self.tcp_server_socket.close()
def main():
"""控制整体,创建一个web 服务器对象,然后调用这个对象的run_forever方法运行"""
wsgi_server = WSGIServer()
wsgi_server.run_forever()
if __name__ == "__main__":
main()
WSGI
我们写的框架,如何与现有的开源服务器结合
-
当我们写好了框架后,可以自己实现服务器来与该框架结合,但是自己写的服务器不一定能支持大量并发 -
如果使用nginx等开源服务器的话,那nginx也不知道应该调用我们框架中的哪些方法呢,并且nginx服务器也不知道如何引用我们的框架呢 -
这个就需要python的 Web Server Gateway Interface
(或简称WSGI
,读作“wizgy”
)
WSGI
-
WSGI可以让框架与服务器协同工作 -
WSGI允许开发者将选择web框架和web服务器分开。 -
可以混合匹配web服务器和web框架,选择一个适合的配对。 -
比如,可以在Gunicorn 或者 Nginx/uWSGI 或者 Waitress上运行 Django, Flask, 或 Pyramid。 -
真正的混合匹配,得益于WSGI同时支持服务器和架构
web服务器必须具备WSGI接口
-
所有的现代Python Web框架都已具备WSGI接口,它让你不对代码作修改就能使服务器和特点的web框架协同工作。
定义WSGI接口
-
WSGI接口定义非常简单,它只要求Web开发者实现一个函数,就可以响应HTTP请求 -
函数如下
def application(environ, set_response_header):
start_response('200 OK', [('Content-Type', 'text/html')])
return 'Hello World!'
-
上面的 application()
函数就是符合WSGI标准的一个HTTP处理函数,它接收两个参数:
environ
:一个包含所有HTTP请求信息的dict对象;set_response_header
:一个发送HTTP响应的函数(这个方法不一定就是这个,这里是为了说明用法)。set_response_header
方法是需要两个参数,第一个参数是状态码,第二个参数就是响应头,是一个列表,里面存的是元组,可以有多个元组
-
整个 application()
函数本身没有涉及到任何解析HTTP的部分,也就是说,把底层web服务器解析部分和应用程序逻辑部分进行了分离,这样开发者就可以专心做一个领域了 -
application()
函数必须由WSGI服务器来调用。 -
application()
函数的返回值就是body
浏览器请求动态页面过程
-
当浏览器请求到实现了WSGI的服务器时,服务器就会去调用框架的 application()
函数,并且传递两个参数,environ
和set_response_header
environ
是HTTP请求对象的dict字典set_response_header
是让框架调用服务器中,设置响应头的回调函数
-
框架会去调用服务器传递过来的第二个参数回调函数,把响应头给服务器,返回到框架中 -
在框架中,去查询数据库得到body,此时把body返回给服务器 -
服务器,把得到的响应头和body,一起返回给浏览器
服务器支持WSGI
第一版,传递给框架的空字典版本
-
服务器给框架传递的参数时,传递一个空的字典 -
该版本的框架,只能返回固定的内容
框架支持WSGI
-
框架中按照WSGI的要求来实现application函数 -
mini_freame.py
def application(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html;charset=utf-8')])
return 'Hello World! 我爱你中国....'
服务器支持WSGI
-
服务器端定义一个被框架回调的函数 set_response_header()
,用于让框架来传递响应头 -
这里传递给框架的 application()
中的environ的字典,是一个空字典,也可以传递空字典 -
web_server.py
import socket
import re
import multiprocessing
import time
import mini_frame
class WSGIServer(object):
def __init__(self):
# 1. 创建套接字
self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 2. 绑定
self.tcp_server_socket.bind(("", 7890))
# 3. 变为监听套接字
self.tcp_server_socket.listen(128)
def service_client(self, new_socket):
"""为这个客户端返回数据"""
# 1. 接收浏览器发送过来的请求 ,即http请求
# GET / HTTP/1.1
# .....
request = new_socket.recv(1024).decode("utf-8")
# print(">>>"*50)
# print(request)
request_lines = request.splitlines()
print("")
print(">"*20)
print(request_lines)
# GET /index.html HTTP/1.1
# get post put del
file_name = ""
ret = re.match(r"[^/]+(/[^ ]*)", request_lines[0])
if ret:
file_name = ret.group(1)
# print("*"*50, file_name)
if file_name == "/":
file_name = "/index.html"
# 2. 返回http格式的数据,给浏览器
# 2.1 如果请求的资源不是以.py结尾,那么就认为是静态资源(html/css/js/png,jpg等)
if not file_name.endswith(".py"):
try:
f = open("./html" + file_name, "rb")
except:
response = "HTTP/1.1 404 NOT FOUNDrn"
response += "rn"
response += "------file not found-----"
new_socket.send(response.encode("utf-8"))
else:
html_content = f.read()
f.close()
# 2.1 准备发送给浏览器的数据---header
response = "HTTP/1.1 200 OKrn"
response += "rn"
# 2.2 准备发送给浏览器的数据---boy
# response += "hahahhah"
# 将response header发送给浏览器
new_socket.send(response.encode("utf-8"))
# 将response body发送给浏览器
new_socket.send(html_content)
else:
# 2.2 如果是以.py结尾,那么就认为是动态资源的请求
env = dict()
body = mini_frame.application(env, self.set_response_header)
header = "HTTP/1.1 %srn" % self.status
for temp in self.headers:
header += "%s:%srn" % (temp[0], temp[1])
header += "rn"
response = header+body
# 发送response给浏览器
new_socket.send(response.encode("utf-8"))
# 关闭套接
new_socket.close()
## 用于让框架 回调的
def set_response_header(self, status, headers):
self.status = status
self.headers = [("server", "mini_web v8.8")]
self.headers += headers
def run_forever(self):
"""用来完成整体的控制"""
while True:
# 4. 等待新客户端的链接
new_socket, client_addr = self.tcp_server_socket.accept()
# 5. 为这个客户端服务
p = multiprocessing.Process(target=self.service_client, args=(new_socket,))
p.start()
new_socket.close()
# 关闭监听套接字
self.tcp_server_socket.close()
def main():
"""控制整体,创建一个web 服务器对象,然后调用这个对象的run_forever方法运行"""
wsgi_server = WSGIServer()
wsgi_server.run_forever()
if __name__ == "__main__":
main()
第二版,传递给框架的字典中存放其他信息
可以在字典中传递请求的路径等信息
框架实现WSGI
-
从框架中得到的env中提取PATH_INFO,这里面存放的是请求的路径 -
根据请求的路径,动态的返回内容
def index():
return "这是主页"
def login():
return "这是登录页面"
def application(env, start_response):
start_response('200 OK', [('Content-Type', 'text/html;charset=utf-8')])
file_name = env['PATH_INFO']
# file_name = "/index.py"
if file_name == "/index.py":
return index()
elif file_name == "/login.py":
return login()
else:
return 'Hello World! 我爱你中国....'
服务器支持WSGI
-
服务器调用框架时传递的env环境变量时,把PATH_INFO传递进去,它代表的是要请求的路径
import socket
import re
import multiprocessing
import time
import mini_frame
class WSGIServer(object):
def __init__(self):
# 1. 创建套接字
self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 2. 绑定
self.tcp_server_socket.bind(("", 7890))
# 3. 变为监听套接字
self.tcp_server_socket.listen(128)
def service_client(self, new_socket):
"""为这个客户端返回数据"""
# 1. 接收浏览器发送过来的请求 ,即http请求
# GET / HTTP/1.1
# .....
request = new_socket.recv(1024).decode("utf-8")
# print(">>>"*50)
# print(request)
request_lines = request.splitlines()
print("")
print(">"*20)
print(request_lines)
# GET /index.html HTTP/1.1
# get post put del
file_name = ""
ret = re.match(r"[^/]+(/[^ ]*)", request_lines[0])
if ret:
file_name = ret.group(1)
# print("*"*50, file_name)
if file_name == "/":
file_name = "/index.html"
# 2. 返回http格式的数据,给浏览器
# 2.1 如果请求的资源不是以.py结尾,那么就认为是静态资源(html/css/js/png,jpg等)
if not file_name.endswith(".py"):
try:
f = open("./html" + file_name, "rb")
except:
response = "HTTP/1.1 404 NOT FOUNDrn"
response += "rn"
response += "------file not found-----"
new_socket.send(response.encode("utf-8"))
else:
html_content = f.read()
f.close()
# 2.1 准备发送给浏览器的数据---header
response = "HTTP/1.1 200 OKrn"
response += "rn"
# 2.2 准备发送给浏览器的数据---boy
# response += "hahahhah"
# 将response header发送给浏览器
new_socket.send(response.encode("utf-8"))
# 将response body发送给浏览器
new_socket.send(html_content)
else:
# 2.2 如果是以.py结尾,那么就认为是动态资源的请求
env = dict()
body = mini_frame.application(env, self.set_response_header)
header = "HTTP/1.1 %srn" % self.status
for temp in self.headers:
header += "%s:%srn" % (temp[0], temp[1])
header += "rn"
response = header+body
# 发送response给浏览器
new_socket.send(response.encode("utf-8"))
# 关闭套接
new_socket.close()
## 用于让框架 回调的
def set_response_header(self, status, headers):
self.status = status
self.headers = [("server", "mini_web v8.8")]
self.headers += headers
def run_forever(self):
"""用来完成整体的控制"""
while True:
# 4. 等待新客户端的链接
new_socket, client_addr = self.tcp_server_socket.accept()
# 5. 为这个客户端服务
p = multiprocessing.Process(target=self.service_client, args=(new_socket,))
p.start()
new_socket.close()
# 关闭监听套接字
self.tcp_server_socket.close()
def main():
"""控制整体,创建一个web 服务器对象,然后调用这个对象的run_forever方法运行"""
wsgi_server = WSGIServer()
wsgi_server.run_forever()
if __name__ == "__main__":
main()
web框架支持模板功能
-
当启动服务器后,如果访问的是 .py
文件的话,就去动态的发送这个请求到web框架 -
web框架根据请求路径,动态找到该请求对应的模板文件 -
读取模板文件,然后作为响应体返回给服务器 -
服务器返回给浏览器 -
该版本的缺点: 这里的模板,是根据请求体返回的数据,是直接读取模板内容然后返回的
web框架mini_freame.py
-
根据请求,读取对应的文件,返回给服务器 -
注意:这里的模块文件路径的写法,是以服务器程序运行为起始路径,寻找的,所以下面是一个 .
,而不是两个..
def index():
with open("./templates/index.html",encoding="utf-8") as f:
content = f.read()
return content
def center():
with open("./templates/center.html") as f:
return f.read()
def application(env, start_response):
start_response('200 OK', [('Content-Type', 'text/html;charset=utf-8')])
file_name = env['PATH_INFO']
# file_name = "/index.py"
if file_name == "/index.py":
return index()
elif file_name == "/center.py":
return center()
else:
return 'Hello World! 我爱你中国....'
服务器
-
服务器,如果是 .py
结尾的文件,就去web框架请求数据 -
如果是静态文件,直接读取静态文件返回
import socket
import re
import multiprocessing
import time
import dynamic.mini_frame
class WSGIServer(object):
def __init__(self):
# 1. 创建套接字
self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 2. 绑定
self.tcp_server_socket.bind(("", 7890))
# 3. 变为监听套接字
self.tcp_server_socket.listen(128)
def service_client(self, new_socket):
"""为这个客户端返回数据"""
# 1. 接收浏览器发送过来的请求 ,即http请求
# GET / HTTP/1.1
# .....
request = new_socket.recv(1024).decode("utf-8")
# print(">>>"*50)
# print(request)
request_lines = request.splitlines()
print("")
print(">"*20)
print(request_lines)
# GET /index.html HTTP/1.1
# get post put del
file_name = ""
ret = re.match(r"[^/]+(/[^ ]*)", request_lines[0])
if ret:
file_name = ret.group(1)
# print("*"*50, file_name)
if file_name == "/":
file_name = "/index.html"
# 2. 返回http格式的数据,给浏览器
# 2.1 如果请求的资源不是以.py结尾,那么就认为是静态资源(html/css/js/png,jpg等)
if not file_name.endswith(".py"):
try:
f = open("./static" + file_name, "rb")
except:
response = "HTTP/1.1 404 NOT FOUNDrn"
response += "rn"
response += "------file not found-----"
new_socket.send(response.encode("utf-8"))
else:
html_content = f.read()
f.close()
# 2.1 准备发送给浏览器的数据---header
response = "HTTP/1.1 200 OKrn"
response += "rn"
# 2.2 准备发送给浏览器的数据---boy
# response += "hahahhah"
# 将response header发送给浏览器
new_socket.send(response.encode("utf-8"))
# 将response ic.mini_frame.applicationbody发送给浏览器
new_socket.send(html_content)
else:
# 2.2 如果是以.py结尾,那么就认为是动态资源的请求
env = dict() # 这个字典中存放的是web服务器要传递给 web框架的数据信息
env['PATH_INFO'] = file_name
# {"PATH_INFO": "/index.py"}
body = dynamic.mini_frame.application(env, self.set_response_header)
header = "HTTP/1.1 %srn" % self.status
for temp in self.headers:
header += "%s:%srn" % (temp[0], temp[1])
header += "rn"
response = header+body
# 发送response给浏览器
new_socket.send(response.encode("utf-8"))
# 关闭套接
new_socket.close()
def set_response_header(self, status, headers):
self.status = status
self.headers = [("server", "mini_web v8.8")]
self.headers += headers
def run_forever(self):
"""用来完成整体的控制"""
while True:
# 4. 等待新客户端的链接
new_socket, client_addr = self.tcp_server_socket.accept()
# 5. 为这个客户端服务
p = multiprocessing.Process(target=self.service_client, args=(new_socket,))
p.start()
new_socket.close()
# 关闭监听套接字
self.tcp_server_socket.close()
def main():
"""控制整体,创建一个web 服务器对象,然后调用这个对象的run_forever方法运行"""
wsgi_server = WSGIServer()
wsgi_server.run_forever()
if __name__ == "__main__":
main()
web框架增加替换模板中数据功能
在模板文件中增加一些特殊标记的信息
-
上面的 {%content%}
就是特殊标记的信息,类似于占位符 -
当数据返回的时候,是需要被替换成其他内容的
web框架实现替换模板中数据的功能
-
读取模块中的内容 -
通过正则得到占位符的信息,然后替换成需要的数据
import re
def index():
with open("./templates/index.html",encoding="utf-8") as f:
content = f.read()
my_stock_info = "哈哈哈哈 这是你的本月名称....."
content = re.sub(r"{%content%}", my_stock_info, content)
return content
def center():
with open("./templates/center.html") as f:
content = f.read()
my_stock_info = "这里是从mysql查询出来的数据。。。"
content = re.sub(r"{%content%}", my_stock_info, content)
return content
def application(env, start_response):
start_response('200 OK', [('Content-Type', 'text/html;charset=utf-8')])
file_name = env['PATH_INFO']
# file_name = "/index.py"
if file_name == "/index.py":
return index()
elif file_name == "/center.py":
return center()
else:
return 'Hello World! 我爱你中国....'
服务器
import socket
import re
import multiprocessing
import time
import dynamic.mini_frame
class WSGIServer(object):
def __init__(self):
# 1. 创建套接字
self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 2. 绑定
self.tcp_server_socket.bind(("", 7890))
# 3. 变为监听套接字
self.tcp_server_socket.listen(128)
def service_client(self, new_socket):
"""为这个客户端返回数据"""
# 1. 接收浏览器发送过来的请求 ,即http请求
# GET / HTTP/1.1
# .....
request = new_socket.recv(1024).decode("utf-8")
# print(">>>"*50)
# print(request)
request_lines = request.splitlines()
print("")
print(">"*20)
print(request_lines)
# GET /index.html HTTP/1.1
# get post put del
file_name = ""
ret = re.match(r"[^/]+(/[^ ]*)", request_lines[0])
if ret:
file_name = ret.group(1)
# print("*"*50, file_name)
if file_name == "/":
file_name = "/index.html"
# 2. 返回http格式的数据,给浏览器
# 2.1 如果请求的资源不是以.py结尾,那么就认为是静态资源(html/css/js/png,jpg等)
if not file_name.endswith(".py"):
try:
f = open("./static" + file_name, "rb")
except:
response = "HTTP/1.1 404 NOT FOUNDrn"
response += "rn"
response += "------file not found-----"
new_socket.send(response.encode("utf-8"))
else:
html_content = f.read()
f.close()
# 2.1 准备发送给浏览器的数据---header
response = "HTTP/1.1 200 OKrn"
response += "rn"
# 2.2 准备发送给浏览器的数据---boy
# response += "hahahhah"
# 将response header发送给浏览器
new_socket.send(response.encode("utf-8"))
# 将response ic.mini_frame.applicationbody发送给浏览器
new_socket.send(html_content)
else:
# 2.2 如果是以.py结尾,那么就认为是动态资源的请求
env = dict() # 这个字典中存放的是web服务器要传递给 web框架的数据信息
env['PATH_INFO'] = file_name
# {"PATH_INFO": "/index.py"}
body = dynamic.mini_frame.application(env, self.set_response_header)
header = "HTTP/1.1 %srn" % self.status
for temp in self.headers:
header += "%s:%srn" % (temp[0], temp[1])
header += "rn"
response = header+body
# 发送response给浏览器
new_socket.send(response.encode("utf-8"))
# 关闭套接
new_socket.close()
def set_response_header(self, status, headers):
self.status = status
self.headers = [("server", "mini_web v8.8")]
self.headers += headers
def run_forever(self):
"""用来完成整体的控制"""
while True:
# 4. 等待新客户端的链接
new_socket, client_addr = self.tcp_server_socket.accept()
# 5. 为这个客户端服务
p = multiprocessing.Process(target=self.service_client, args=(new_socket,))
p.start()
new_socket.close()
# 关闭监听套接字
self.tcp_server_socket.close()
def main():
"""控制整体,创建一个web 服务器对象,然后调用这个对象的run_forever方法运行"""
wsgi_server = WSGIServer()
wsgi_server.run_forever()
if __name__ == "__main__":
main()
运行web程序时指定端口和web框架
-
该服务器运行的时候,用法如下 -
python3 webservice.py 7890 mini_frame:application
-
运行服务器 webservice.py
的时候,指定端口 -
并且指定了web框架 mini_frame
,并且运行的是mini_frame
框架中的application
方法 -
由于运行时指定了web框架,所以在服务器中不能直接导入这个框架了,而是运行时动态加载框架
服务器
-
该服务器的缺点:
服务器可以根据传递的web框架调用其运行 但是,该服务器中把web框架的位置写死了,最好通过配置文件来读取
-
webservice.py
import socket
import re
import multiprocessing
import time
# import dynamic.mini_frame
import sys
class WSGIServer(object):
def __init__(self, port, app):
# 1. 创建套接字
self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 2. 绑定
self.tcp_server_socket.bind(("", port))
# 3. 变为监听套接字
self.tcp_server_socket.listen(128)
self.application = app
def service_client(self, new_socket):
"""为这个客户端返回数据"""
# 1. 接收浏览器发送过来的请求 ,即http请求
# GET / HTTP/1.1
# .....
request = new_socket.recv(1024).decode("utf-8")
# print(">>>"*50)
# print(request)
request_lines = request.splitlines()
print("")
print(">"*20)
print(request_lines)
# GET /index.html HTTP/1.1
# get post put del
file_name = ""
ret = re.match(r"[^/]+(/[^ ]*)", request_lines[0])
if ret:
file_name = ret.group(1)
# print("*"*50, file_name)
if file_name == "/":
file_name = "/index.html"
# 2. 返回http格式的数据,给浏览器
# 2.1 如果请求的资源不是以.py结尾,那么就认为是静态资源(html/css/js/png,jpg等)
if not file_name.endswith(".py"):
try:
f = open("./static" + file_name, "rb")
except:
response = "HTTP/1.1 404 NOT FOUNDrn"
response += "rn"
response += "------file not found-----"
new_socket.send(response.encode("utf-8"))
else:
html_content = f.read()
f.close()
# 2.1 准备发送给浏览器的数据---header
response = "HTTP/1.1 200 OKrn"
response += "rn"
# 2.2 准备发送给浏览器的数据---boy
# response += "hahahhah"
# 将response header发送给浏览器
new_socket.send(response.encode("utf-8"))
# 将response ic.mini_frame.applicationbody发送给浏览器
new_socket.send(html_content)
else:
# 2.2 如果是以.py结尾,那么就认为是动态资源的请求
env = dict() # 这个字典中存放的是web服务器要传递给 web框架的数据信息
env['PATH_INFO'] = file_name
# {"PATH_INFO": "/index.py"}
# body = dynamic.mini_frame.application(env, self.set_response_header)
body = self.application(env, self.set_response_header)
header = "HTTP/1.1 %srn" % self.status
for temp in self.headers:
header += "%s:%srn" % (temp[0], temp[1])
header += "rn"
response = header+body
# 发送response给浏览器
new_socket.send(response.encode("utf-8"))
# 关闭套接
new_socket.close()
def set_response_header(self, status, headers):
self.status = status
self.headers = [("server", "mini_web v8.8")]
self.headers += headers
def run_forever(self):
"""用来完成整体的控制"""
while True:
# 4. 等待新客户端的链接
new_socket, client_addr = self.tcp_server_socket.accept()
# 5. 为这个客户端服务
p = multiprocessing.Process(target=self.service_client, args=(new_socket,))
p.start()
new_socket.close()
# 关闭监听套接字
self.tcp_server_socket.close()
def main():
"""控制整体,创建一个web 服务器对象,然后调用这个对象的run_forever方法运行"""
if len(sys.argv) == 3:
try:
port = int(sys.argv[1]) # 7890
frame_app_name = sys.argv[2] # mini_frame:application
except Exception as ret:
print("端口输入错误。。。。。")
return
else:
print("请按照以下方式运行:")
print("python3 xxxx.py 7890 mini_frame:application")
return
# mini_frame:application
ret = re.match(r"([^:]+):(.*)", frame_app_name)
if ret:
frame_name = ret.group(1) # mini_frame
app_name = ret.group(2) # application
else:
print("请按照以下方式运行:")
print("python3 xxxx.py 7890 mini_frame:application")
return
sys.path.append("./dynamic")
# import frame_name --->找frame_name.py
frame = __import__(frame_name) # 返回值标记这 导入的这个模块
app = getattr(frame, app_name) # 此时app就指向了 dynamic/mini_frame模块中的application这个函数
# print(app)
wsgi_server = WSGIServer(port, app)
wsgi_server.run_forever()
if __name__ == "__main__":
main()
让web服务器支持配置文件
配置文件web_server.conf
-
配置文件中配置web框架中的位置,和静态文件的文件
{
"static_path":"./static",
"dynamic_path":"./dynamic"
}
web框架mini_freame.py
import re
def index():
with open("./templates/index.html") as f:
content = f.read()
my_stock_info = "哈哈哈哈 这是你的本月名称....."
content = re.sub(r"{%content%}", my_stock_info, content)
return content
def center():
with open("./templates/center.html") as f:
content = f.read()
my_stock_info = "这里是从mysql查询出来的数据。。。"
content = re.sub(r"{%content%}", my_stock_info, content)
return content
def application(env, start_response):
start_response('200 OK', [('Content-Type', 'text/html;charset=utf-8')])
file_name = env['PATH_INFO']
# file_name = "/index.py"
if file_name == "/index.py":
return index()
elif file_name == "/center.py":
return center()
else:
return 'Hello World! 我爱你中国....'
服务器web_server.py
-
这样服务器中,就没有任何写死的web框架的信息了 -
完全是去配置文件中读取数配置信息,然后直接读取内容找到web框架
import socket
import re
import multiprocessing
import time
# import dynamic.mini_frame
import sys
class WSGIServer(object):
def __init__(self, port, app, static_path):
# 1. 创建套接字
self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 2. 绑定
self.tcp_server_socket.bind(("", port))
# 3. 变为监听套接字
self.tcp_server_socket.listen(128)
self.application = app
self.static_path = static_path
def service_client(self, new_socket):
"""为这个客户端返回数据"""
# 1. 接收浏览器发送过来的请求 ,即http请求
# GET / HTTP/1.1
# .....
request = new_socket.recv(1024).decode("utf-8")
# print(">>>"*50)
# print(request)
request_lines = request.splitlines()
print("")
print(">"*20)
print(request_lines)
# GET /index.html HTTP/1.1
# get post put del
file_name = ""
ret = re.match(r"[^/]+(/[^ ]*)", request_lines[0])
if ret:
file_name = ret.group(1)
# print("*"*50, file_name)
if file_name == "/":
file_name = "/index.html"
# 2. 返回http格式的数据,给浏览器
# 2.1 如果请求的资源不是以.py结尾,那么就认为是静态资源(html/css/js/png,jpg等)
if not file_name.endswith(".py"):
try:
f = open(self.static_path + file_name, "rb")
except:
response = "HTTP/1.1 404 NOT FOUNDrn"
response += "rn"
response += "------file not found-----"
new_socket.send(response.encode("utf-8"))
else:
html_content = f.read()
f.close()
# 2.1 准备发送给浏览器的数据---header
response = "HTTP/1.1 200 OKrn"
response += "rn"
# 2.2 准备发送给浏览器的数据---boy
# response += "hahahhah"
# 将response header发送给浏览器
new_socket.send(response.encode("utf-8"))
# 将response ic.mini_frame.applicationbody发送给浏览器
new_socket.send(html_content)
else:
# 2.2 如果是以.py结尾,那么就认为是动态资源的请求
env = dict() # 这个字典中存放的是web服务器要传递给 web框架的数据信息
env['PATH_INFO'] = file_name
# {"PATH_INFO": "/index.py"}
# body = dynamic.mini_frame.application(env, self.set_response_header)
body = self.application(env, self.set_response_header)
header = "HTTP/1.1 %srn" % self.status
for temp in self.headers:
header += "%s:%srn" % (temp[0], temp[1])
header += "rn"
response = header+body
# 发送response给浏览器
new_socket.send(response.encode("utf-8"))
# 关闭套接
new_socket.close()
def set_response_header(self, status, headers):
self.status = status
self.headers = [("server", "mini_web v8.8")]
self.headers += headers
def run_forever(self):
"""用来完成整体的控制"""
while True:
# 4. 等待新客户端的链接
new_socket, client_addr = self.tcp_server_socket.accept()
# 5. 为这个客户端服务
p = multiprocessing.Process(target=self.service_client, args=(new_socket,))
p.start()
new_socket.close()
# 关闭监听套接字
self.tcp_server_socket.close()
def main():
"""控制整体,创建一个web 服务器对象,然后调用这个对象的run_forever方法运行"""
if len(sys.argv) == 3:
try:
port = int(sys.argv[1]) # 7890
frame_app_name = sys.argv[2] # mini_frame:application
except Exception as ret:
print("端口输入错误。。。。。")
return
else:
print("请按照以下方式运行:")
print("python3 xxxx.py 7890 mini_frame:application")
return
# mini_frame:application
ret = re.match(r"([^:]+):(.*)", frame_app_name)
if ret:
frame_name = ret.group(1) # mini_frame
app_name = ret.group(2) # application
else:
print("请按照以下方式运行:")
print("python3 xxxx.py 7890 mini_frame:application")
return
with open("./web_server.conf") as f:
conf_info = eval(f.read())
# 此时 conf_info是一个字典里面的数据为:
# {
# "static_path":"./static",
# "dynamic_path":"./dynamic"
# }
sys.path.append(conf_info['dynamic_path'])
# import frame_name --->找frame_name.py
frame = __import__(frame_name) # 返回值标记这 导入的这个模板
app = getattr(frame, app_name) # 此时app就指向了 dynamic/mini_frame模块中的application这个函数
# print(app)
wsgi_server = WSGIServer(port, app, conf_info['static_path'])
wsgi_server.run_forever()
if __name__ == "__main__":
main()
web框架通过装饰器实现路由
mini_frame.py
-
该框架中,使用装饰器来修饰函数 -
python文件的执行是从上向下的,遇到装饰器的装饰的函数时,虽然没有调用被装饰的函数,但是其上的装饰器都会执行 -
所以,定义了一个 URL_FUNC_DICT = dict()
字典 -
遇到了装饰器,就把所有被 @route
装饰的数据方法存放到URL_FUNC_DIC
,这里面存放了所有的路由 -
最后根据请求url,去匹配路由里面的方法,调用返回给服务器
import re
"""
URL_FUNC_DICT = {
"/index.py": index,
"/center.py": center
}
"""
URL_FUNC_DICT = dict()
def route(url):
def set_func(func):
# URL_FUNC_DICT["/index.py"] = index
URL_FUNC_DICT[url] = func
#def call_func(*args, **kwargs):
# return func(*args, **kwargs)
#return call_func
return set_func
@route("/index.py") # 相当于 @set_func # index = set_func(index)
def index():
with open("./templates/index.html",encoding="utf-8") as f:
content = f.read()
my_stock_info = "哈哈哈哈 这是你的本月名称....."
content = re.sub(r"{%content%}", my_stock_info, content)
return content
@route("/center.py")
def center():
with open("./templates/center.html",encoding="utf-8") as f:
content = f.read()
my_stock_info = "这里是从mysql查询出来的数据。。。"
content = re.sub(r"{%content%}", my_stock_info, content)
return content
def application(env, start_response):
start_response('200 OK', [('Content-Type', 'text/html;charset=utf-8')])
file_name = env['PATH_INFO']
# file_name = "/index.py"
"""
if file_name == "/index.py":
return index()
elif file_name == "/center.py":
return center()
else:
return 'Hello World! 我爱你中国....'
"""
try:
# func = URL_FUNC_DICT[file_name]
# return func()
return URL_FUNC_DICT[file_name]()
except Exception as ret:
return "产生了异常:%s" % str(ret)
服务器web_server.py
import socket
import re
import multiprocessing
import time
# import dynamic.mini_frame
import sys
class WSGIServer(object):
def __init__(self, port, app, static_path):
# 1. 创建套接字
self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 2. 绑定
self.tcp_server_socket.bind(("", port))
# 3. 变为监听套接字
self.tcp_server_socket.listen(128)
self.application = app
self.static_path = static_path
def service_client(self, new_socket):
"""为这个客户端返回数据"""
# 1. 接收浏览器发送过来的请求 ,即http请求
# GET / HTTP/1.1
# .....
request = new_socket.recv(1024).decode("utf-8")
# print(">>>"*50)
# print(request)
request_lines = request.splitlines()
print("")
print(">"*20)
print(request_lines)
# GET /index.html HTTP/1.1
# get post put del
file_name = ""
ret = re.match(r"[^/]+(/[^ ]*)", request_lines[0])
if ret:
file_name = ret.group(1)
# print("*"*50, file_name)
if file_name == "/":
file_name = "/index.html"
# 2. 返回http格式的数据,给浏览器
# 2.1 如果请求的资源不是以.py结尾,那么就认为是静态资源(html/css/js/png,jpg等)
if not file_name.endswith(".py"):
try:
f = open(self.static_path + file_name, "rb", encoding="utf-8")
except:
response = "HTTP/1.1 404 NOT FOUNDrn"
response += "rn"
response += "------file not found-----"
new_socket.send(response.encode("utf-8"))
else:
html_content = f.read()
f.close()
# 2.1 准备发送给浏览器的数据---header
response = "HTTP/1.1 200 OKrn"
response += "rn"
# 2.2 准备发送给浏览器的数据---boy
# response += "hahahhah"
# 将response header发送给浏览器
new_socket.send(response.encode("utf-8"))
# 将response ic.mini_frame.applicationbody发送给浏览器
new_socket.send(html_content)
else:
# 2.2 如果是以.py结尾,那么就认为是动态资源的请求
env = dict() # 这个字典中存放的是web服务器要传递给 web框架的数据信息
env['PATH_INFO'] = file_name
# {"PATH_INFO": "/index.py"}
# body = dynamic.mini_frame.application(env, self.set_response_header)
body = self.application(env, self.set_response_header)
header = "HTTP/1.1 %srn" % self.status
for temp in self.headers:
header += "%s:%srn" % (temp[0], temp[1])
header += "rn"
response = header+body
# 发送response给浏览器
new_socket.send(response.encode("utf-8"))
# 关闭套接
new_socket.close()
def set_response_header(self, status, headers):
self.status = status
self.headers = [("server", "mini_web v8.8")]
self.headers += headers
def run_forever(self):
"""用来完成整体的控制"""
while True:
# 4. 等待新客户端的链接
new_socket, client_addr = self.tcp_server_socket.accept()
# 5. 为这个客户端服务
p = multiprocessing.Process(target=self.service_client, args=(new_socket,))
p.start()
new_socket.close()
# 关闭监听套接字
self.tcp_server_socket.close()
def main():
"""控制整体,创建一个web 服务器对象,然后调用这个对象的run_forever方法运行"""
if len(sys.argv) == 3:
try:
port = int(sys.argv[1]) # 7890
frame_app_name = sys.argv[2] # mini_frame:application
except Exception as ret:
print("端口输入错误。。。。。")
return
else:
print("请按照以下方式运行:")
print("python3 xxxx.py 7890 mini_frame:application")
return
# mini_frame:application
ret = re.match(r"([^:]+):(.*)", frame_app_name)
if ret:
frame_name = ret.group(1) # mini_frame
app_name = ret.group(2) # application
else:
print("请按照以下方式运行:")
print("python3 xxxx.py 7890 mini_frame:application")
return
with open("./web_server.conf",encoding="utf-8") as f:
conf_info = eval(f.read())
# 此时 conf_info是一个字典里面的数据为:
# {
# "static_path":"./static",
# "dynamic_path":"./dynamic"
# }
sys.path.append(conf_info['dynamic_path'])
# import frame_name --->找frame_name.py
frame = __import__(frame_name) # 返回值标记这 导入的这个模板
app = getattr(frame, app_name) # 此时app就指向了 dynamic/mini_frame模块中的application这个函数
# print(app)
wsgi_server = WSGIServer(port, app, conf_info['static_path'])
wsgi_server.run_forever()
if __name__ == "__main__":
main()
这里用到的配置文件**web_server.conf**
{
"static_path":"./static",
"dynamic_path":"./dynamic"
}
原文始发于微信公众号(Python之家):Python基础-30-动态服务器
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/198443.html