python搭建簡單的靜態web服務器

python搭建簡單的靜態web服務器

[TOC]

儲備知識

  • 一丟丟的python(io和多線程的知識)
  • 一丟丟的http協議
  • 一丟丟的tcp/ip協議(當然不了解也沒關系)
  • 一丟丟的正則表達式知識

web服務器基本原理

  • 當在瀏覽器的地址欄輸入一個ip與端口之后,瀏覽器就會通過tcp/ip協議與相應的主機端口進程建立聯系。經歷過三次握手之后就會將http請求發送到相應的服務器進程去,之前我們了解的http協議在服務進程收到的其實就是一串有特殊格式的字符串。

    當我們瀏覽器輸入localhost:9876 后服務進程實際收到的如下:

大致流程

  1. 在服務端建立tcp服務進程,為了保證服務端可以同時處理多個請求,我們需要在每接受一個請求后為其單獨使用一個線程(或者進程)為其進行服務。

    server = socket(AF_INET, SOCK_STREAM)
    server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
    address = ('', 9876)
    server.bind(address)
    server.listen(10)
    try:
        while True:
            print("-------等待接受服務----------")
            client, client_address = server.accept()
            print("-------接受服務成功----------")
            # 如果使用進程服務,可以在在后面把client關閉。
            p = Thread(target=deal_socket, args=(client,))
            p.start()
    except Exception as e:
           print(e)
    finally:
           server.close()
           print("-------服務結束----------")
    

    這里的deal_socket函數就是我們為一個請求服務的函數,在一個單獨的線程中運行。

  2. 在處理url請求的函數中,我們需要讀取出客戶端的http請求。

    def deal_socket(client):
        print("-------開啟新的線程----------")
        try:
            data = client.recv(1024)
            if len(data) > 0:
                fileName = get_request_name_from_http(data.decode("utf-8"))
                writeHtml(client, fileName)
    
        finally:
            client.close()
            print("-------關閉新的線程----------")
    
    • 在這里的data就是我們讀取到的http服務請求,當其長度等于0時代表客戶端已經關閉了tcp連接。
    • 這里的get_request_name_from_http()需要我們解析出請求的靜態資源
    • 這里的writeHtml()將靜態文本寫回到客戶端。
  3. get_request_name_from_http函數中我們需要從原始的url請求中解析出http請求中我們需要的請求資源部分,這里我們可以通過正則表達式完成簡單的完成解析。

    def get_request_name_from_http(http):
        # 注意這里通過非貪婪模式匹配
        r = re.search(r"GET /(.+?) ", http)
        fileName = ""
        if r != None:
            fileName = r.group(1)
        return fileName
    
    • 請求的http大概格式是這樣(當我們訪問http://localhost:9876/html/index.html時)

      GET /html/index.html HTTP/1.1
      Host: localhost:9876
      Connection: keep-alive
      Cache-Control: max-age=0
      Upgrade-Insecure-Requests: 1
      User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.109 Safari/537.36
      

      第一行就是我們請求的靜態資源,我們將其通過非貪婪模式的正則表達式扣出來。

  4. 在解析到請求的靜態地址后就是簡單的讀取請求的文件,然后已http協議的格式返回回去就行了。

    def writeHtml(client, fileName):
        rspHead = None
        rspBody = None
        if not os.path.exists(fileName):
            rspHead = "HTTP/1.1 404 error\r\nServer: foreverServer\r\n\r\n"
            rspBody = "file not found"
        else:
            rspHead = "HTTP/1.1 200 OK\r\nServer: foreverServer\r\n\r\n"
            html = open(fileName, 'r', encoding='UTF-8')
            rspBody = html.read()
        client.send((rspHead + rspBody).encode("utf-8"))
    
    • 當請求的靜態文件不存在時,將返回給客戶端文件不存在。

    • 上面的相應格式是根據http相應報文的格式而定的,否則瀏覽器會不識別:

      ?

      在Windows中\r\n分別代表回車和換行,而現在在unix系統中\n就代表了回車換行。

      ?

完整代碼

  • 下面是完整的服務代碼,不到60行就可以完成一個簡單的靜態web服務器,這就是python的魅力:

    from socket import *
    from threading import Thread
    import os
    import re
    
    def get_request_name_from_http(http):
        r = re.search(r"GET /(.+?) ", http)
        fileName = ""
        if r != None:
            fileName = r.group(1)
        return fileName
    
    def writeHtml(client, fileName):
        rspHead = None
        rspBody = None
        if not os.path.exists(fileName):
            rspHead = "HTTP/1.1 404 error\r\nServer: foreverServer\r\n\r\n"
            rspBody = "file not found"
        else:
            rspHead = "HTTP/1.1 200 OK\r\nServer: foreverServer\r\n\r\n"
            html = open(fileName, 'r', encoding='UTF-8')
            rspBody = html.read()
        client.send((rspHead + rspBody).encode("utf-8"))
    def deal_socket(client):
        print("-------開啟新的線程----------")
        try:
            data = client.recv(1024)
            if len(data) > 0:
                fileName = get_request_name_from_http(data.decode("utf-8"))
                writeHtml(client, fileName)
    
        finally:
            client.close()
            print("-------關閉新的線程----------")
     def main():
         server = socket(AF_INET, SOCK_STREAM)
        server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
        address = ('', 9876)
        server.bind(address)
        server.listen(10)
        try:
            while True:
                print("-------等待接受服務----------")
                client, client_address = server.accept()
                print("-------接受服務成功----------")
                # 就這里和多線程不同,并且千萬不能把client關掉
                p = Thread(target=deal_socket, args=(client,))
                p.start()
        except Exception as e:
            print(e)
        finally:
            server.close()
            print("-------服務結束----------")
      if name == "main":
        main()
    
  • 到此就用python構建了史上最挫的靜態wen服務器了,直接在瀏覽其輸入靜態html請求就可以顯示網頁了:

    雖然很挫,不過web服務器的基本原理就是如此,牛逼的服務器也只是在這之上做了很多完善,下一篇我們將采用python提供的WSGI標準完成一個動態的web框架。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,837評論 18 139
  • web靜態服務器 服務端: 1.1.1顯示固定的頁面 參考代碼: import socket from multi...
    chen_000閱讀 2,088評論 0 1
  • http://python.jobbole.com/85231/ 關于專業技能寫完項目接著寫寫一名3年工作經驗的J...
    燕京博士閱讀 7,606評論 1 118
  • 1.1 回顧網絡編程 1.2 http協議介紹 HTTP是Hyper Text Transfer Protocol...
    TENG書閱讀 524評論 0 0
  • 今天早上在盤點部門主管的維持考核,一直到下午2點,下午跟娟兒到索菲亞酒店,參加芯美昕公司的說明會,跳出行業以外,去...
    卓彤的美好時光閱讀 155評論 0 0