总体内容
- 1、多进程-web服务器面向对象web服务器,返回的是静态界面
- 2、静态资源、动态资源、web服务器支持动态解析
- 3、实现很简单的框架,让web服务器支持
- 4、模仿WSGI协议来做一个web服务器的框架
- 5、通过传字典实现浏览器请求的资源不一样,响应的不一样
- 6、给程序传递参数、添加web服务器的配置文件、添加shell功能
一、多进程-web服务器面向对象web服务器,返回的是静态界面
import socket import multiprocessing import re class WSGIServer(object): def __init__(self):= """用来完成整体的控制""" # 1.创建套接字 self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 2.绑定 self.tcp_server_socket.bind(("192.168.3.6", 7790)) # 3.变为监听套接字 self.tcp_server_socket.listen(128) def server_client(self,new_socket): """为这个客户端返回数据""" # # 组织相应 头信息(header) # 1.接收浏览器发送过来的请求,即 http请求 # GET / HTTP/1.1 # .... request = new_socket.recv(1024).decode("utf-8") # print(request) request_lines = request.splitlines() print("") print(">"*20) print(request_lines) print("<" * 20) # 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("file_name=%s" % file_name) print("*"*50,file_name) if file_name == "/": file_name = "/index.html" # 2.返回http格式的数据,给浏览器 # 2.1、准备发送给浏览器的数据---header try: f = open("./html"+file_name,"rb") except: response = "HTTP/1.1 404 NOT FOUND\r\n" response += "\r\n" response += "----file not found" new_socket.send(response.encode("utf-8")) else: print("-----------OK------------") html_content = f.read() f.close() response = "HTTP/1.1 200 OK\r\n" # 200表示找到这个资源 response += "\r\n" # 用一个空的行与body进行隔开 # 2.2、准备发送给浏览器的数据 ---body # 将response的header发送给浏览器 new_socket.send(response.encode("utf-8")) # 将response的 body 发送给浏览器 new_socket.send(html_content) # 3.关闭套接字 new_socket.close() def run_server(self): while True: # 4.等待客户端的链接 new_socket, client_addr = self.tcp_server_socket.accept() # 5.开辟一个进程为这个客户端服务 p = multiprocessing.Process(target=self.server_client,args=(new_socket,)) p.start() new_socket.close() # 6.关闭监听的套接字 tcp_server_socket.close() def main(): wsgi_server = WSGIServer() wsgi_server.run_server() if __name__ == '__main__': main()
二、静态资源、动态资源、web服务器支持动态解析
返回的是动态界面,也就是在上面的server_client
代码中加一个判断,我们以请求的是 .py结尾
来判断是是动态页面请求
def server_client(self,new_socket): """为这个客户端返回数据""" # # 组织相应 头信息(header) # 1.接收浏览器发送过来的请求,即 http请求 # GET / HTTP/1.1 # .... request = new_socket.recv(1024).decode("utf-8") # print(request) request_lines = request.splitlines() print("") print(">"*20) print(request_lines) print("<" * 20) # 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("file_name=%s" % file_name) print("*"*50,file_name) if file_name == "/": file_name = "/index.html" # 2.返回http格式的数据,给浏览器 # 2.1、准备发送给浏览器的数据---header if not file_name.endswith(".py"): try: f = open("./html"+file_name,"rb") except: response = "HTTP/1.1 404 NOT FOUND\r\n" response += "\r\n" response += "----file not found" new_socket.send(response.encode("utf-8")) else: print("-----------OK------------") html_content = f.read() f.close() response = "HTTP/1.1 200 OK\r\n" # 200表示找到这个资源 response += "\r\n" # 用一个空的行与body进行隔开 # 2.2、准备发送给浏览器的数据 ---body # 将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 OK\r\n" # 200表示找到这个资源 header += "\r\n" # 用一个空的行与body进行隔开 body = "HHHHH" response = header + body # 准备发送给浏览器的数据 ---body # 将response发送给浏览器 new_socket.send(response.encode("utf-8")) # 3.关闭套接字 new_socket.close()
三、实现很简单的框架,让web服务器支持
提示:也就是写一个共用模块,把请求的数据传到这个模块,然后再模块内进行分析,返回相应的数据,我们把这个模块定义为 mini_frame
,模块内的代码如下
import time def login(): return "welcome to our login ......time:%s"% time.ctime() def register(): return "welcome to our register ......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..."
把之前 def server_client 里面判断是 .py
结尾的代码里面的body改为如下代码
body = mini_frame.application(file_name)
四、模仿WSGI协议来做一个web服务器的框架
- 4.1、先了解一下 WSGI 协议
- 怎么在你刚建立的Web服务器上运行一个Django应用和Flask应用,如何不做任何改变而适应不同的web架构呢?
- 在以前,选择 Python web 架构会受制于可用的web服务器,反之亦然。如果架构和服务器可以协同工作,那就好了:
但有可能面对(或者曾有过)下面的问题,当要把一个服务器和一个架构结合起来时,却发现他们不是被设计成协同工作的:
那么,怎么可以不修改服务器和架构代码而确保可以在多个架构下运行web服务器呢?答案就是 Python Web Server Gateway Interface (或简称 WSGI,读作“wizgy”)。
WSGI允许开发者将选择web框架和web服务器分开。可以混合匹配web服务器和web框架,选择一个适合的配对。比如,可以在Gunicorn 或者 Nginx/uWSGI 或者 Waitress上运行 Django, Flask, 或 Pyramid。真正的混合匹配,得益于WSGI同时支持服务器和架构:
- web服务器必须具备WSGI接口,所有的现代Python Web框架都已具备WSGI接口,它让你不对代码作修改就能使服务器和特点的web框架协同工作。
- WSGI由web服务器支持,而web框架允许你选择适合自己的配对,但它同样对于服务器和框架开发者提供便利使他们可以专注于自己偏爱的领域和专长而不至于相互牵制。其他语言也有类似接口:java有Servlet API,Ruby 有 Rack。
- 4.2、模仿WSGI 服务器代码的具体实现简单的叙述下WSGI的思路:1.在服务器有一个接收header头的方法,我们在此定义为
set_response_header
,2.在自定义的web框架,调用服务器的set_response_header
方法,并返回 body内容,具体的代码如下
- 服务器
if not file_name.endswith(".py"): pass else{ # 2.2 如果是以 .py 结尾请求就认为是动态资源请求 evn = dict() body = mini_wsgi_frame.application(evn,self.set_response_header) header = "HTTP/1.1 %s\r\n"%self.status # 200表示找到这个资源 for temp in self.headers: header += "%s:%s\r\n"%(temp[0],temp[1]) header += "\r\n" # 用一个空的行与body进行隔开 response = header + body # 准备发送给浏览器的数据 ---body # 将response发送给浏览器 new_socket.send(response.encode("utf-8")) } def set_response_header(self,status,headers): self.status = status self.headers = [("server","mini_web v9.2")] self.headers += headers
- 自定义web框架代码:
mini_wsgi_frame
def application(environ,start_response): start_response('200 OK',[('text/html;charset=utf-8')]) return 'Hello World!'
五、通过给web服务器传字典实现浏览器请求的资源不一样,响应的不一样
- 服务器:只写 是
.py
里面的代码
if not file_name.endswith(".py"): # 2.2 如果是以 .py 结尾请求就认为是动态资源请求 evn = dict() evn["PATH_INFO"] = file_name body = dynamic.mini_wsgi_true_frame.application(evn,self.set_response_header) header = "HTTP/1.1 %s\r\n"%self.status # 200表示找到这个资源 for temp in self.headers: header += "%s:%s\r\n"%(temp[0],temp[1]) header += "\r\n" # 用一个空的行与body进行隔开 response = header + body # 准备发送给浏览器的数据 ---body # 将response发送给浏览器 new_socket.send(response.encode("utf-8"))
- WSGI框架代码:
mini_wsgi_true_frame.py
import time def login(): return "welcome to our login ......time:%s"% time.ctime() def register(): return "welcome to our register ......time:%s"% time.ctime() def application(environ,start_response): start_response('200 OK',[('Content-Type','text/html;charset=utf-8')]) file_name = environ["PATH_INFO"] if file_name == "/login.py": return login() elif file_name == "/register.py": return register() else: return 'Hello World! 我爱中华'
六、给程序传递参数、添加web服务器的配置文件、添加shell功能
- 6.1、给程序传递参数(把上面代码中的端口写活),达到用终端运行
mini_wsgi_true_frame.py
class WSGIServer(object): def __init__(self,port): """用来完成整体的控制""" # 1.创建套接字 self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 2.绑定 self.tcp_server_socket.bind(("192.168.3.6", port)) # 3.变为监听套接字 self.tcp_server_socket.listen(128) def main(): if len(sys.argv) == 2: try: port = int(sys.argv[1]) # 8890 except Exception as ret: print("端口输入错误") return else: print("请按照以下方式运行") print("python3 xxx.py 7890") return wsgi_server = WSGIServer(port) wsgi_server.run_server() if __name__ == '__main__': main()
- 6.2、添加web服务器的配置文件之前的代码还要在服务器里面导入
import dynamic
,这个不太好,我们要写活,有可能,换框架,我们需要导入框架的名字来变成活的,那么我们就需要设置 配置文件,起名为web_server.conf
- web_server.conf里面的代码如下:提示这是一个字符串
{ "static_path":"./static", "dynamic_path":"./dynamic" }
- 核心代码如下
def main(): 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_wsgi_true_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_wsgi_true_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_server() if __name__ == '__main__': main()
- 6.3、添加shell功能
创建run.sh
文件
python3 JK_06_webServer.py 7290 mini_wsgi_true_frame:application
- 运行的时候,
cd run.sh所在的文件夹
,然后输入./run.sh
,回车键只有直接运行
提示:
run.sh
有可能没有执行的权限,可以输入chmod +x run.sh
来增加x
执行的权限
- 6.4、服务器完整的代码
import socket import multiprocessing import re 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) # 2.绑定 self.tcp_server_socket.bind(("192.168.3.6", port)) # 3.变为监听套接字 self.tcp_server_socket.listen(128) self.application = app self.static_path = static_path def server_client(self,new_socket): """为这个客户端返回数据""" # # 组织相应 头信息(header) # 1.接收浏览器发送过来的请求,即 http请求 # GET / HTTP/1.1 # .... request = new_socket.recv(1024).decode("utf-8") # print(request) request_lines = request.splitlines() print("") print(">"*20) print(request_lines) print("<" * 20) # 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("file_name=%s" % file_name) print("*"*50,file_name) if file_name == "/": file_name = "/index.html" # 2.返回http格式的数据,给浏览器 # 2.1、准备发送给浏览器的数据---header if not file_name.endswith(".py"): try: f = open(self.static_path+file_name,"rb") except: response = "HTTP/1.1 404 NOT FOUND\r\n" response += "\r\n" response += "----file not found" new_socket.send(response.encode("utf-8")) else: print("-----------OK------------") html_content = f.read() f.close() response = "HTTP/1.1 200 OK\r\n" # 200表示找到这个资源 response += "\r\n" # 用一个空的行与body进行隔开 # 2.2、准备发送给浏览器的数据 ---body # 将response的header发送给浏览器 new_socket.send(response.encode("utf-8")) # 将response的 body 发送给浏览器 new_socket.send(html_content) else: # 2.2 如果是以 .py 结尾请求就认为是动态资源请求 evn = dict() evn["PATH_INFO"] = file_name body = self.application(evn,self.set_response_header) header = "HTTP/1.1 %s\r\n"%self.status # 200表示找到这个资源 for temp in self.headers: header += "%s:%s\r\n"%(temp[0],temp[1]) header += "\r\n" # 用一个空的行与body进行隔开 response = header + body # 准备发送给浏览器的数据 ---body # 将response发送给浏览器 new_socket.send(response.encode("utf-8")) # 3.关闭套接字 new_socket.close() def set_response_header(self,status,headers): self.status = status self.headers = [("server","mini_web v9.2")] self.headers += headers def run_server(self): while True: # 4.等待客户端的链接 new_socket, client_addr = self.tcp_server_socket.accept() # 5.开辟一个进程为这个客户端服务 p = multiprocessing.Process(target=self.server_client,args=(new_socket,)) p.start() new_socket.close() # 6.关闭监听的套接字 tcp_server_socket.close() def main(): 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_wsgi_true_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_wsgi_true_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_server() if __name__ == '__main__': main()