前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >多进程web动态服务器

多进程web动态服务器

作者头像
不断折腾
发布2019-09-23 10:51:30
3.2K0
发布2019-09-23 10:51:30
举报

次会用到我们上次写的多进程服务器

我们既然学习了 面向对象,就用面向对象来改进一个这个程序:

import socket

import re

import multiprocessing

class server(object):

def __init__(self):

self.tcp_server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

# 关闭套接字后释放资源

self.tcp_server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)

self.tcp_server.bind(("",7891))

self.tcp_server.listen(128)

print('等待')

def dump_data(self,cli_socket):

recv_data = cli_socket.recv(1024).decode('utf-8')

recv_data_lines = recv_data.splitlines()

print(recv_data_lines)

ret = re.match(r'[^/]+(/[^ ]*)',recv_data_lines[0])

if ret:

f_name = ret.group(1)

if f_name=="/":

f_name = '/index.html'

try:

file = open('F:'+f_name,'rb')

except:

resp_data = 'HTTP/1.1 404 NOT FOUND\r\n'

resp_data += 'Content-Type:text/html;charset:uft-8\r\n'

resp_data += '\r\n'

resp_data += '404'

cli_socket.send(resp_data.encode('utf-8'))

else:

html_con = file.read()

file.close()

resp_data = 'HTTP/1.1 200 OK\r\n'

resp_data += 'Content-Type:text/html;charset:uft-8\r\n'

resp_data += '\r\n'

cli_socket.send(resp_data.encode('utf-8'))

cli_socket.send(html_con)

cli_socket.close()

def run_server(self,):

while True:

cli_socket,cli_addr = self.tcp_server.accept()

# 多进程实现调用该方法

p = multiprocessing.Process(target=self.dump_data,args =(cli_socket,))

p.start()

cli_socket.close()

self.tcp_server.close()

def main():

wsgiserver = server()

wsgiserver.run_server()

if __name__ == "__main__":

main()

现在我们返回的数据都是静态的,也就是说写好的,直接返回给浏览器,但是这并不能满足我们的需求,

比如今日头条里面的内容都是动态的,我们不能说每天都去修改html代码。

接下来我们来写一个可以解析动态的web服务器。

我们简单模拟一下,首先说一下思路,我们认为客户端如果请求的是.py结尾的文件,我们认为他请求的动态页面,我们给他返回一个随机数。(py文件是动态是我们暂时这样认为)

import socket

import re

import multiprocessing

import random

class server(object):

def __init__(self):

self.tcp_server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

# 关闭套接字后释放资源

self.tcp_server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)

self.tcp_server.bind(("",7891))

self.tcp_server.listen(128)

print('等待')

def dump_data(self,cli_socket):

recv_data = cli_socket.recv(1024).decode('utf-8')

recv_data_lines = recv_data.splitlines()

print(recv_data_lines)

ret = re.match(r'[^/]+(/[^ ]*)',recv_data_lines[0])

if ret:

f_name = ret.group(1)

if f_name=="/":

f_name = '/index.html'

# 添加判断是否是.py结尾

if f_name.endswith(".py"):

head = 'HTTP/1.1 404 NOT FOUND\r\n'

head += '\r\n'

# 生成一个0-100的随机数

body = random.randint(0,100)

resp_data = head + body

cli_socket.send(resp_data.encode('utf-8'))

else:

try:

file = open('F:'+f_name,'rb')

except:

resp_data = 'HTTP/1.1 404 NOT FOUND\r\n'

resp_data += 'Content-Type:text/html;charset:uft-8\r\n'

resp_data += '\r\n'

resp_data += '404'

cli_socket.send(resp_data.encode('utf-8'))

else:

html_con = file.read()

file.close()

resp_data = 'HTTP/1.1 200 OK\r\n'

resp_data += 'Content-Type:text/html;charset:uft-8\r\n'

resp_data += '\r\n'

cli_socket.send(resp_data.encode('utf-8'))

cli_socket.send(html_con)

cli_socket.close()

def run_server(self,):

while True:

cli_socket,cli_addr = self.tcp_server.accept()

# 多进程实现调用该方法

p = multiprocessing.Process(target=self.dump_data,args =(cli_socket,))

p.start()

cli_socket.close()

self.tcp_server.close()

def main():

wsgiserver = server()

wsgiserver.run_server()

if __name__ == "__main__":

main()

此时在浏览器中输入127.0.0.1:7891/a.py 或者以.py结尾的都会返回一个随机数,刷新会有变化。

这样写是实现了我们想要的效果,但是耦合性太高,我们可以把动态页面,也就是py文件单独写出来,只要是动态页面,我们让他执行该py文件即可,这是解耦的体现。

首先我们在当前文件夹下新建一个py文件写上:

import random

def login():

return f'欢迎登陆{random.randint(0,100)}'

将上面的web服务器修改:

1、导入该模块

import login

将动态部分的body修改成

body = login.login()

我们发现缺点很多,首先要导入模块,而且动态模块只有一个,要想有更多还有if判断。

