Python_web-靜態(tài)服務器

非阻塞網絡IO

  • 非阻塞的特點:當沒有數據來的時候不阻塞當前進程等待,而是報出一個異常 (套接字.setblocking(False))

IO多路復用

  • 多路IO好處就在于單個process就可以同時處理多個網絡連接的IO
  • 特點: 通過一種機制使一個進程能同時等待多個文件描述符,而這些文件描述(套接字描述符)其中的任意一個進入讀就緒狀態(tài),epoll()函數就可以返回
  • epoll 只能在Linux中使用
  • EPOLLIN(可讀)
  • EPOLLOUT(可寫)
  • EPOLLET(ET模式)
水平觸發(fā)和邊緣觸發(fā)
  • LT(level trigger):會在數據存在的情況下一直通知
  • ET(edge trigger): 只在數據到達的一刻通知一次

文件描述符

  • 文件描述符就是對進程內部所擁有文件資源的一種描述的符號,是一個無符號整數(0,1,2...)
  • 啟動一個程序默認啟動 標準輸入、標準輸出、標準錯誤 sock.fileno()

web服務器-多線程

import socket
import threading


def request_handler(client_socket):
    """專門來處理客戶端請求的函數"""
    # 接收用戶請求
    recv_data = client_socket.recv(1024)
    if not recv_data:
        print("客戶端已斷開連接")
        client_socket.close()
        return
    # 解碼數據
    recv_str_data = recv_data.decode()
    # 切割請求數據-->列表,取第0個元素  GET /index2.html HTTP/1.1\r\n
    request_line = recv_str_data.split("\r\n")[0]
    # 再次切割 取列表第一個元素 就是用戶路徑 /index2.html
    path_info = request_line.split(" ")[1]

    if path_info == "/":
        path_info = "/index.html"

    try:
        # # 嘗試打開用戶需要的文件, 不存在則拋出異常
        # f = open("./static" + path_info, "rb")
        # # 如果文件較大 容易產生隱患
        # # 讀出文件數據
        # ret = f.read()
        # f.close()
        # with 語句 自動將對象的資源驚醒釋放--> 上下文管理
        # 支持的數據資源有文件 socket 互斥鎖等
        # static文件夾為你訪問的數據(自備),與本程序放在同一目錄下
        with open("./static" + path_info, "rb") as f:
            # 讀出文件數據
            ret = f.read()


    except Exception as e:
        response_line = "HTTP/1.1 404 NOt Found\r\n"
        response_header = "Server: PythonServer2.0\r\n"
        response_body = "ERROR"
        response_data = response_line + response_header + "\r\n" + response_body
        client_socket.send(response_data.encode())
    else:
        # 響應行 響應頭 \r\n 響應體
        # 響應行
        response_line = "HTTP/1.1 200 OK\r\n"
        # 響應頭
        response_header = "Server: PythonServer1.0\r\n"
        # 響應體
        request_body = ret
        # 報文拼接
        data = (response_line + response_header + "\r\n").encode() + request_body
        # 發(fā)送
        client_socket.send(data)
    finally:
        # 關閉
        client_socket.close()


if __name__ == '__main__':
    # 創(chuàng)建套接字
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 地址重用(1.設置 0.取消)
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    # 綁定端口
    server_socket.bind(("", 8888))
    # 監(jiān)聽
    server_socket.listen(128)
    while True:
        # 取出一個客戶端套接字用以通信
        client_socket, client_address = server_socket.accept()
        print("接收到來自客戶端%s的請求" % str(client_address))
        # request_handler(client_socket)
        # 為每個客戶端請求的執(zhí)行都創(chuàng)建一個線程
        # 創(chuàng)建線程
        thd = threading.Thread(target=request_handler, args=(client_socket, ))
        thd.start()

web服務器-多進程

import socket
import multiprocessing


