欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

python网络-动态Web服务器案例(30)

程序员文章站 2022-04-29 17:53:07
一、浏览器请求HTML页面的过程 了解了HTTP协议和HTML文档,其实就明白了一个Web应用的本质就是: 浏览器发送一个HTTP请求; 服务器收到请求,生成一个HTML文档; 服务器把HTML文档作为HTTP响应的Body发送给浏览器; 浏览器收到HTTP响应,从HTTP Body取出HTML文档 ......

一、浏览器请求html页面的过程

  了解了http协议和html文档,其实就明白了一个web应用的本质就是:

  1. 浏览器发送一个http请求;

  2. 服务器收到请求,生成一个html文档;

  3. 服务器把html文档作为http响应的body发送给浏览器;

  4. 浏览器收到http响应,从http body取出html文档并显示。

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

python网络-动态Web服务器案例(30)

三、wsgi

1、wsgi介绍

pythonweb服务器网关接口(python web server gateway interface,缩写为wsgi)是python应用程序或框架和web服务器之间的一种接口,

wsgi接口定义非常简单,它只要求web开发者实现一个函数,就可以响应http请求。

# wsgi 规范的函数
def application(environ, start_response): start_response('200 ok', [('content-type', 'text/html')]) return '<h1>hello, se7en_hou!</h1>'

上面的application()函数就是符合wsgi标准的一个http处理函数,它接收两个参数:

    • environ:一个包含所有http请求信息的dict对象;

    • start_response:一个发送http响应的函数。

application()函数中,调用:

start_response('200 ok', [('content-type', 'text/html')])

2、运行wsgi服务

1、 python内置了一个wsgi服务器,这个模块叫wsgiref,首先我们先实现一个hello.py文件,实现web应用程序的wsgi处理函数

def application(environ, start_response):
    start_response("200 ok",[("content-type","text/html")])
    return "<h1>hello,se7en_hou!</h1>"

2、然后,再编写一个server.py,负责启动wsgi服务器,加载application()函数: 

 

#coding:utf-8

# 导入wsgiref模块
from wsgiref.simple_server import make_server
from hello import application


# 创建一个服务器,ip地址为空,端口号为7788,处理的函数是application
httpserver = make_server("", 7788, application)
# 开始监听http请求
httpserver.serve_forever()

3、确保以上两个文件在同一个目录下,然后使用命令行输入python server.py来启动wsgi服务器:

houleidemacpro:wsgi se7en_hou$ python server.py
127.0.0.1 - - [19/jun/2019 15:52:37] "get / http/1.1" 200 24
127.0.0.1 - - [19/jun/2019 15:52:37] "get /favicon.ico http/1.1" 200 24   

python网络-动态Web服务器案例(30)

 

  4、 按ctrl+c终止服务器。如果你觉得这个web应用太简单了,可以稍微改造一下,从environ里读取path_info,这样可以显示更加动态的内容:

def application(environ, start_response):
    start_response("200 ok",[("content-type","text/html")])
    return "<h1>hello,%s</h1>"%(environ["path_info"][1:] or "se7en_hou")

  5、你可以在地址栏输入用户名作为url的一部分,将返回hello, xxx

 python网络-动态Web服务器案例(30)

四、动态web服务器案例

1、daynamic.py

#coding=utf-8
import socket
import sys
from multiprocessing import process
import re

