python網絡-動態Web服務器案例(30)

1、瀏覽器請求HTML頁面的過程

  瞭解了HTTP協議和HTML文檔,其實就明白了一個Web應用的本質就是:html

  1. 瀏覽器發送一個HTTP請求;python

  2. 服務器收到請求,生成一個HTML文檔;web

  3. 服務器把HTML文檔做爲HTTP響應的Body發送給瀏覽器;瀏覽器

  4. 瀏覽器收到HTTP響應,從HTTP Body取出HTML文檔並顯示。服務器

2、瀏覽器請求動態頁面的過程

3、WSGI

一、WSGI介紹併發

PythonWeb服務器網關接口(Python Web Server Gateway Interface,縮寫爲WSGI)是Python應用程序或框架和Web服務器之間的一種接口,app

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處理函數,它接收兩個參數:socket

    • environ:一個包含全部HTTP請求信息的dict對象;tcp

    • start_response:一個發送HTTP響應的函數。

application()函數中,調用:

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

二、運行WSGI服務

一、 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>"

二、而後,再編寫一個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()

三、確保以上兩個文件在同一個目錄下,而後使用命令行輸入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   

 

  四、 按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")

  五、你能夠在地址欄輸入用戶名做爲URL的一部分,將返回Hello, xxx

 

4、動態web服務器案例

一、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()

二、應用程序代碼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()]

三、命令行執行代碼 

四、瀏覽器運行結果

相關文章
相關標籤/搜索