基础代码
服务器
from socket import *
import multiprocessing
import re
import wsgi_5
class WEB_Server(object):
def __init__(self):
"""初始化服务器"""
# 创建一个服务器套接字
self.server_soc = socket(AF_INET, SOCK_STREAM)
# 使得套接字关闭后,端口可以立即释放,立即连接
self.server_soc.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
# 绑定服务器套接字的ip地址和端口,转为监听状态
self.server_soc.bind(('', 1314))
self.server_soc.listen(128)
def handle_request(self, client_soc):
# 接收客户端传来的请求
recv_data = client_soc.recv(1024).decode("utf-8")
print("客户端文件接收成功:\r\n", recv_data)
if recv_data:
# 提取出请求的文件路径
first_line = recv_data.splitlines()[0]
content_path = re.match(r'[^/]+(/[^ ]*)', first_line).group(1)
# 响应请求
if content_path == '/':
content_path = "/index.html"
# 如果搜索的内容路径为.py文件,动态返回,否则静态返回
if content_path.endswith(".py"):
# 返回动态数据
# 空字典
params_server = dict()
# 添加一个地址
params_server['url'] = content_path
body = wsgi_5.application(params_server, self.set_head_params)
# 这个是返回一个头数据以后才能拼接
head = "HTTP/1.1 %s\r\n" % self.stauts
# 拼接头部
for temp in self.params:
head += "%s:%s\r\n" % (temp[0], temp[1])
# 拼接响应内容
content = head + "\r\n" + body
client_soc.send(content.encode('utf-8'))
else:
#返回静态数据
try:
# 获取到文件内容
with open('html' + content_path, 'rb') as file: # 本地文件夹直接找名字,不需要在之前加‘/’
content = file.read()
except:
# 文件不存在
responce_head = "HTTP/1.1 404 NOT FOUND\r\n"
responce_head += 'Content-Type: text/html;charset=utf-8\r\n'
responce_head += "\r\n"
responce_body = "没有找到内容"
client_soc.send(responce_head.encode("utf-8"))
client_soc.send(responce_body.encode("utf-8"))
else:
# 返回网页给浏览器
responce_head = "HTTP/1.1 200 OK\r\n"
responce_head += "\r\n"
responce_body = content
client_soc.send(responce_head.encode("utf-8"))
client_soc.send(responce_body)
# 关闭子进程p1的客服套接字(主进程中客服套接字的硬链接)
client_soc.close()
def run_server(self):
while True:
# 等待客户端连接
print("等待客户端连接")
client_soc, client_addr = self.server_soc.accept()
print("客户端连接成功 %s" % client_addr[0])
# 与客户端进行通信
# 创建子进程并启动
p = multiprocessing.Process(target=self.handle_request, args=(client_soc,))
p.start()
# 关闭主进程的客服套接字
client_soc.close() # 进程是互相独立的,需要也在主进程中关闭套接字
# # 关闭套接字
# server_soc.close()
def set_head_params(self,stauts,params):
self.stauts = stauts
self.params = params
def main():
"""服务器主逻辑"""
# 创建服务器对象
web_server = WEB_Server()
# 开启服务器
web_server.run_server()
if __name__ == '__main__':
main()
miniWEB框架
# 定义一个application函数,获取服务器传来的待处理数据及设置响应头函数引用,完成导入响应头信息,返回响应体信息的功能
# 定义函数
def application(environ, start_responce):
# 启动导入响应头信息函数
start_responce("200 ok", [('Content-Type', 'test/html')])
# 对待处理数据进行处理后,返回响应体信息
url = environ['file_name']
if url == "index.py":
return "this is index.py"
elif url == "center.py":
return "this is center.py"
else:
return "no file found"
封装网页信息代码
- 在实际开发中,是将前端的html代码作为响应体传给浏览器的
- 可以使用函数封装代码,主逻辑会清晰一些,但是,还是需要把网页信息整个放在函数里
- 但是不利用前端更改使用,那么就使用with open打开相应文件,会更易于阅读而且不耽误文件信息的更改。
- 但是当页面比较多时,会写出很多的if,elif语句,不好,一般多于三个if分支,就要考虑其他方式
用字典返回响应方法
- 为了解决多页面问题。引入字典的使用,可以所有有效的文件路径和相应函数引用放在一个字典里
- 需要返回某个页面时,只需要遍历字典,判断请求的文件路径匹配字典中的哪一个键,取出对应的值,调用响应函数即可
完后路由功能
- 需要把所有的路径和函数引用一一输入到字典中,还是比较麻烦,有没有更方便快捷的办法?
- 有! 给装饰器传参,使用插拔式的路由功能!
- 装饰器是指,在不改变原函数体的,正常调用原函数的的情况下,给函数增加新的功能
- 在装饰器的基础上,可以再给装饰器传递一个参数(文件的路径),这样就可以在程序执行到定义函数的时候,就自动给预先定义的字典生成了一个键值对(该键值对对应一个页面请求的路径和调用文件函数引用)
- 注意路由实现代码里,最好把函数引用存储为第二层函数的引用,这样,方便后期对函数添加功能
完整mini_WEB框架_路由
代码
import re
# 定义存请求文件路径和响应函数引用的对应键值对
body_dict = {}
# 定义路由,即在装饰器的基础上再加一层函数,作用是传递一个参数,返回@set_fun
def route(url):
def set_fun(func):
def call_fun(*args, **kwargs):
return func(*args, **kwargs)
body_dict[url] = call_fun
return call_fun
return set_fun
# 输出index.html页面
@route("/index.html") # 使用路由
def index():
with open("./templates/index.html") as f:
content = f.read()
row_str = """<tr>
<td>1</td>
<td>000007</td>
<td>全新好</td>
<td>10.01%</td>
<td>4.40%</td>
<td>16.05</td>
<td>14.60</td>
<td>2017-07-18</td>
<td>
<input type="button" value="添加" id="toAdd" name="toAdd" systemidvaule="000007">
</td>
</tr>"""
content_new = re.sub(r'\{%content%\}', row_str, content)
return content_new
# 输出center.html页面
@route("/center.html")
def center():
with open("./templates/center.html") as f:
content = f.read()
row_str = """
<tr>
<td>000036</td>
<td>华联控股</td>
<td>10.04%</td>
<td>10.80%</td>
<td>11.29</td>
<td>10.26</td>
<td>123</td>
<td>
<a type="button" class="btn btn-default btn-xs" href="/update/000036.html"> <span class="glyphicon glyphicon-star" aria-hidden="true"></span> 修改 </a>
</td>
<td>
<input type="button" value="删除" id="toDel" name="toDel" systemidvaule="000036">
</td>
</tr>
"""
content_new = re.sub(r'\{%content%\}', row_str, content)
return content_new
# 输出update.html页面
@route("/update.html")
def update():
with open("./templates/update.html") as f:
content = f.read()
return content
# WSGI协议
def application(environ, start_response):
# 传回响应头信息
start_response('200 OK', [('Content-Type', 'text/html;charset=utf-8')])
# 取出请求的文件路径
url = environ['url']
# 返回body值
try:
# 匹配到响应函数,执行,即可返回相应文件内容
return body_dict[url]()
except Exception as e:
# 没有匹配到,返回提示页面
return "no page was found"
总结
- 路由功能,即通过一个地址,找到一个页面
- 其实,就是AOP编程(面向切面编程)思想的一个运用,让我们只需要关心当前业务功能的实现,不需要关注整个程序的代码逻辑