你好呀~欢迎关注微信公众号:python入门到放弃

多进程web动态服务器

Python基础 admin 597浏览

这次会用到我们上次写的多进程服务器 我们既然学习了 面向对象,就用面向对象来改进一个这个程序:

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中读取好看的页面返回即可。 公众号后台回复web服务器,获取源代码。

转载请注明: 十三 » 多进程web动态服务器

喜欢 (0) or 分享 ( 0)