瞭解了HTTP協議和HTML文檔,其實就明白了一個Web應用的本質就是:html
瀏覽器發送一個HTTP請求;python
服務器收到請求,生成一個HTML文檔;web
服務器把HTML文檔做爲HTTP響應的Body發送給瀏覽器;瀏覽器
瀏覽器收到HTTP響應,從HTTP Body取出HTML文檔並顯示。服務器
一、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
一、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()]
三、命令行執行代碼
四、瀏覽器運行結果