pythonweb成功案例_从零开始的python世界的闯荡 第二十四话 web服务器案例---<Two>...

上一节咱们实现了静态web服务器。 本节咱们来尝试实现动态web服务器

一. 动态web服务器

#1.什么是动态 web 服务器

#答: 静态web服务器,就是只能显示出,咱们事先写好的页面。恒古不变,除非咱们手动的去修改html

文件

动态web服务器,在我们没有修改html代码的等情况的前提下,页面的内容会随着时间的改变而改

变。 比如,网页上的时间,咱们刷新网页,你会发现,时间发生了改变。

#2.动态 web服务器处理的过程

#答: 1.浏览器发送请求给服务器。

2.首先,服务器进行解析请求,

其次,通过某种方法,处理请求,并将此时的页面信息放入到body中。

3.服务器将响应发送给浏览器。

4.浏览器呈现出此刻的页面

其实 动态web服务器 和 静态web服务器 区别就在于

动态web服务器需要通过某种方法去生成动态的网页

#3.如何生成动态的页面?

#答: 1.我们要理解,这个动态的意思,并不是你的页面会动。而是你的数据会根据时间的不同而改变。

说是动态,其实在那一瞬间其实还是静态的数据。 比如,在网页上显示此时的时间,会随时间的

改变而改变。

2.以显示时间为例,如果咱们服务器有个函数,可以实时的显示此刻的时间,并发送给浏览器显示

出来。作为浏览器,显示出来的页面,是不是随着时间的变化,显示的时间也在改变?

3.没错,生成动态的页面,实际上可以粗略的认为,服务器运行了一个程序,并把结果给放在body

中,发送给了浏览器。

#好的,简单的生成动态页面的原理知道了,过程也知道了,咱们现在来实现一下。

#

#4.第一次实现动态web服务器(以显示此刻时间为例)

# coding:utf-8

import socket

import time

from multiprocessing import Process

#设置服务器路径

HTML_ROOT_PATH = "./static"

class HTTPServer(object):

""""""

def __init__(self):

"""构造方法"""

self.webS = socket.socket(family=socket.AF_INET,type=socket.SOCK_STREAM)

def start(self):

"""用于开启服务器的方法"""

self.webS.listen(5)

while True:

conn, addr = self.webS.accept()

client_p = Process(target = self.dealwithclient, args=(conn, addr))

client_p.start()

conn.close()

def dealwithclient(self, conn, addr):

"""实现显示用户指定的网页的方法"""

#1.接受客户端发来的请求

request_header = conn.recv(1024).decode("utf-8").splitlines()

for lines in request_header:

print(lines)

#2.进行筛选出请求的路径

path = request_header[0].split()[1]

# path ==> "/index.html"

#如果路径后缀为.py结尾,说明请求的是动态页面

if path.endswith(".py"):

#调用处理相应的函数,并获取数据

response_body = time.ctime()

response_header = "HTTP/1.1 200 OK \r\n"

response = response_header + "\r\n" + response_body

else:

#3.对路径进行判断

if path == "/":

path = HTML_ROOT_PATH+"/index.html"

else:

#先对路径切割再拼接

path = HTML_ROOT_PATH + path

response_body = ""

try:

#4.打开文件

with open(path, "r", encoding="utf-8") as f:

#设置响应头

response_header = "HTTP/1.1 200 OK \r\n"

for date in f:

response_body += date

except FileNotFoundError:

response_header = "HTTP/1.1 404 not found\r\n"

response_body = "

====sorry ,file not found====

"

finally:

#进行响应头和body的拼接

response = response_header + "\r\n" + response_body

conn.send(response.encode("utf-8"))

#4.关闭套接字

conn.close()

def bind(self, ipaddr, port):

"""绑定服务器的IP和端口"""

self.webS.bind((ipaddr,port))

def main():

"""主函数"""

http_server = HTTPServer()

http_server.bind("localhost", 9999)

http_server.start()

if __name__ == "__main__":

main()

#进行思考:

# 1. 首先上面这个代码,当咱们访问的页面是localhost:9999/xxx.py 都只会触发到这个IF语句,

然后显示时间,这就有很大的局限性。需要进行改进,使得能呈现出咱们指定的程序返回的结果。

# 2. 新增加的代码,都放在服务器类中,一旦咱们实现第一步后,服务器类中代码太长,不适宜维护

