[Python之路] 实现简易HTTP服务器与MINI WEB框架(利用WSGI实现服务器与框架解耦)
本文描述如果简单实现自定义Web服务器与自定义简易框架,并且不断进行版本迭代,从而清晰的展现服务器与Web框架之间是如何结合、如何配合工作的。以及WSGI是什么。
本文帖的代码有点多,但基本每次迭代修改的地方很少(为了每一节相对完整,所以重复代码比较多),注意看代码中黄色背景的部分,即是修改的部分。
一、选取一个自定义的服务器版本
参照 https://www.cnblogs.com/leokale-zz/p/11957768.html 中的各种服务器实现版本,我们选择比较简单的多进程版本作为演示版本。
代码如下:
import socket
import re
import multiprocessing def handle_request(new_socket):
# 接收请求
recv_msg = ""
recv_msg = new_socket.recv(1024).decode("utf-8")
if recv_msg == "":
print("recv null")
new_socket.close()
return # 从请求中解析出URI
recv_lines = recv_msg.splitlines()
print(recv_lines.__len__())
# 使用正则表达式提取出URI
ret = re.match(r"[^/]+(/[^ ]*)", recv_lines[0])
if ret:
# 获取URI字符串
file_name = ret.group(1)
# 如果URI是/,则默认返回index.html的内容
if file_name == "/":
file_name = "/index.html" try:
# 根据请求的URI,读取相应的文件
fp = open("." + file_name, "rb")
except:
# 找不到文件,响应404
response_msg = "HTTP/1.1 404 NOT FOUND\r\n"
response_msg += "\r\n"
response_msg += "<h1>----file not found----</h1>"
new_socket.send(response_msg.encode("utf-8"))
else:
html_content = fp.read()
fp.close()
# 响应正确 200 OK
response_msg = "HTTP/1.1 200 OK\r\n"
response_msg += "\r\n" # 返回响应头
new_socket.send(response_msg.encode("utf-8"))
# 返回响应体
new_socket.send(html_content) # 关闭该次socket连接
new_socket.close() def main():
# 创建TCP SOCKET实例
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# # 设置重用地址
# tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 绑定地址(默认本机IP)和端口
tcp_server_socket.bind(("", 7890))
# 监听
tcp_server_socket.listen(128)
# 循环接收客户端连接
while True:
new_socket, client_addr = tcp_server_socket.accept()
# 启动一个子进程来处理客户端的请求
sub_p = multiprocessing.Process(target=handle_request, args=(new_socket,))
sub_p.start()
# 这里要关闭父进程中的new_socket,因为创建子进程会复制一份new_socket给子进程
new_socket.close() # 关闭整个SOCKET
tcp_server_socket.close() if __name__ == "__main__":
main()
二、将代码用面向对象思想改写
import socket
import re
import multiprocessing class WSGIServer(object):
def __init__(self):
# 创建socket实例
self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置资源重用
self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 绑定IP端口
self.tcp_server_socket.bind(("", 7890))
# 开始监听
self.tcp_server_socket.listen(128) # 请求处理函数
def handle_request(self, new_socket):
# 接收请求
recv_msg = ""
recv_msg = new_socket.recv(1024).decode("utf-8")
if recv_msg == "":
print("recv null")
new_socket.close()
return # 从请求中解析出URI
recv_lines = recv_msg.splitlines()
# 使用正则表达式提取出URI
ret = re.match(r"[^/]+(/[^ ]*)", recv_lines[0])
if ret:
# 获取URI字符串
file_name = ret.group(1)
# 如果URI是/,则默认返回index.html的内容
if file_name == "/":
file_name = "/index.html" try:
# 根据请求的URI,读取相应的文件
fp = open("." + file_name, "rb")
except:
# 找不到文件,响应404
response_msg = "HTTP/1.1 404 NOT FOUND\r\n"
response_msg += "\r\n"
response_msg += "<h1>----file not found----</h1>"
new_socket.send(response_msg.encode("utf-8"))
else:
html_content = fp.read()
fp.close()
# 响应正确 200 OK
response_msg = "HTTP/1.1 200 OK\r\n"
response_msg += "\r\n" # 返回响应头
new_socket.send(response_msg.encode("utf-8"))
# 返回响应体
new_socket.send(html_content) # 关闭该次socket连接
new_socket.close() # 开始无限循环,接受请求
def run_forever(self):
while True:
new_socket, client_addr = self.tcp_server_socket.accept()
# 启动一个子进程来处理客户端的请求
sub_p = multiprocessing.Process(target=self.handle_request, args=(new_socket,))
sub_p.start()
# 这里要关闭父进程中的new_socket,因为创建子进程会复制一份new_socket给子进程
new_socket.close() # 关闭整个SOCKET
tcp_server_socket.close() def main():
wsgi_server = WSGIServer()
wsgi_server.run_forever() if __name__ == "__main__":
main()
在构造函数内创建监听socket实例,然后使用run_forever开始运行。
三、静态资源和动态资源
静态资源:例如html页面、css文件、js文件、图片等都属于静态资源。
动态资源:每次请求返回的数据都不一样,例如从数据库中获取的数据,或者变化的时间等,都叫动态资源。
如下图所示:
解释:
1.当服务器收到请求,判断请求内容为xxx.html、xxxx.css、xxxx.js、xxxx.png等,则直接从磁盘获取静态文件,读取并返回。
2.当服务器收到请求,判断请求内容为xxx.py(或其他自定义的特殊形式),则会调用web框架中的函数来获取数据。
3.HTTP服务器除了返回静态数据,以及从web框架获取reponse header和reponse body,将两者组装起来返回给客户端,他不做其他事情。
四、给HTTP服务器加上处理动态请求的逻辑
import socket
import re
import multiprocessing class WSGIServer(object):
def __init__(self):
# 创建socket实例
self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置资源重用
self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 绑定IP端口
self.tcp_server_socket.bind(("", 7890))
# 开始监听
self.tcp_server_socket.listen(128) # 请求处理函数
def handle_request(self, new_socket):
# 接收请求
recv_msg = ""
recv_msg = new_socket.recv(1024).decode("utf-8")
if recv_msg == "":
print("recv null")
new_socket.close()
return # 从请求中解析出URI
recv_lines = recv_msg.splitlines()
# 使用正则表达式提取出URI
ret = re.match(r"[^/]+(/[^ ]*)", recv_lines[0])
if ret:
# 获取URI字符串
file_name = ret.group(1)
# 如果URI是/,则默认返回index.html的内容
if file_name == "/":
file_name = "/index.html" if not file_name.endswith(".py"):
try:
# 根据请求的URI,读取相应的文件
fp = open("." + file_name, "rb")
except:
# 找不到文件,响应404
response_msg = "HTTP/1.1 404 NOT FOUND\r\n"
response_msg += "\r\n"
response_msg += "<h1>----file not found----</h1>"
new_socket.send(response_msg.encode("utf-8"))
else:
html_content = fp.read()
fp.close()
# 响应正确 200 OK
response_msg = "HTTP/1.1 200 OK\r\n"
response_msg += "\r\n" # 返回响应头
new_socket.send(response_msg.encode("utf-8"))
# 返回响应体
new_socket.send(html_content)
else:
header = "HTTP/1.1 200 OK\r\n"
header += "\r\n"
body = "dynamic request" + " %s" % time.ctime()
response = header + body
new_socket.send(response.encode("utf-8")) # 关闭该次socket连接
new_socket.close() # 开始无限循环,接受请求
def run_forever(self):
while True:
new_socket, client_addr = self.tcp_server_socket.accept()
# 启动一个子进程来处理客户端的请求
sub_p = multiprocessing.Process(target=self.handle_request, args=(new_socket,))
sub_p.start()
# 这里要关闭父进程中的new_socket,因为创建子进程会复制一份new_socket给子进程
new_socket.close() # 关闭整个SOCKET
tcp_server_socket.close() def main():
wsgi_server = WSGIServer()
wsgi_server.run_forever() if __name__ == "__main__":
main()
添加判断分支,如果请求内容以.py结尾,则判断为动态数据,返回dynamic request + 时间。如下图:
五、服务器与动态请求解耦
另外实现一个py模块,叫mini_frame.py
import time def login():
return "----login page----\r\n %s" % time.ctime() def register():
return "----register page----\r\n %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..."
服务器一旦判断收到的请求为动态数据请求,则调用application(file_name),并将请求交给mini_frame来处理。
服务器端代码:
import socket
import re
import multiprocessing
import time import mini_frame class WSGIServer(object):
def __init__(self):
# 创建socket实例
self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置资源重用
self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 绑定IP端口
self.tcp_server_socket.bind(("", 7890))
# 开始监听
self.tcp_server_socket.listen(128) # 请求处理函数
def handle_request(self, new_socket):
# 接收请求
recv_msg = ""
recv_msg = new_socket.recv(1024).decode("utf-8")
if recv_msg == "":
print("recv null")
new_socket.close()
return # 从请求中解析出URI
recv_lines = recv_msg.splitlines()
# 使用正则表达式提取出URI
ret = re.match(r"[^/]+(/[^ ]*)", recv_lines[0])
if ret:
# 获取URI字符串
file_name = ret.group(1)
# 如果URI是/,则默认返回index.html的内容
if file_name == "/":
file_name = "/index.html" if not file_name.endswith(".py"):
try:
# 根据请求的URI,读取相应的文件
fp = open("." + file_name, "rb")
except:
# 找不到文件,响应404
response_msg = "HTTP/1.1 404 NOT FOUND\r\n"
response_msg += "\r\n"
response_msg += "<h1>----file not found----</h1>"
new_socket.send(response_msg.encode("utf-8"))
else:
html_content = fp.read()
fp.close()
# 响应正确 200 OK
response_msg = "HTTP/1.1 200 OK\r\n"
response_msg += "\r\n" # 返回响应头
new_socket.send(response_msg.encode("utf-8"))
# 返回响应体
new_socket.send(html_content)
else:
header = "HTTP/1.1 200 OK\r\n"
header += "\r\n"
body = mini_frame.application(file_name)
response = header + body
new_socket.send(response.encode("utf-8")) # 关闭该次socket连接
new_socket.close() # 开始无限循环,接受请求
def run_forever(self):
while True:
new_socket, client_addr = self.tcp_server_socket.accept()
# 启动一个子进程来处理客户端的请求
sub_p = multiprocessing.Process(target=self.handle_request, args=(new_socket,))
sub_p.start()
# 这里要关闭父进程中的new_socket,因为创建子进程会复制一份new_socket给子进程
new_socket.close() # 关闭整个SOCKET
tcp_server_socket.close() def main():
wsgi_server = WSGIServer()
wsgi_server.run_forever() if __name__ == "__main__":
main()
服务器只需调用mini_frame.application(file_name),获取body数据即可。
六、WSGI介绍
如图,WSGI主要有以下流程:
1.服务器默认调用WEB 框架提供的application函数,参数为env和func_name,env是一个字典,用于存放请求中的一些数据,例如"/login.py"等,func_name是函数引用,这个函数的服务器WSGIServer类的一个成员函数,用于提供给application调用,从而将response header先返回给服务器,并保存在self.header中。
2.Web框架再调用func_name指向的函数之后,再准备response body数据,并通过return返回给服务器。
3.服务器将self.header和body数据组合起来,返回给客户端,完成一次完整的请求流程。
七、WSGI标准接口
def application(environ, start_response):
start_response('200 OK',[('Content-Type','text/html')])
return 'Hello World...'
上述代码是一个最简单符合WSGI标准的HTTP处理函数,接收两个参数,environ是包含所有HTTP请求信息的dict对象,start_response提供给web框架调用的函数。
将我们的nimi_frame框架代码修改一下:
def application(env, start_response):
start_response('200 OK', [('Content-Tpye', 'text/html')])
return "Hello World.."
将服务器代码修改一下:
import socket
import re
import multiprocessing
import time import mini_frame class WSGIServer(object):
def __init__(self):
self.headers = list()
self.status = ""
# 创建socket实例
self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置资源重用
self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 绑定IP端口
self.tcp_server_socket.bind(("", 7890))
# 开始监听
self.tcp_server_socket.listen(128) # 请求处理函数
def handle_request(self, new_socket):
# 接收请求
recv_msg = ""
recv_msg = new_socket.recv(1024).decode("utf-8")
if recv_msg == "":
print("recv null")
new_socket.close()
return # 从请求中解析出URI
recv_lines = recv_msg.splitlines()
# 使用正则表达式提取出URI
ret = re.match(r"[^/]+(/[^ ]*)", recv_lines[0])
if ret:
# 获取URI字符串
file_name = ret.group(1)
# 如果URI是/,则默认返回index.html的内容
if file_name == "/":
file_name = "/index.html" if not file_name.endswith(".py"):
try:
# 根据请求的URI,读取相应的文件
fp = open("." + file_name, "rb")
except:
# 找不到文件,响应404
response_msg = "HTTP/1.1 404 NOT FOUND\r\n"
response_msg += "\r\n"
response_msg += "<h1>----file not found----</h1>"
new_socket.send(response_msg.encode("utf-8"))
else:
html_content = fp.read()
fp.close()
# 响应正确 200 OK
response_msg = "HTTP/1.1 200 OK\r\n"
response_msg += "\r\n" # 返回响应头
new_socket.send(response_msg.encode("utf-8"))
# 返回响应体
new_socket.send(html_content)
else:
env = dict()
body = mini_frame.application(env, self.set_response_header) # 将框架返回的status组合进response header中
header = "HTTP/1.1 %s\r\n" % self.status
# 将框架返回的响应头键值对加入到header中
for temp in self.headers:
header += "%s:%s\r\n" % (temp[0], temp[1])
# 最后加上一个分割行
header += "\r\n" # 将响应体数据加在header后面
response = header + body
# 返回给客户端
new_socket.send(response.encode("utf-8")) # 关闭该次socket连接
new_socket.close() # 该成员函数提供给web框架的applicaiton函数调用,并将status,headers设置到服务器中
def set_response_header(self, status, headers):
self.status = status
self.headers = headers # 开始无限循环,接受请求
def run_forever(self):
while True:
new_socket, client_addr = self.tcp_server_socket.accept()
# 启动一个子进程来处理客户端的请求
sub_p = multiprocessing.Process(target=self.handle_request, args=(new_socket,))
sub_p.start()
# 这里要关闭父进程中的new_socket,因为创建子进程会复制一份new_socket给子进程
new_socket.close() # 关闭整个SOCKET
tcp_server_socket.close() def main():
wsgi_server = WSGIServer()
wsgi_server.run_forever() if __name__ == "__main__":
main()
解释:
1.代码的开头导入了mini_frame模块,这样导入显然是不太合理的,因为我们使用开源的服务器,不可能直接导入我们的框架代码。我们在后续版本中会解决这个问题。
2.动态请求的逻辑代码中,服务器调用mini_frame.application,这里传递了一个空的env字典(后续版本会将请求信息通过env传递给mini_frame)。
3.服务器定义了一个成员函数,set_response_header(),并将其引用传递给了application函
4.在mini_frame.application中,框架通过set_response_header()函数向服务器返回了response header(分为status和headers列表)。
5.服务器通过拼接self.status和self.headers,得到真正的response header。
6.application返回了一个固定的字符串(Hello World..这个字符串模拟response body),并将其串接在response header后,形成最终的响应数据。
八、通过environ字典传递请求头信息给web框架
mini_frame代码修改为:
import time def login():
return "----login page----\r\n %s" % time.ctime() def register():
return "----register page----\r\n %s" % time.ctime() def application(env, start_response):
file_name = env['PATH_INFO']
if file_name == '/login.py':
start_response('200 OK', [('Content-Tpye', 'text/html')])
return login()
elif file_name == '/register.py':
start_response('200 OK', [('Content-Tpye', 'text/html')])
return register()
else:
start_response('404 NOT FOUND', [])
return "Not found page..."
通过env来获取服务器传递过来的请求头信息,然后返回响应的动态内容。如果没有对应数据,则返回404。
服务器代码修改为:
import socket
import re
import multiprocessing
import time import mini_frame class WSGIServer(object):
def __init__(self):
self.headers = list()
self.status = ""
# 创建socket实例
self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置资源重用
self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 绑定IP端口
self.tcp_server_socket.bind(("", 7890))
# 开始监听
self.tcp_server_socket.listen(128) # 请求处理函数
def handle_request(self, new_socket):
# 接收请求
recv_msg = ""
recv_msg = new_socket.recv(1024).decode("utf-8")
if recv_msg == "":
print("recv null")
new_socket.close()
return # 从请求中解析出URI
recv_lines = recv_msg.splitlines()
# 使用正则表达式提取出URI
ret = re.match(r"[^/]+(/[^ ]*)", recv_lines[0])
if ret:
# 获取URI字符串
file_name = ret.group(1)
# 如果URI是/,则默认返回index.html的内容
if file_name == "/":
file_name = "/index.html" if not file_name.endswith(".py"):
try:
# 根据请求的URI,读取相应的文件
fp = open("." + file_name, "rb")
except:
# 找不到文件,响应404
response_msg = "HTTP/1.1 404 NOT FOUND\r\n"
response_msg += "\r\n"
response_msg += "<h1>----file not found----</h1>"
new_socket.send(response_msg.encode("utf-8"))
else:
html_content = fp.read()
fp.close()
# 响应正确 200 OK
response_msg = "HTTP/1.1 200 OK\r\n"
response_msg += "\r\n" # 返回响应头
new_socket.send(response_msg.encode("utf-8"))
# 返回响应体
new_socket.send(html_content)
else:
env = dict()
# 将请求的内容添加到字典中,并传递给application
env['PATH_INFO'] = file_name
body = mini_frame.application(env, self.set_response_header) # 将框架返回的status组合进response header中
header = "HTTP/1.1 %s\r\n" % self.status
# 将框架返回的响应头键值对加入到header中
for temp in self.headers:
header += "%s:%s\r\n" % (temp[0], temp[1])
# 最后加上一个分割行
header += "\r\n" # 将响应体数据加在header后面
response = header + body
# 返回给客户端
new_socket.send(response.encode("utf-8")) # 关闭该次socket连接
new_socket.close() # 该成员函数提供给web框架的applicaiton函数调用,并将status,headers设置到服务器中
def set_response_header(self, status, headers):
self.status = status
# 如果要在响应头中添加服务器信息,一定是在服务器代码中加,而不是web框架代码中加
self.headers = [("server", "mini_server v1.0")]
self.headers += headers # 开始无限循环,接受请求
def run_forever(self):
while True:
new_socket, client_addr = self.tcp_server_socket.accept()
# 启动一个子进程来处理客户端的请求
sub_p = multiprocessing.Process(target=self.handle_request, args=(new_socket,))
sub_p.start()
# 这里要关闭父进程中的new_socket,因为创建子进程会复制一份new_socket给子进程
new_socket.close() # 关闭整个SOCKET
tcp_server_socket.close() def main():
wsgi_server = WSGIServer()
wsgi_server.run_forever() if __name__ == "__main__":
main()
解释:
1.将请求内容,例如/login.py、/register.py放到env字典中,传递给application。
2.接收到来自Web框架的response header后,如果想要加入服务器信息,则服务器代码中自己添加,这样才符合解耦的思想。
九、如何安排项目文件
目前我们的代码有以下几部分:
1.mini_web_server部分
2.mini_frame部分
3.静态资源部分,例如css、js、图片等
4.html页面,称之为模板(template)
我们将这些文件重新分类放置:
解释:
1.将mini_frame放到dynamic中,因为web框架用于处理动态数据请求。
2.将css、js、图片等静态资源分类放到static中。
3.将html页面放到templates文件夹中。
4.web服务器程序在根目录下。
注意:文件放置的目录变化了,代码中响应的代码也需要改变,例如读取index.html,则需要修改为templates/index.html。
十、让web框架返回页面数据
当我们请求http://192.168.1.8:7890/index.py时,注意这里不是index.html。
修改mini_frame代码:
import time def index():
with open("templates/index.html", 'rb') as f:
content = f.read()
return content.decode("utf-8") def login():
return "----login page----\r\n %s" % time.ctime() def register():
return "----register page----\r\n %s" % time.ctime() def application(env, start_response):
file_name = env['PATH_INFO']
if file_name == '/login.py':
start_response('200 OK', [('Content-Tpye', 'text/html')])
return login()
elif file_name == '/register.py':
start_response('200 OK', [('Content-Tpye', 'text/html')])
return register()
elif file_name == '/index.py':
start_response('200 OK', [('Content-Tpye', 'text/html')])
return index()
else:
start_response('404 NOT FOUND', [])
return "Not found page..."
服务器代码无需进行修改。这里相当于使用动态请求的方式index.py,来请求了index.html。
十一、总结
不管我们使用index.py的方式还是index.html的方式访问服务器,服务器都能够正确的返回index.html的内容给客户端。
当index.html中需求请求其他资源时(例如图片,css样式等),则浏览器会自动发起新的请求,此时服务器同样按照既定的方式才处理就可以了。
十二、最后完善一些东西
1.将一些信息使用配置文件来提供给服务器
例如静态资源路径、动态资源路径、绑定端口、web框架路径等:
创建一个配置文件web_server.conf:
{
"static_path":"./static"
"dynamic_path":"./dynamic"
"bind_port":7890
"app_path":"mini_frame:application"
}
在服务器代码中,可以通过添加以下代码来读取配置文件信息(在WSGIServer的__init__中添加即可):
with open("./web_server.conf") as f:
self.conf_info = eval(f.read()) self.static_path = self.conf_info['static_path']
self.dynamic_path = self.conf_info['dynamic_path']
self.bind_port = self.conf_info['bind_port']
self.app_module = self.conf_info['app_module']
self.app_name = self.conf_info["app_name"] # 导入mini_frame.application
import sys
sys.path.append(self.dynamic_path)
frame = __import__(self.app_module)
self.app = getattr(frame, self.app_name)
解释:
1.读取配置文件,并将其转换为字典。
2.获取字典中的每个配置项。
3.将dynamic_path加入python环境变量,然后使用__import__导入mini_frame。
4.使用getattr获取mini_frame模块中的application函数的引用。
5.其他的配置项,诸如static_path、bind_port,则将其替换到响应的位置即可。
2.创建一个快速启动的shell脚本
创建一个run.sh
python3 web_server.py
修改运行权限:
chmod +x run.sh
[Python之路] 实现简易HTTP服务器与MINI WEB框架(利用WSGI实现服务器与框架解耦)的更多相关文章
- Python之路【第二十篇】其他WEB框架
WEB框架功能分析 WEB框架本质上,就是一个SOCKET Server WEB框架前面有WSGI或者是自己写的SOCKET,然后交给URL路由系统处理,然后交给某个函数或某个类,然后在模板里拿到模板 ...
- [Python WEB开发] 使用WSGI开发类Flask框架 (二)
WSGI Web服务器网关接口 WSGI主要规定了Web服务器如何与Web应用程序进行通信,以及如何将Web应用程序链接在一起来处理一个请求. wsgiref Python中的WSGI参考模块 ...
- Python之路【第二十三篇】:Django 初探--Django的开发服务器及创建数据库(笔记)
Django 初探--Django的开发服务器及创建数据库(笔记) 1.Django的开发服务器 Django框架中包含一些轻量级的web应用服务器,开发web项目时不需再对其配置服务器,Django ...
- Python之路【第十八章】:Web框架
Web框架本质 1.众所周知,对于所有的Web应用,本质上其实就是一个socket服务端,用户的浏览器其实就是一个socket客户端 #!/usr/bin/env python # -*- codin ...
- Python之路【第十五篇】WEB框架
WEB框架本质 Python的WEB框架分为两类: 1.自己写socket,自己处理请求 2.基于wsgi(Web Server Gateway Interface WEB服务网关接口),自己处理请求 ...
- [Python之路] 使用装饰器给Web框架添加路由功能(静态、动态、伪静态URL)
一.观察以下代码 以下来自 Python实现简易HTTP服务器与MINI WEB框架(利用WSGI实现服务器与框架解耦) 中的mini_frame最后版本的代码: import time def in ...
- [Python之路] 实现简单Web服务器(TCP3次握手4次挥手解释)
一.如何使用Python实现一个返回固定页面的Web Server 1.使用socket创建一个TCP Server 2.接受来自浏览器的TCP链接,并接收HTTP请求 3.返回固定响应数据给浏览器 ...
- Python之路【第十九篇】:爬虫
Python之路[第十九篇]:爬虫 网络爬虫(又被称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本.另外一些不常使用 ...
- Python之路【第十八篇】:Web框架们
Python之路[第十八篇]:Web框架们 Python的WEB框架 Bottle Bottle是一个快速.简洁.轻量级的基于WSIG的微型Web框架,此框架只由一个 .py 文件,除了Pytho ...
随机推荐
- web/服务器知识
一 PV 推到出 QPS 你想建设一个能承受500万PV/每天的网站吗? 500万PV是什么概念?服务器每秒要处理多少个请求才能应对?如果计算呢?? PV是什么:PV是page view的简写.PV是 ...
- Spring Boot 面试总结(一)
1.使用 Spring Boot 前景? 多年来,随着新功能的增加,spring变得越来越复杂.只需访问https://spring.io/projects页面,我们就会看到可以在我们的应用程序中使用 ...
- HDU-6704 K-th occurrence (后缀自动机father树上倍增建权值线段树合并)
layout: post title: HDU-6704 K-th occurrence (后缀自动机father树上倍增建权值线段树合并) author: "luowentaoaa&quo ...
- 今天测试大商创,遇到了 upstream sent too big header while reading response header from upstream
今天在测试大商创后台系统时,打开店铺结算,查看店铺对应的订单列表时,该列表自动跳转到502,查看线上和测试环境都能正常打开,唯独我的电脑上打开是502, 查询nginx的error.log日志,记录了 ...
- Codeforces 1240A. Save the Nature
传送门 显然可以二分答案 如果知道卖的票数,那么就能算出有多少 $a$ 倍数但不是 $b$ 倍数的位置,多少 $b$ 倍数但不是 $a$ 倍数的位置,多少既是 $a$ 又是 $b$ 倍数的位置 然后贪 ...
- 牛客 132C 简单瞎搞题 (bitset)
大意: 给定序列$a$的第$i$个元素的取值范围$[L_i,R_i]$, 求$a$的平方和的种类数. 用bitset优化, 复杂度$O(\frac{n^5}{\omega})$ #include &l ...
- webSocket协议和Socket.IO
一.Http无法轻松实现实时应用: ● HTTP协议是无状态的,服务器只会响应来自客户端的请求,但是它与客户端之间不具备持续连接. ● 我们可以非常轻松的捕获浏览器上发生的事件(比如用户点击了盒子), ...
- C#面向对象22 委托事件反射
1.委托的定义:声明委托类型(返回值和参数,命名空间中):定义委托对象 (把委托想象成函数中的占位符~因为你并不确定调用哪个函数~) using System; using System.Collec ...
- 解决windows系统下ping,ipconfig不是内部或外部命令
一般情况下,都是误删了系统变量path的值.解决方法:右击我的电脑 → 选择属性 → 选择高级系统设置 → 环境变量 → 在系统变量列表中,找到“path”环境变量双击,打开.在变量值这一栏检测下是否 ...
- 【原创】大叔经验分享(71)docker容器中使用jvm工具
java应用中经常需要用到jvm工具来进行一些操作,如果java应用部署在docker容器中,如何使用jvm工具? 首先要看使用的docker镜像, 比如常用的openjdk镜像分为jdk和jre,只 ...