class wsgiserver(object):

    addressfamily = socket.af_inet
    sockettype = socket.sock_stream
    requestqueuesize = 5

    def __init__(self, serveraddress):
        #创建一个tcp套接字
        self.listensocket = socket.socket(self.addressfamily,self.sockettype)
        #允许重复使用上次的套接字绑定的port
        self.listensocket.setsockopt(socket.sol_socket, socket.so_reuseaddr, 1)
        #绑定
        self.listensocket.bind(serveraddress)
        #变为被动,并制定队列的长度
        self.listensocket.listen(self.requestqueuesize)

        self.servrname = "localhost"
        self.serverport = serveraddress[1]

    def serveforever(self):
        '循环运行web服务器,等待客户端的链接并为客户端服务'
        while true:
            #等待新客户端到来
            self.clientsocket, client_address = self.listensocket.accept()

            #方法2,多进程服务器,并发服务器于多个客户端
            newclientprocess = process(target = self.handlerequest)
            newclientprocess.start()

            #因为创建的新进程中,会对这个套接字+1,所以需要在主进程中减去依次,即调用一次close
            self.clientsocket.close()

    def setapp(self, application):
        '设置此wsgi服务器调用的应用程序入口函数'
        self.application = application

    def handlerequest(self):
        '用一个新的进程,为一个客户端进行服务'
        self.recvdata = self.clientsocket.recv(2014)
        requestheaderlines = self.recvdata.splitlines()
        for line in requestheaderlines:
            print(line)

        httprequestmethodline = requestheaderlines[0]
        getfilename = re.match("[^/]+(/[^ ]*)", httprequestmethodline).group(1)
        print("file name is ===>%s"%getfilename) #for test

        if getfilename[-3:] != ".py":

            if getfilename == '/':
                getfilename = documentroot + "/index.html"
            else:
                getfilename = documentroot + getfilename

            print("file name is ===2>%s"%getfilename) #for test

            try:
                f = open(getfilename)
            except ioerror:
                responseheaderlines = "http/1.1 404 not found\r\n"
                responseheaderlines += "\r\n"
                responsebody = "====sorry ,file not found===="
            else:
                responseheaderlines = "http/1.1 200 ok\r\n"
                responseheaderlines += "\r\n"
                responsebody = f.read()
                f.close()
            finally:
                response = responseheaderlines + responsebody
                self.clientsocket.send(response)
                self.clientsocket.close()
        else:
            #处理接收到的请求头
            self.parserequest()

            #根据接收到的请求头构造环境变量字典
            env = self.getenviron()

            #调用应用的相应方法,完成动态数据的获取
            bodycontent = self.application(env, self.startresponse)

            #组织数据发送给客户端
            self.finishresponse(bodycontent)

    def parserequest(self):
        '提取出客户端发送的request'
        requestline = self.recvdata.splitlines()[0]
        requestline = requestline.rstrip('\r\n')
        self.requestmethod, self.path, self.requestversion = requestline.split(" ")

    def getenviron(self):
        env = {}
        env['wsgi.version']      = (1, 0)
        env['wsgi.input']        = self.recvdata
        env['request_method']    = self.requestmethod    # get
        env['path_info']         = self.path             # /index.html
        return env

    def startresponse(self, status, response_headers, exc_info=none):
        serverheaders = [
            ('date', 'tue, 31 mar 2016 10:11:12 gmt'),
            ('server', 'wsgiserver 0.2'),
        ]
        self.headers_set = [status, response_headers + serverheaders]

    def finishresponse(self, bodycontent):
        try:
            status, response_headers = self.headers_set
            #response的第一行
            response = 'http/1.1 {status}\r\n'.format(status=status)
            #response的其他头信息
            for header in response_headers:
                response += '{0}: {1}\r\n'.format(*header)
            #添加一个换行,用来和body进行分开
            response += '\r\n'
            #添加发送的数据
            for data in bodycontent:
                response += data

            self.clientsocket.send(response)
        finally:
            self.clientsocket.close()

#设定服务器的端口
serveraddr = (host, port) = '', 8888
#设置服务器静态资源的路径
documentroot = './html'
#设置服务器动态资源的路径
pythonroot = './wsgipy'

def makeserver(serveraddr, application):
    server = wsgiserver(serveraddr)
    server.setapp(application)
    return server

def main():

    if len(sys.argv) < 2:
        sys.exit('请按照要求,指定模块名称:应用名称,例如 module:callable')

    #获取module:callable
    apppath = sys.argv[1]
    #根据冒号切割为module和callable
    module, application = apppath.split(':')
    print("module=%s"%module)
    #添加路径套sys.path
    sys.path.insert(0, pythonroot)
    #动态导入module变量中指定的模块
    module = __import__(module)
    #获取module变量中制定的模块的application变量指定的属性
    application = getattr(module, application)
    httpd = makeserver(serveraddr, application)
    print('wsgiserver: serving http on port {port} ...\n'.format(port=port))
    httpd.serveforever()

if __name__ == '__main__':
    main()

2、应用程序代码ctime.py

#coding:utf-8


import time


def app(environ, start_response):
    status = "200 ok"

    response_headers = [
        ("content-type", "text/plain")
    ]

    start_response(status, response_headers)
    return [str(environ)+'--->%s\n'%time.ctime()]

3、命令行执行代码 

python网络-动态Web服务器案例(30)

4、浏览器运行结果

python网络-动态Web服务器案例(30)