咱们要将程序放在别的文件中,然后进行调用即可。

#5.改进上述两个问题后,第二次实现动态 web服务器

# coding:utf-8

import socket

import time

import sys

from multiprocessing import Process

#设置服务器路径

HTML_ROOT_PATH = "./static"

DYNAMIC_PATH = "./dynamic"

class HTTPServer(object):

""""""

def __init__(self):

"""构造方法"""

self.webS = socket.socket(family=socket.AF_INET,type=socket.SOCK_STREAM)

def start(self):

"""用于开启服务器的方法"""

self.webS.listen(5)

while True:

conn, addr = self.webS.accept()

client_p = Process(target = self.dealwithclient, args=(conn, addr))

client_p.start()

conn.close()

def dealwithclient(self, conn, addr):

"""实现显示用户指定的网页的方法"""

#1.接受客户端发来的请求

response_body = ""

request_header = conn.recv(1024).decode("utf-8").splitlines()

for lines in request_header:

print(lines)

#2.进行筛选出请求的路径

path = request_header[0].split()[1]

# path ==> "/index.html"

#如果路径后缀为.py结尾,说明请求的是动态页面

if path.endswith(".py"):

#调用处理相应的函数,并获取数据

#咱们将每个新的程序都单独放在一个模块中

try:

#1.获取模块名,然后导入模块, 咱们需要使用一个方法 __import__()

m = __import__(path[1:-3])

response_body = m.application()

#假如中文乱码,那么需要在这儿设置content-type中设置charset为utf-8

response_header = "HTTP/1.1 200 OK \r\n" + "Content-Type:text/html;charset=utf-8 \r\n"

except ModuleNotFoundError:

#当没有这个程序的时候,走这儿,显示404

response_header = "HTTP/1.1 404 not found \r\n" + "Content-Type:text/html;charset=utf-8 \r\n"

response_body = "not found"

else:

#3.对路径进行判断

if path == "/":

path = HTML_ROOT_PATH+"/index.html"

else:

#先对路径切割再拼接

path = HTML_ROOT_PATH + path

try:

#4.打开文件

with open(path, "r", encoding="utf-8") as f:

#设置响应头

response_header = "HTTP/1.1 200 OK \r\n"

for date in f:

response_body += date

except FileNotFoundError:

response_header = "HTTP/1.1 404 not found\r\n"

response_body = "

====sorry ,file not found====

"

#进行响应头和body的拼接

response = response_header + "\r\n" + response_body

conn.send(response.encode("utf-8"))

#4.关闭套接字

conn.close()

def bind(self, ipaddr, port):

"""绑定服务器的IP和端口"""

self.webS.bind((ipaddr,port))

def main():

"""主函数"""

#添加导入模块的路径

sys.path.insert(1,"./dynamic")

http_server = HTTPServer()

http_server.bind("localhost", 9999)

http_server.start()

if __name__ == "__main__":

main()

#在上述代码中,咱们使用了 __import__()这个魔术方法

# __import__(模块名) : 这个方法是用来动态的调用模块,为啥是动态的呢,因为模块名是不固定的

有可能是time 也有可以是sys 。

__import__(sys) 相当于 import sys

并且返回一个值,用一个变量进行接收,之后对模块的操作,就是通过这个

变量。 比如a = __import__(sys) a.path 相当于 sys.path

#还有一点,为什么咱们写的两个程序,里面的函数名都要为相同的名字呢?

#答:这是为了主程序更方便的去调用他们,相同的函数名,就不用担心不同模块中的函数名不同而要进行

判断。

#粗略的看上述代码,发现已经能够很好的实现了需求,但是,如果这就满意了,那么你将不是一个合格

程序猿。 因为,上面的程序,只是咱们随心临时写出来的,书写规范等都还不是很好,所以,咱们要

严格的按照协议去进行修改。

#那么这个协议是什么协议呢?

二.WSGI协议

WSGI:(Web Server Gateway Interface)web服务器网关接口,他是服务器和web应用程序或者框架

的一种接口。 在早期的时候,咱们使用的框架不同,服务器的实现也有可能不同,这样使得开发

非常的艰难,因此为了让服务器和框架能够混合匹配,就产生了WSGI这个东西。

别看他称为web服务器网关接口,实际上,他在设计之初并未用严格的代码实现,他更像是一种协议

你只要满足他的协议,那么你的WSGI引用就能够在任何服务器上运行。