def request_handler(client_socket):
    """專門來處理客戶端請求的函數"""
    # 接收用戶請求
    recv_data = client_socket.recv(1024)
    if not recv_data:
        print("客戶端已斷開連接")
        client_socket.close()
        return
    # 解碼數據
    recv_str_data = recv_data.decode()
    # 切割請求數據-->列表,取第0個元素  GET /index2.html HTTP/1.1\r\n
    request_line = recv_str_data.split("\r\n")[0]
    # 再次切割 取列表第一個元素 就是用戶路徑 /index2.html
    path_info = request_line.split(" ")[1]

    if path_info == "/":
        path_info = "/index.html"

    try:
        # # 嘗試打開用戶需要的文件, 不存在則拋出異常
        # f = open("./static" + path_info, "rb")
        # # 如果文件較大 容易產生隱患
        # # 讀出文件數據
        # ret = f.read()
        # f.close()
        # with 語句 自動將對象的資源驚醒釋放--> 上下文管理
        # 支持的數據資源有文件 socket 互斥鎖等      
        # static文件夾為你訪問的數據(自備),與本程序放在同一目錄下
        with open("./static" + path_info, "rb") as f:
            # 讀出文件數據
            ret = f.read()

    except Exception as e:
        response_line = "HTTP/1.1 404 NOt Found\r\n"
        response_header = "Server: PythonServer2.0\r\n"
        response_body = "ERROR"
        response_data = response_line + response_header + "\r\n" + response_body
        client_socket.send(response_data.encode())
    else:
        # 響應行 響應頭 \r\n 響應體
        # 響應行
        response_line = "HTTP/1.1 200 OK\r\n"
        # 響應頭
        response_header = "Server: PythonServer1.0\r\n"
        # 響應體
        request_body = ret
        # 報文拼接
        data = (response_line + response_header + "\r\n").encode() + request_body
        # 發(fā)送
        client_socket.send(data)
    finally:
        # 關閉
        client_socket.close()


if __name__ == '__main__':
    # 創(chuàng)建套接字
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 地址重用(1.設置 0.取消)
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    # 綁定端口
    server_socket.bind(("", 8888))
    # 監(jiān)聽
    server_socket.listen(128)
    while True:
        # 取出一個客戶端套接字用以通信
        client_socket, client_address = server_socket.accept()
        print("接收到來自客戶端%s的請求" % str(client_address))
        # request_handler(client_socket)
        # 為每個客戶端請求的執(zhí)行都創(chuàng)建一個線程
        # 創(chuàng)建進程
        pro = multiprocessing.Process(target=request_handler, args=(client_socket,))
        pro.start()
        # 關閉在父進程的套接字,因為在子進程中使用這個套接字,而父進程中已經不需要了
        # 父進程和子進程中共有兩個對象 引用底層的套接字資源 為了讓套接字正常使用
        client_socket.close()

web服務器-面向對象

import socket
import multiprocessing


class HTTPServer(object):
    def __init__(self):
        """創(chuàng)建服務器相關資源"""
        # 創(chuàng)建套接字
        server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 地址重用(1.設置 0.取消)
        server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        # 綁定端口
        server_socket.bind(("", 8888))
        # 監(jiān)聽
        server_socket.listen(128)

        self.server_socket = server_socket

    def request_handler(self, client_socket):
        """專門來處理客戶端請求的函數"""
        # 接收用戶請求
        recv_data = client_socket.recv(1024)
        if not recv_data:
            print("客戶端已斷開連接")
            client_socket.close()
            return
        # 解碼數據
        recv_str_data = recv_data.decode()
        # 切割請求數據-->列表,取第0個元素  GET /index2.html HTTP/1.1\r\n
        request_line = recv_str_data.split("\r\n")[0]
        # 再次切割 取列表第一個元素 就是用戶路徑 /index2.html
        path_info = request_line.split(" ")[1]

        if path_info == "/":
            path_info = "/index.html"

        try:
            # # 嘗試打開用戶需要的文件, 不存在則拋出異常
            # f = open("./static" + path_info, "rb")
            # # 如果文件較大 容易產生隱患
            # # 讀出文件數據
            # ret = f.read()
            # f.close()
            # with 語句 自動將對象的資源驚醒釋放--> 上下文管理
            # 支持的數據資源有文件 socket 互斥鎖等
            # static文件夾為你訪問的數據(自備),與本程序放在同一目錄下
            with open("./static" + path_info, "rb") as f:
                # 讀出文件數據
                ret = f.read()


        except Exception as e:
            response_line = "HTTP/1.1 404 NOt Found\r\n"
            response_header = "Server: PythonServer2.0\r\n"
            response_body = "ERROR"
            response_data = response_line + response_header + "\r\n" + response_body
            client_socket.send(response_data.encode())
        else:
            # 響應行 響應頭 \r\n 響應體
            # 響應行
            response_line = "HTTP/1.1 200 OK\r\n"
            # 響應頭
            response_header = "Server: PythonServer1.0\r\n"
            # 響應體
            request_body = ret
            # 報文拼接
            data = (response_line + response_header + "\r\n").encode() + request_body
            # 發(fā)送
            client_socket.send(data)
        finally:
            # 關閉
            client_socket.close()

    def start(self):
        while True:
            # 取出一個客戶端套接字用以通信
            client_socket, client_address = self.server_socket.accept()
            print("接收到來自客戶端%s的請求" % str(client_address))
            # request_handler(client_socket)
            # 為每個客戶端請求的執(zhí)行都創(chuàng)建一個線程
            # 創(chuàng)建進程
            pro = multiprocessing.Process(target=self.request_handler, args=(client_socket,))
            pro.start()
            # 關閉在父進程的套接字,因為在子進程中使用這個套接字,而父進程中已經不需要了
            # 父進程和子進程中共有兩個對象 引用底層的套接字資源 為了讓套接字正常使用
            client_socket.close()