最终还是要在服务器中国调用函数,我们想要把他分开。

很简单,我们把请求的方法从服务器传过去,在py文件中判断就好了。自己尝试一下。

我们刚刚写的login.py相当于一个很小的web框架,我们的很low,就就用别人写的,比如Django,在框架我服务器之前如何动态的传输?也有一套规则,我们将这套规则叫做WSGI协议。

WSGI协议

通俗的说:

1、框架中必须要有一个application方法

2、application需要有两个参数

3、两个参数:一个是字典,一个是对函数的引用

4、利用第二个参数(函数)获取到发送过来的header信息等返回给服务器状态和头信息

5、框架通过查询数据库等,生成一个动态的body,再发送给服务器

简单实现一个WSGI协议

简单实现返回 Holle World

将上面的login文件里面的内容全部删掉,写上:

def application(evairon,start_response):

# 第一个参数返回状态码,第二个以键值的方式返回你想添加的head部分

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

# 返回给网页的内容

return 'Holle World'

web服务器修改成:

import socket

import re

import multiprocessing

import login

class server(object):

def __init__(self):

self.tcp_server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

# 关闭套接字后释放资源

self.tcp_server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)

self.tcp_server.bind(("",7891))

self.tcp_server.listen(128)

print('等待')

def dump_data(self,cli_socket):

recv_data = cli_socket.recv(1024).decode('utf-8')

recv_data_lines = recv_data.splitlines()

print(recv_data_lines)

ret = re.match(r'[^/]+(/[^ ]*)',recv_data_lines[0])

if ret:

f_name = ret.group(1)

if f_name=="/":

f_name = '/index.html'

# 添加判断是否是.py结尾

if f_name.endswith(".py"):

# 要传入两个值,第一个为字典,第二个为一个方法

# 先定义一个空字典

# 传入一个方法接收heads值

env = dict()

body = login.application(env,self.set_start_response)

head = f'HTTP/1.1 {self.status}\r\n'

for item in self.heads:

head += f'{item[0]}:{item[1]}\r\n'

head += '\r\n'

resp_data = head + body

cli_socket.send(resp_data.encode('utf-8'))

else:

try:

file = open('F:'+f_name,'rb')

except:

resp_data = 'HTTP/1.1 404 NOT FOUND\r\n'

resp_data += 'Content-Type:text/html;charset:uft-8\r\n'

resp_data += '\r\n'

resp_data += '404'

cli_socket.send(resp_data.encode('utf-8'))

else:

html_con = file.read()

file.close()

resp_data = 'HTTP/1.1 200 OK\r\n'

resp_data += 'Content-Type:text/html;charset:uft-8\r\n'

resp_data += '\r\n'

cli_socket.send(resp_data.encode('utf-8'))

cli_socket.send(html_con)

cli_socket.close()

def set_start_response(self,status,heads):

self.status = status

self.heads = heads

def run_server(self,):

while True:

cli_socket,cli_addr = self.tcp_server.accept()

p = multiprocessing.Process(target=self.dump_data,args =(cli_socket,))

p.start()

cli_socket.close()

self.tcp_server.close()

def main():

wsgiserver = server()

wsgiserver.run_server()

if __name__ == "__main__":

main()

这样只要在浏览器输出以.py结尾的就会返回Holle World,如果想要添加中文,需要把login中的添加成:

start_response('200 ok',[('Content-Type','text/html;charset=utf-8')])即可。

我也想实现我的服务器:

修改成:start_response('200 ok',[('Content-Type','text/html;charset=utf-8'),('server','zhansgan 1.0')]),这样我们在浏览器中就可以看到我们设置的服务器。但是WSGI就是负责和服务器交互的,我们需要在服务器中修改。将set_start_response方法修改成:

def set_start_response(self,status,heads):

self.status = status

self.heads = [('server','zhangsan v1.0')]

self.heads += heads

由此我们看出set_start_response方法就是将服务器的东西和客户端发送过来的数据进行整合。

说完了这个函数的作用,我们来说第一个字典的作用,上面我们传了一个空字典,我们来看看它的作用。

首先我们只要访问以.py结尾的都会去访问该方法,我能不能传入login.py去访问登陆,create.py去访问注册页面?

这个字典就可以实现这个效果。

将login.py修改成:

def login():

return '<h1>登录的页面</h1>'

def create():

return '<h1>注册的页面</h1>'

def application(evairon,start_response):

# 第一个参数返回状态码,第二个以键值的方式返回你想添加的head部分

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

# 返回给网页的内容

if evairon['path_info'] == '/login.py':

return login()

elif evairon['path_info'] == '/create.py':

return create()

为服务器中的字典传入我们的页面名字的值:

env['path_info'] = f_name

即可。运行,在浏览器中是127.0.0.1:7891/login.py就会显示我是登录页面,create就会显示我是注册页面。

当然,字典里你也可以添加一些你想要的东西。

如果你想显示好看的页面可以在login.py中读取好看的页面返回即可。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-12-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 python入门到放弃 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档