#那么我们如何来实现这个WSGI接口呢?

#解答: 它只要求Web开发者实现一个函数即可,函数的具体格式如下

def application(environ, start_response):

start_response("200 ok", [("Content-Type","text/html")])

return "response_body"

首先函数名 最好都设置成 application

其次两个参数:1. environ这个参数代表着 浏览器发送过来的请求头数据。值的格式为字典(dict)类型

2. start_response 这个参数是一个函数的引用,是用来发送服务器响应的函数。

然后start_response这个函数接收两个参数: 1. 第一个为状态码和状态的说明

2.第二个为响应头,值的格式为[(header1,header1_value),

(header2,header2_value)

...]

最后,application函数的返回值是作为响应的body。也就是常说的response_body

特别的,那么这个application函数由谁调用呢? 肯定是让WSGI服务器来调用了,因为还要传请求头

和发送响应的函数引用。

那么WSGI服务器哪里找呢? 其实我们可以进行实现,WSGI服务器有很多,咱们只要好好的按照协议,让

WSGI接口完成任务即可。

#按照wsgi协议进行修改代码:

# coding:utf-8

import socket

import time

import sys

from multiprocessing import Process

#设置服务器路径

HTML_ROOT_PATH = "./static"

DYNAMIC_PATH = "./dynamic"

class HTTPServer(object):

""""""

def __init__(self):

"""构造方法"""

self.webS = socket.socket(family=socket.AF_INET,type=socket.SOCK_STREAM)

def start(self):

"""用于开启服务器的方法"""

self.webS.listen(5)

while True:

conn, addr = self.webS.accept()

client_p = Process(target = self.dealwithclient, args=(conn, addr))

client_p.start()

conn.close()

def dealwithclient(self, conn, addr):

"""实现显示用户指定的网页的方法"""

#1.接受客户端发来的请求

response_body = ""

request_header = conn.recv(1024).decode("utf-8").splitlines()

for lines in request_header:

print(lines)

#2.进行筛选出请求的路径

path = request_header[0].split()[1]

# path ==> "/index.html"

#如果路径后缀为.py结尾,说明请求的是动态页面

if path.endswith(".py"):

#调用处理相应的函数,并获取数据

#咱们将每个新的程序都单独放在一个模块中

try:

#1.获取模块名,然后导入模块, 咱们需要使用一个方法 __import__()

m = __import__(path[1:-3])

response_body = m.application({},self.start_response)

except ModuleNotFoundError:

#2.当没有这个程序的时候,走这儿,显示404

self.response_header = "HTTP/1.1 404 not found \r\n" + "Content-Type:text/html;charset=utf-8 \r\n"

response_body = "not found"

finally:

#3.拼接整个响应头

response = self.response_header + "\r\n" + response_body

else:

#3.对路径进行判断

if path == "/":

path = HTML_ROOT_PATH+"/index.html"

else:

path = HTML_ROOT_PATH + path

try:

#4.打开文件

with open(path, "r", encoding="utf-8") as f:

#设置响应头

response_header = "HTTP/1.1 200 OK \r\n"

for date in f:

response_body += date

except FileNotFoundError:

response_header = "HTTP/1.1 404 not found\r\n"

response_body = "

====sorry ,file not found====

"

finally:

#进行响应头和body的拼接

response = response_header + "\r\n" + response_body

conn.send(response.encode("utf-8"))

#4.关闭套接字

conn.close()

def bind(self, ipaddr, port):

"""绑定服务器的IP和端口"""

self.webS.bind((ipaddr,port))

def start_response(self, status, response_header):

"""status : 状态码

respense_header : [(,),()...]

"""

#第一行响应头

self.response_header = "HTTP/1.1 " + status + "\r\n"

#拼接后面的响应头

for header, value in response_header:

self.response_header += header + ":" + value + "\r\n"

def main():

"""主函数"""

#添加导入模块的路径

sys.path.insert(1,"./dynamic")

http_server = HTTPServer()

http_server.bind("localhost", 9999)

http_server.start()

if __name__ == "__main__":

main()

#通过上面的实现,已经符合了WSGI协议了。 但是这样依旧不太完美,因为,如果想要把这个程序打包

给他人使用的话:

首先,实现动态页面的程序,必须放置在dynamic这个目录下。

其次,当用户有新的需求,就得写一个新的py程序,来生成实时数据,而且格式都像ctime2.py这个文件