if __name__ == '__main__':
    # 創(chuàng)建一個web服務器實例對象
    hs = HTTPServer()
    # 調用實例方法
    hs.start()

web服務器-協程

from gevent import monkey

monkey.patch_all()
import gevent
import socket


class HTTPServer(object):
    def __init__(self):
        """創(chuàng)建服務器相關資源"""
        # 創(chuàng)建套接字
        server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 地址重用(1.設置 0.取消)
        server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        # 綁定端口
        server_socket.bind(("", 8888))
        # 監(jiān)聽
        server_socket.listen(128)

        self.server_socket = server_socket

    def request_handler(self, client_socket):
        """專門來處理客戶端請求的函數"""
        # 接收用戶請求
        recv_data = client_socket.recv(1024)
        if not recv_data:
            print("客戶端已斷開連接")
            client_socket.close()
            return      
        # 解碼數據
        recv_str_data = recv_data.decode()
        # 切割請求數據-->列表,取第0個元素  GET /index2.html HTTP/1.1\r\n
        request_line = recv_str_data.split("\r\n")[0]
        # 再次切割 取列表第一個元素 就是用戶路徑 /index2.html
        path_info = request_line.split(" ")[1]

        if path_info == "/":
            path_info = "/index.html"

        try:
            # # 嘗試打開用戶需要的文件, 不存在則拋出異常
            # f = open("./static" + path_info, "rb")
            # # 如果文件較大 容易產生隱患
            # # 讀出文件數據
            # ret = f.read()
            # f.close()
            # with 語句 自動將對象的資源驚醒釋放--> 上下文管理
            # 支持的數據資源有文件 socket 互斥鎖等
            # static文件夾為你訪問的數據(自備),與本程序放在同一目錄下
            with open("./static" + path_info, "rb") as f:
                # 讀出文件數據
                ret = f.read()


        except Exception as e:
            response_line = "HTTP/1.1 404 NOt Found\r\n"
            response_header = "Server: PythonServer2.0\r\n"
            response_body = "ERROR"
            response_data = response_line + response_header + "\r\n" + response_body
            client_socket.send(response_data.encode())
        else:
            # 響應行 響應頭 \r\n 響應體
            # 響應行
            response_line = "HTTP/1.1 200 OK\r\n"
            # 響應頭
            response_header = "Server: PythonServer1.0\r\n"
            # 響應體
            request_body = ret
            # 報文拼接
            data = (response_line + response_header + "\r\n").encode() + request_body
            # 發(fā)送
            client_socket.send(data)
        finally:
            # 關閉
            client_socket.close()

    def start(self):
        while True:
            # 取出一個客戶端套接字用以通信
            client_socket, client_address = self.server_socket.accept()
            print("接收到來自客戶端%s的請求" % str(client_address))
            # 創(chuàng)建并執(zhí)行協程
            gevent.spawn(self.request_handler, client_socket)


if __name__ == '__main__':
    # 創(chuàng)建一個web服務器實例對象
    hs = HTTPServer()
    # 調用實例方法
    hs.start()


web服務器-命令行參數控制端口

from gevent import monkey
monkey.patch_all()
import socket
import gevent
import sys