一样。这就造成了重复累赘。

最后,咱们应该将程序代码进行优化,除去重复代码。

#所以呢,咱们综上所述,咱们可以开发一个框架,来解决上述的问题。

#框架(利用面向对象)代码的实现:

#首先是HTTPServer服务器:

------------------------

dynamic_web_server.py

------------------------

# coding:utf-8

import socket

import sys

import my_web_framework

from multiprocessing import Process

#设置服务器路径

HTML_ROOT_PATH = "./static"

DYNAMIC_PATH = "./dynamic"

class HTTPServer(object):

""""""

def __init__(self):

"""构造方法"""

self.webS = socket.socket(family=socket.AF_INET,type=socket.SOCK_STREAM)

def start(self):

"""用于开启服务器的方法"""

self.webS.listen(5)

while True:

conn, addr = self.webS.accept()

client_p = Process(target = self.dealwithclient, args=(conn, addr))

client_p.start()

conn.close()

def dealwithclient(self, conn, addr):

"""实现显示用户指定的网页的方法"""

#1.接受客户端发来的请求

response_body = ""

request_header = conn.recv(1024).decode("utf-8").splitlines()

for lines in request_header:

print(lines)

#2.进行筛选出请求的路径

path = request_header[0].split()[1]

# path ==> "/index.html"

response_body = my_web_framework.application({"path": path}, self.start_response)

response = self.response_header + "\r\n" + response_body

conn.send(response.encode("utf-8"))

#4.关闭套接字

conn.close()

def bind(self, ipaddr, port):

"""绑定服务器的IP和端口"""

self.webS.bind((ipaddr,port))

def start_response(self, status, response_header):

"""status : 状态码

respense_header : [(,),()...]

"""

#第一行响应头

self.response_header = "HTTP/1.1 " + status + "\r\n"

#拼接后面的响应头

for header, value in response_header:

self.response_header += header + ":" + value + "\r\n"

def main():

"""主函数"""

#添加导入模块的路径

sys.path.insert(1,"./dynamic")

http_server = HTTPServer()

http_server.bind("localhost", 9999)

http_server.start()

if __name__ == "__main__":

main()

接下来是框架代码:

----------------------

my_web_framework.py

----------------------

import time

#当用户有新的需求,那么只要在url中添加新的映射关系, 路径名 : 函数名

#以及在本程序中添加新的函数即可。

HTML_ROOT_PATH = "./static"

class Application(object):

"""框架的主体"""

def __init__(self, url):

""""""

self.url = url

def __call__(self, environ, start_response):

"""

environ: 值为请求头,dict类型数据

start_response: 返回响应头的函数

return: 返回的是response_body

"""

#获取请求的路径名

path = environ.get("path")

#对请求静态Or动态页面进行判断

if self.url.get(path) != None:

#如果在框架中,说明是动态页面,首先拼接响应头

start_response("200 ok ",[("Content-Type","text/html;charset=utf-8")])

#最后是获取响应body

filename = self.url.get(path)

return filename()

#如果能执行到这,说明是静态页面 或者页面不存在

response_body = ""

if path == "/":

path = HTML_ROOT_PATH+"/index.html"

else:

path = HTML_ROOT_PATH + path

try:

#4.打开文件

with open(path, "r", encoding="utf-8") as f:

#设置接受数据的变量

for date in f:

response_body += date

except FileNotFoundError:

start_response("404 not found",[("Content-Type","text/html;charset=utf-8")])

return "找不到相应页面----not found"

else:

start_response("200 ok ",[("Content-Type","text/html;charset=utf-8")])

return response_body

def ctime2():

""""""

return "ctime2显示的时间为:" + time.ctime()

def ctime():

""""""

return "ctime显示的时间为:" + time.ctime()

#url映射,框架中有的函数,都在这url中能找到

url = {

"/ctime2.py" : ctime2,

"/ctime.py" : ctime,

}

application = Application(url)

#特别说明: 在框架中还使用到了一个 __call__()内置方法。

__call__(): 当我们实例出一个类对象,如果将对象当成函数一样使用,那么实际上是调用__call__方法

比如 上图中的 Application类。

application = Application()

application() ---> 就是调用 application.__call__()

#WSGI这个协议,在以后咱们学习django 模块的时候还能够接触到,实际的原理就是咱们上面写的代码。

三.总结:

浏览器请求动态页面的过程:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值