class HTTPServer(object):
    def __init__(self,port):
        """創(chuàng)建服務器相關的資源 """
        # 創(chuàng)建服務器套接字
        server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        # 為了防止服務器不能立馬重新使用相應的端口 設置套接字地址重用選項 1設置 0取消
        server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        # 綁定
        server_socket.bind(('', port))

        # 監(jiān)聽
        server_socket.listen(128)

        self.server_socket = server_socket

    def request_handler(self, client_socket):
        """專門用來處理客戶端請求的函數"""
        # 接收用戶請求
        recv_data = client_socket.recv(4096)
        if not recv_data:
            print("客戶端已經斷開連接")
            client_socket.close()
            return  # 如果客戶端已經斷開連接 則不需要再執(zhí)行后續(xù)代碼 直接結束函數即可
        # 解碼數據
        recv_str_data = recv_data.decode()

        # 切割請求數據 ----> 列表
        data_list = recv_str_data.split('\r\n')
        # print(data_list)

        # 列表 中的第0個元素就是請求行  GET /index.html HTTP/1.1
        request_line = data_list[0]
        # print(request_line)

        # 請求行中的 切割出來的列表中的第一個元素就是用戶的請求路徑
        path_info = request_line.split(" ")[1]
        # /index.html  /home/python/1.txt 直接使用系統(tǒng)的根目錄存放數據容易引起數據安全問題
        # 在用戶請求的路徑前 加上一個指定路徑  這樣當用戶訪問的時候就會訪問指定目錄下的數據 防止服務器其他數據被竊取
        # ./static  + /index.html
        print(path_info)

        # 當用戶只輸入域名(IP) + [端口]  用戶請求路徑是/
        if path_info == '/':
            path_info = '/index.html'

        try:
            # # 嘗試打開用戶需要的文件 如果文件不存在則拋出異常
            # file = open("./static" + path_info, "rb")
            # # 如果文件比較大 容易產生隱患
            #
            # # 讀出文件數據
            # file_data = file.read()
            #
            # file.close()

            # with語句 自動將對象的資源進行釋放  ----> 上下文管理器
            # 支持的數據資源 有文件 socket 互斥鎖等
            # static文件夾為你訪問的數據(自備),與本程序放在同一目錄下
            with open("./static" + path_info, "rb") as file:
                # 讀出文件數據
                file_data = file.read()

        except Exception as e:
            response_line = "HTTP/1.1 404 Not Found\r\n"
            response_header = "Server: PythonServer2.0\r\n"
            response_body = "ERROR"
            response_data = response_line + response_header + "\r\n" + response_body
            client_socket.send(response_data.encode())

        else:
            # 響應行 響應頭 \r\n 響應體
            # 響應行
            response_line = "HTTP/1.1 200 OK\r\n"
            # 響應頭
            response_header = "Server: PythonServer2.0\r\n"
            # 響應體 就是瀏覽器收到的文件數據
            response_body = file_data
            # 按照HTTP響應報文格式 進行拼接
            response_data = (response_line + response_header + "\r\n").encode() + response_body

            # 發(fā)送響應報文 send不一定能夠全部發(fā)送完成 sendall能夠保證全部發(fā)送完成
            # client_socket.send(response_data)
            client_socket.sendall(response_data)

        finally:
            # 斷開連接
            client_socket.close()

    def start(self):
        while True:
            # 取出一個客戶端套接字用以通信
            client_socket, client_addr = self.server_socket.accept()
            print("接受到來自%s的連接請求" % str(client_addr))
            # 為每個客戶端請求的執(zhí)行 都創(chuàng)建一個線程
            # request_handler(client_socket)

            # 創(chuàng)建并且執(zhí)行 協程
            gevent.spawn(self.request_handler, client_socket)


def main():
    # python3 web.py 端口號
    # sys.argv是一個列表  每個元素都是一個字符串
    if len(sys.argv) != 2:
        print("參數錯誤")
        return
    # print(sys.argv)
    port = sys.argv[1]
    if not port.isdigit():
        print("端口號應該是數字")
        return

    port_number = int(port)

    # 創(chuàng)建一個web服務器實例
    httpserver = HTTPServer(port_number)

    # 啟動web服務器 的運行
    httpserver.start()


if __name__ == '__main__':
    main()




?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 轉自: http://m.itdecent.cn/p/486b0965c296 http://www.jia...
    demop閱讀 4,136評論 1 21
  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現,斷路器,智...
    卡卡羅2017閱讀 136,724評論 19 139
  • 上一篇《聊聊同步、異步、阻塞與非阻塞》[http://m.itdecent.cn/p/aed6067eeac...
    七寸知架構閱讀 141,639評論 57 445
  • 本文摘抄自linux基礎編程 IO概念 Linux的內核將所有外部設備都可以看做一個文件來操作。那么我們對與外部設...
    VD2012閱讀 1,071評論 0 2
  • 本文摘抄自linux基礎編程 IO概念 Linux的內核將所有外部設備都可以看做一個文件來操作。那么我們對與外部設...
    lintong閱讀 1,690評論 0 4

友情鏈接更多精彩內容