[注記](ファイルディスクリプタのリアライズを含む)Pythonの高度な静的Webサーバ

ディレクトリ

1.httpプロトコル(理解)

1.1。HTTPリクエスト

1.2。HTTP形式

2. Webサーバーの静的

2.1 PYシンプルなHTTPサーバ

2.2。Pyの実装では、必要に応じてブラウザのページのhttpサーバを返します。

3.ウェブ静的サーバー(マルチタスキング)

3.1。マルチプロセスHTTPサーバを実装

3.2。マルチスレッドHTTPサーバー

3.3。Geventは、httpサーバ達成

3.4。非ブロックモード

    3.4.1単一のタスクを非ブロック

    3.4.2シングルタスク(長いリンク)の非ブロッキング

    3.4.3のepollリアライズのhttpサーバ


 

1.httpプロトコル(理解)

HTTP(ハイパーテキスト転送プロトコル)<ハイパーテキスト転送プロトコル>。

1.1。HTTPリクエスト

1)まず、ブラウザは、サーバにHTTPリクエストを送信する要求があっ

方法:GETまたはPOSTは、唯一の要求リソースをGET、POSTは、ユーザデータに含まれます。

パス:/フル/ URL /パス。

ドメイン名:ホスト:ホストヘッダーを指定www.sina.com

そして、他の関連するヘッダー。

POSTであれば、その要求は、ユーザデータを含む、本体と、

2)サーバは、ブラウザに対してHTTPレスポンスを返す応答があっ

応答コード:200は、クライアントエラーにより送信された要求を表すの4xxは、5xxのエラーは、サーバー・プロセス中に発生した、3XXリダイレクションが表す、成功を示します。

応答タイプ:コンテンツタイプで指定されました。

そして、他の関連するヘッダー。

通常、HTTPサーバの応答があるコンテンツを、運ぶことができる、ボディ、コンテンツ、体内のページのHTMLソースコードの応答があります。

ブラウザは写真など、サーバーに追加のリソースを要求し続けるために必要とされる場合3)、それは再びHTTP要求を行い、手順1と2を繰り返します。

応答パターン、大幅に開発を簡素化 - 非常に単純な要求に使用されるWeb HTTPプロトコル。私たちがページを書くとき、私たちは送出されたHTTPリクエストにHTMLを配置する必要があり、必要な要求の画像と動画は、それはそれゆえ、別のHTTPリクエストを送信する場合には、写真、ビデオ、および他のブラウザに付属する方法を検討する必要はありません、HTTP唯一のリソース要求を処理する(この時点では、このような複数のリンクを確立する必要性などのTCPプロトコルに短い接続、各リソースのための唯一のリンク、として理解することができる複数を必要とします)

 

1.2。HTTP形式

各HTTP要求と応答が同じ形式、ボディは任意である2つの部分を含むHTTPヘッダとボディをたどります。

HTTPプロトコルは、テキストプロトコルであるので、そのフォーマットは非常に単純です。

1)HTTP GETリクエストのフォーマット:

    GET /パスHTTP / 1.1
    ヘッダ1:値1
    ヘッダ2:値2
    Header3:VALUE3

2)HTTP POSTリクエストのフォーマット:

    POST /パスHTTP / 1.1
    ヘッダ1:値1
    ヘッダ2:値2
    Header3:VALUE3

    身体データをここに...

注:二つの連続\ R \ N(空行)、ヘッダ部分の端、後者データ身体の全てに直面したとき。

3)HTTPレスポンスのフォーマット:

    200 OK
    ヘッダ1:値1
    ヘッダ2:値2
    Header3:VALUE3

    身体データをここに...

注:体を含む場合は、だけでなく、\ rを\ nは\ rを\ nは分離することによってHTTPレスポンス。データのボディタイプは、Webページの場合、絵ならば、画像はバイナリデータの本体で、本文テキストで、Content-Typeのヘッドによって決定されます。

 

2. Webサーバーの静的

 

2.1 PYシンプルなHTTPサーバ

import socket


def service_client(new_socket):
    """为这个客户端返回数据"""

    # 1. 接收浏览器发送过来的请求 ,即http请求  
    # GET / HTTP/1.1
    # .....
    request = new_socket.recv(1024)
    print(request)

    # 2. 返回http格式的数据,给浏览器
    # 2.1 准备发送给浏览器的数据---header
    response = "HTTP/1.1 200 OK\r\n"
    response += "\r\n"
    # 2.2 准备发送给浏览器的数据---boy
    response += "hello world"
    new_socket.send(response.encode("utf-8"))  # python3发送的必须是字节类型

    # 关闭套接字
    new_socket.close()
    

def main():
    """用来完成整体的控制"""
    # 1. 创建套接字
    tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 设置当服务器先close 即服务器端4次挥手之后资源能够立即释放,这样就保证了,下次运行程序时 可以立即绑定7788端口
    tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

    # 2. 绑定
    tcp_server_socket.bind(("", 7890))

    # 3. 变为监听套接字
    tcp_server_socket.listen(128)

    while True:
        # 4. 等待新客户端的链接
        new_socket, client_addr = tcp_server_socket.accept()

        # 5. 为这个客户端服务
        service_client(new_socket)

    # 关闭监听套接字
    tcp_server_socket.close()


if __name__ == "__main__":
    main()

ブラウザの入力で実行した後  http://127.0.0.1:7890/  、サーバは、応答、ブラウザの表示ハローワールドを返します。

 

注:それは4倍(詳細なリファレンスエリアネットワークの知識)を振って、TCP 3ウェイハンドシェイクのプロセスを理解するのが最善です。

単に以下を参照してください。

近くを強調する一般的なクライアント。

 

 2.2。Pyの実装では、必要に応じてブラウザのページのhttpサーバを返します。

2.1では、HTTPサーバは固定文字列を返し、このセクションでは、ブラウザのページのhttpサーバへのリターンを達成するために必要。

 

例:最初の実装を返しますindex.htmlを

まず、クライアントへの復帰のために実行する前に、htmlファイルのフォルダのindex.htmlファイルを準備します。

import socket


def service_client(new_socket):
    """为这个客户端返回数据"""

    # 1. 接收浏览器发送过来的请求 ,即http请求  
    # GET / HTTP/1.1
    # .....
    request = new_socket.recv(1024)
    print(">>>"*50)
    print(request)

    # 2. 返回http格式的数据,给浏览器
    # 2.1 准备发送给浏览器的数据---header
    response = "HTTP/1.1 200 OK\r\n"
    response += "\r\n"
    # 2.2 准备发送给浏览器的数据---boy
    # response += "hahahhah"
    
    f = open("./html/index.html", "rb")
    html_content = f.read()
    f.close()

    # 因为是以二进制打开的文件,不能直接response+=html_content

    # 将response header发送给浏览器
    new_socket.send(response.encode("utf-8"))
    # 将response body发送给浏览器
    new_socket.send(html_content)

    # 关闭套接
    new_socket.close()
    

def main():
    """用来完成整体的控制"""
    # 1. 创建套接字
    tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

    # 2. 绑定
    tcp_server_socket.bind(("", 7890))

    # 3. 变为监听套接字
    tcp_server_socket.listen(128)

    while True:
        # 4. 等待新客户端的链接
        new_socket, client_addr = tcp_server_socket.accept()

        # 5. 为这个客户端服务
        service_client(new_socket)

    # 关闭监听套接字
    tcp_server_socket.close()


if __name__ == "__main__":
    main()

ブラウザの入力で実行後  http://127.0.0.1:7890/を  、すなわち、サーバは応答を返し、ブラウザはフロントエンドに対応たindex.htmlの内容を表示します。

 

改善するためにブラウザのページへの必要なリターンを達成するためにこの時、最初のクライアントに、送信された要求をデコードします。ユーザーによって要求されたページの抽出したファイル名をデコードした後。

import socket
import re


def service_client(new_socket):
    """为这个客户端返回数据"""

    # 1. 接收浏览器发送过来的请求 ,即http请求  
    # GET / HTTP/1.1
    # .....
    request = new_socket.recv(1024).decode("utf-8")  # decode进行解码
    # print(">>>"*50)
    # print(request)  # 会打印解码后的请求

    request_lines = request.splitlines()  # 对请求按行(请求的每行内容都有一个含义)分割,返回一个列表
    print("")
    print(">"*20)
    print(request_lines)

    # GET /index.html HTTP/1.1   (这是请求的第一行)
    # get post put del
    file_name = ""
    ret = re.match(r"[^/]+(/[^ ]*)", request_lines[0])  # 依据请求第一行解析文件名([^/]+匹配多个非/,[^ ]*匹配0或多个非空格)
    if ret:
        file_name = ret.group(1)
        # print("*"*50, file_name)
        if file_name == "/":
            file_name = "/index.html"  # 设置请求没指定文件名时,默认的请求页面

    # 2. 返回http格式的数据,给浏览器
    try:
        f = open("./html" + file_name, "rb")  # 上面匹配不到时,没有file_name会异常
    except:
        response = "HTTP/1.1 404 NOT FOUND\r\n"
        response += "\r\n"
        response += "------file not found-----"
        new_socket.send(response.encode("utf-8"))
    else:
        html_content = f.read()
        f.close()
        # 2.1 准备发送给浏览器的数据---header
        response = "HTTP/1.1 200 OK\r\n"
        response += "\r\n"
        # 2.2 准备发送给浏览器的数据---boy
        # response += "hahahhah"

        # 将response header发送给浏览器
        new_socket.send(response.encode("utf-8"))
        # 将response body发送给浏览器
        new_socket.send(html_content)


    # 关闭套接
    new_socket.close()
    

def main():
    """用来完成整体的控制"""
    # 1. 创建套接字
    tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

    # 2. 绑定
    tcp_server_socket.bind(("", 7890))

    # 3. 变为监听套接字
    tcp_server_socket.listen(128)

    while True:
        # 4. 等待新客户端的链接
        new_socket, client_addr = tcp_server_socket.accept()

        # 5. 为这个客户端服务
        service_client(new_socket)

    # 关闭监听套接字
    tcp_server_socket.close()


if __name__ == "__main__":
    main()

ブラウザの入力で実行した後http://127.0.0.1:7890/  指定されたファイル名(例:http://127.0.0.1:7890/のindex.html)、次に、サーバは、応答、ファイルが存在するブラウザ・ディスプレイを返します。対応するコンテンツ。 

 

3.ウェブ静的サーバー(マルチタスキング)

 

3.1。マルチプロセスHTTPサーバを実装

達成するための新しいプロセスとプロセスの実現にはまだある、プロセス、およびサービスを受け入れます。

(注:実行する前に、最初の準備リソース)

import socket
import re
import multiprocessing


def service_client(new_socket):
    """为这个客户端返回数据"""

    # 1. 接收浏览器发送过来的请求 ,即http请求  
    # GET / HTTP/1.1
    # .....
    request = new_socket.recv(1024).decode("utf-8")
    # print(">>>"*50)
    # print(request)

    request_lines = request.splitlines()
    print("")
    print(">"*20)
    print(request_lines)

    # GET /index.html HTTP/1.1
    # get post put del
    file_name = ""
    ret = re.match(r"[^/]+(/[^ ]*)", request_lines[0])
    if ret:
        file_name = ret.group(1)
        # print("*"*50, file_name)
        if file_name == "/":
            file_name = "/index.html"




    # 2. 返回http格式的数据,给浏览器
    
    try:
        f = open("./html" + file_name, "rb")
    except:
        response = "HTTP/1.1 404 NOT FOUND\r\n"
        response += "\r\n"
        response += "------file not found-----"
        new_socket.send(response.encode("utf-8"))
    else:
        html_content = f.read()
        f.close()
        # 2.1 准备发送给浏览器的数据---header
        response = "HTTP/1.1 200 OK\r\n"
        response += "\r\n"
        # 2.2 准备发送给浏览器的数据---boy
        # response += "hahahhah"

        # 将response header发送给浏览器
        new_socket.send(response.encode("utf-8"))
        # 将response body发送给浏览器
        new_socket.send(html_content)


    # 关闭套接
    new_socket.close()
    

def main():
    """用来完成整体的控制"""
    # 1. 创建套接字
    tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

    # 2. 绑定
    tcp_server_socket.bind(("", 7890))

    # 3. 变为监听套接字
    tcp_server_socket.listen(128)

    while True:
        # 4. 等待新客户端的链接
        new_socket, client_addr = tcp_server_socket.accept()

        # 5. 为这个客户端服务(服务过程用新的进程实现)
        p = multiprocessing.Process(target=service_client, args=(new_socket,))  # 进程执行service_client,参数new_socket
        p.start()

        new_socket.close()  # 通俗理解:由于创建了新的进程(复制了父进程的socket)


    # 关闭监听套接字
    tcp_server_socket.close()


if __name__ == "__main__":
    main()

3.2。マルチスレッドHTTPサーバー

スレッドへのコードとマルチプロセス、マルチ間の主な違い。子スレッドのソケットがメインスレッドをコピーしないことに注意してください、これだけnew_socket.close一回()することができます。

import socket
import re
import threading


def service_client(new_socket):
    """为这个客户端返回数据"""

    # 1. 接收浏览器发送过来的请求 ,即http请求  
    # GET / HTTP/1.1
    # .....
    request = new_socket.recv(1024).decode("utf-8")
    # print(">>>"*50)
    # print(request)

    request_lines = request.splitlines()
    print("")
    print(">"*20)
    print(request_lines)

    # GET /index.html HTTP/1.1
    # get post put del
    file_name = ""
    ret = re.match(r"[^/]+(/[^ ]*)", request_lines[0])
    if ret:
        file_name = ret.group(1)
        # print("*"*50, file_name)
        if file_name == "/":
            file_name = "/index.html"




    # 2. 返回http格式的数据,给浏览器
    
    try:
        f = open("./html" + file_name, "rb")
    except:
        response = "HTTP/1.1 404 NOT FOUND\r\n"
        response += "\r\n"
        response += "------file not found-----"
        new_socket.send(response.encode("utf-8"))
    else:
        html_content = f.read()
        f.close()
        # 2.1 准备发送给浏览器的数据---header
        response = "HTTP/1.1 200 OK\r\n"
        response += "\r\n"
        # 2.2 准备发送给浏览器的数据---boy
        # response += "hahahhah"

        # 将response header发送给浏览器
        new_socket.send(response.encode("utf-8"))
        # 将response body发送给浏览器
        new_socket.send(html_content)


    # 关闭套接
    new_socket.close()
    

def main():
    """用来完成整体的控制"""
    # 1. 创建套接字
    tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

    # 2. 绑定
    tcp_server_socket.bind(("", 7890))

    # 3. 变为监听套接字
    tcp_server_socket.listen(128)

    while True:
        # 4. 等待新客户端的链接
        new_socket, client_addr = tcp_server_socket.accept()

        # 5. 为这个客户端服务
        p = threading.Thread(target=service_client, args=(new_socket,))
        p.start()

        # new_socket.close()  # 线程不会复制主线程的socket,所以只用一次new_socket.close()即可。


    # 关闭监听套接字
    tcp_server_socket.close()


if __name__ == "__main__":
    main()

 

アップデート: マルチプロセスWebサーバのオブジェクト指向

import socket
import re
import multiprocessing


class WSGIServer(object):
    def __init__(self):
        # 1. 创建套接字
        self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        # 2. 绑定
        self.tcp_server_socket.bind(("", 7890))

        # 3. 变为监听套接字
        self.tcp_server_socket.listen(128)

    def service_client(self, new_socket):
        """为这个客户端返回数据"""

        # 1. 接收浏览器发送过来的请求 ,即http请求  
        # GET / HTTP/1.1
        # .....
        request = new_socket.recv(1024).decode("utf-8")
        # print(">>>"*50)
        # print(request)

        request_lines = request.splitlines()
        print("")
        print(">"*20)
        print(request_lines)

        # GET /index.html HTTP/1.1
        # get post put del
        file_name = ""
        ret = re.match(r"[^/]+(/[^ ]*)", request_lines[0])
        if ret:
            file_name = ret.group(1)
            # print("*"*50, file_name)
            if file_name == "/":
                file_name = "/index.html"


        # 2. 返回http格式的数据,给浏览器
        
        try:
            f = open("./html" + file_name, "rb")
        except:
            response = "HTTP/1.1 404 NOT FOUND\r\n"
            response += "\r\n"
            response += "------file not found-----"
            new_socket.send(response.encode("utf-8"))
        else:
            html_content = f.read()
            f.close()
            # 2.1 准备发送给浏览器的数据---header
            response = "HTTP/1.1 200 OK\r\n"
            response += "\r\n"
            # 2.2 准备发送给浏览器的数据---boy
            # response += "hahahhah"

            # 将response header发送给浏览器
            new_socket.send(response.encode("utf-8"))
            # 将response body发送给浏览器
            new_socket.send(html_content)


        # 关闭套接
        new_socket.close()
        

    def run_forever(self):
        """用来完成整体的控制"""

        while True:
            # 4. 等待新客户端的链接
            new_socket, client_addr = self.tcp_server_socket.accept()

            # 5. 为这个客户端服务
            p = multiprocessing.Process(target=self.service_client, args=(new_socket,))
            p.start()

            new_socket.close()


        # 关闭监听套接字
        self.tcp_server_socket.close()


def main():
    """控制整体,创建一个web 服务器对象,然后调用这个对象的run_forever方法运行"""
    wsgi_server = WSGIServer()
    wsgi_server.run_forever()


if __name__ == "__main__":
    main()

 

3.3。Geventは、httpサーバ達成

質問:この例の後にオブジェクトを作成gevent.spawnなぜ、オブジェクトが.join()またはgevent.joinall()スレッドの実行を呼び出す必要はありませんか

import socket
import re
import gevent
from gevent import monkey

monkey.patch_all()


def service_client(new_socket):
    """为这个客户端返回数据"""

    # 1. 接收浏览器发送过来的请求 ,即http请求  
    # GET / HTTP/1.1
    # .....
    request = new_socket.recv(1024).decode("utf-8")
    # print(">>>"*50)
    # print(request)

    request_lines = request.splitlines()
    print("")
    print(">"*20)
    print(request_lines)

    # GET /index.html HTTP/1.1
    # get post put del
    file_name = ""
    ret = re.match(r"[^/]+(/[^ ]*)", request_lines[0])
    if ret:
        file_name = ret.group(1)
        # print("*"*50, file_name)
        if file_name == "/":
            file_name = "/index.html"

    # 2. 返回http格式的数据,给浏览器
    try:
        f = open("./html" + file_name, "rb")
    except:
        response = "HTTP/1.1 404 NOT FOUND\r\n"
        response += "\r\n"
        response += "------file not found-----"
        new_socket.send(response.encode("utf-8"))
    else:
        html_content = f.read()
        f.close()
        # 2.1 准备发送给浏览器的数据---header
        response = "HTTP/1.1 200 OK\r\n"
        response += "\r\n"
        # 2.2 准备发送给浏览器的数据---boy
        # response += "hahahhah"

        # 将response header发送给浏览器
        new_socket.send(response.encode("utf-8"))
        # 将response body发送给浏览器
        new_socket.send(html_content)

    # 关闭套接
    new_socket.close()
    

def main():
    """用来完成整体的控制"""
    # 1. 创建套接字
    tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

    # 2. 绑定
    tcp_server_socket.bind(("", 7890))

    # 3. 变为监听套接字
    tcp_server_socket.listen(128)

    while True:
        # 4. 等待新客户端的链接
        new_socket, client_addr = tcp_server_socket.accept()

        # 5. 为这个客户端服务
        gevent.spawn(service_client, new_socket)  # 协程在service_client执行,参数new_socket

        # new_socket.close()

    # 关闭监听套接字
    tcp_server_socket.close()


if __name__ == "__main__":
    main()

 

3.4。非ブロックモード

Pythonでは、スリーブバイトに設定されてもよいモードブロッキングまたは非ブロッキングモード問題が発生した場合、このような送信など、APIを呼び出した後、非ブロックモードでは()やRECV()メソッドは例外をスローします。ブロックモードでは、人気がすべてのポーリング・モニターに関するエラーが発生し、操作を妨げるものではないと言うこと。

.setblockingオブジェクト(偽)は   、ソケットが設けられた方法で接続されています。

3.4.1単一のタスクを非ブロック

しばらく内のキーコードは、ソケットは、クライアントリストの到着を待っているソケット内のデータを待っていると、お互いに影響を与えます。

注:この小さな方法の実用化が、プロセスは、このように理解すべきであるという平行よりも同時です。

import socket
import time

tcp_server_tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # 创建套接字
tcp_server_tcp.bind(("", 7899))  # 绑定
tcp_server_tcp.listen(128)
tcp_server_tcp.setblocking(False)  # 设置套接字为非堵塞的方式

client_socket_list = list()  # 列表,有新的客户端链接时,将其客户端添加到列表中

while True:

    # time.sleep(0.5)  # 延时方便看效果

    try:  # 收不到值时产生异常
        new_socket, new_addr = tcp_server_tcp.accept()
    except Exception as ret:
        print("---没有新的客户端到来---")
    else:
        print("---只要没有产生异常,那么也就意味着 来了一个新的客户端----")  # 此时还不能调用recv,因为调用后又会堵塞
        new_socket.setblocking(False)  # 设置套接字为非堵塞的方式
        client_socket_list.append(new_socket)  # 有新的客户端连接,将其socket添加到列表
        
    for client_socket in client_socket_list:
        try:
            recv_data = client_socket.recv(1024)  # 检测列表的socket是否有数据
        except Exception as ret:
            print(ret)
            print("----这个客户端没有发送过来数据----")  # client没发送数据,产生异常
        else:
            print("-----没有异常-----")
            print(recv_data)
            if recv_data:
                # 对方发送过来数据
                print("----客户端发送过来了数据-----")
            else:
                # 对方调用close 导致了有数据,但recv返回空,recv_data为空
                client_socket.close()
                client_socket_list.remove(client_socket)
                print("---客户端已关闭----")

 

3.4.2シングルタスク(長いリンク)の非ブロッキング

人気のある、短い各取得リンクを確立するためのデータコネクション(スリーウェイハンドシェイクの接続を4回振っオフ)。手順:接続を確立する - データ転送 - 閉じる...接続を確立するために接続されている - データ転送 - クローズ接続を。

あなたは、サーバーのリソースを節約することができるように長いリンクリンクでは、ソケットを介して同じデータが、いくつかの必要性を得ることができます。手順:接続の確立 - データ転送...(滞在が接続されている)...データ転送 - クローズ接続を。

注:HTTP1.1のバージョンは長いリンクを使用しています。なお、HTTPプロトコルのバージョン1.1を用いた以前のコードが、()、または鉛短いリンクへのソケットオブジェクト.closeへの各呼び出しの後に手動で接続。

非ブロッキング単一タスク(ロングリンク)コード:ヘッダーに追加のContent-Length: どのくらいパッケージブラウザに伝えるために、長さのコンテンツを記録するために使用される、ブラウザなどの送信が終了するかどうかを決定します。あなたは同じ短いリンクで裁判官のは好きではないので、上を通過切断。

import socket
import re


def service_client(new_socket, request):
    """为这个客户端返回数据"""

    # 1. 接收浏览器发送过来的请求 ,即http请求  
    # GET / HTTP/1.1
    # .....
    # request = new_socket.recv(1024).decode("utf-8")
    # print(">>>"*50)
    # print(request)

    request_lines = request.splitlines()
    print("")
    print(">"*20)
    print(request_lines)

    # GET /index.html HTTP/1.1
    # get post put del
    file_name = ""
    ret = re.match(r"[^/]+(/[^ ]*)", request_lines[0])
    if ret:
        file_name = ret.group(1)
        # print("*"*50, file_name)
        if file_name == "/":
            file_name = "/index.html"

    # 2. 返回http格式的数据,给浏览器
    
    try:
        f = open("./html" + file_name, "rb")
    except:
        response = "HTTP/1.1 404 NOT FOUND\r\n"
        response += "\r\n"
        response += "------file not found-----"
        new_socket.send(response.encode("utf-8"))
    else:
        html_content = f.read()
        f.close()

        response_body = html_content

        response_header = "HTTP/1.1 200 OK\r\n"
        response_header += "Content-Length:%d\r\n" % len(response_body)  # 记下内容的长度,告诉浏览器包有多长
        response_header += "\r\n"

        response = response_header.encode("utf-8") + response_body  # 读到的response_body是二进制,response_header是字符串

        new_socket.send(response)  # 发送二进制的数据
   
    # # 关闭监听套接字
    # new_socket.close()


def main():
    """用来完成整体的控制"""
    # 1. 创建套接字
    tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

    # 2. 绑定
    tcp_server_socket.bind(("", 7890))

    # 3. 变为监听套接字
    tcp_server_socket.listen(128)
    tcp_server_socket.setblocking(False)  # 将套接字变为非堵塞

    client_socket_list = list()
    while True:
        # 4. 等待新客户端的链接
        try:
            new_socket, client_addr = tcp_server_socket.accept()
        except Exception as ret:
            pass
        else:  # 没有异常
            new_socket.setblocking(False)
            client_socket_list.append(new_socket)

        for client_socket in client_socket_list:
            try:
                recv_data = client_socket.recv(1024).decode("utf-8")
            except Exception as ret:
                pass
            else:
                if recv_data:  # 有数据
                    service_client(client_socket, recv_data)
                else:  # 空数据,即浏览器断开连接
                    client_socket.close()
                    client_socket_list.remove(client_socket)

    # 关闭监听套接字
    tcp_server_socket.close()


if __name__ == "__main__":
    main()

 

3.4.3のepollリアライズのhttpサーバ

Linuxサーバーは、通常、ファイルディスクリプタ使用して実装されている(注:ファイルディスクリプタは、シングルプロセスは、シングルスレッドである)、高い並列性を実現することができます。

:詳細のepollはを参照して説明のepoll [学習] Linuxの  。

/(IO駆動とも呼ばれるイベント)を多重化するIOのファイルディスクリプタの使用を選択し、ソケットのデータが到着したときに、ユーザーがプロセスに通知されます。単一IOの利点は、複数のネットワーク接続を処理同時に処理することができます。

同時に読み取るための状態がレディに入る任意のそれらのファイル記述子を待っている複数の、これらのファイル記述子(ソケット記述子)を処理するための機構により、ファイルディスクリプタ()関数が返すことができる:基準特徴は次の通りです。\のepollを選択すると、そのソケットに来ているので仕事にいつでもまたは唯一のプロセスまたはスレッドが、効率を改善することができたので、だから、IO多重化、監視」、自然界には、同時機能は存在しません「任意のソケットがすぐに処理読み書き可能なデータを持っている、と\ファイルディスクリプタを選択する手が同時にソケットの多くを検出した場合、処理プロセスにすぐに復帰する動きがあることがあれば、リスト、ソケット上でより良い1よりも、高を処理するためのブロック待ち効率。

 

コード:ファイルディスクリプタのリアライズHTTP(実装プロセスを理解します):

  1. インポート:インポートセレクト
  2. オブジェクトを作成するのepoll:EPL = select.epoll()
  3. epollに登録されたリスニングソケット対応するFD(ファイルDiscriptorファイルディスクリプタ):epl.register(tcp_server_socket.fileno()、select.EPOLLIN)  :パラメータ2:select.EPOLLIN | select.EPOLLET、元ある入力があるか否かを検出ETモード
  4. fd_event_list = epl.poll()#epl.poll( )デフォルトでは、この時間は解除詰まりをします、イベント通知メソッドを介してプログラムを教えに来てデータを監視し、OSまでブロックします。FDとイベントからなるリストを返します。(一覧形式:[(FD、イベント)、(最終的にはソケットファイル記述子、ファイル記述子を対応する、これは)あなたがなど受信、などのrecvイベントを呼び出すことができるものです...]

  5. データ処理......

  6. ログアウト:epl.unregister(FD)。

注:3の上に、ファイルディスクリプタファイルディスクリプタは、2つの動作モードがあるLT(トリガ・レベル)ET(エッジ・トリガを)LTモードがデフォルトモードで次のように、LTおよびETモードモードとの違いは以下のとおりです。

  • LTモード:ファイルディスクリプタ記述子はイベントとイベント通知アプリケーションを検出した場合、アプリケーションはすぐにイベントを処理しない場合があります。ファイルディスクリプタの次の呼び出しは、再度アプリケーションに対応し、このイベントに気づくでしょう。
  • ETモード:ファイルディスクリプタ記述子はイベントとイベント通知アプリケーションを検出した場合、アプリケーションはすぐにイベントを処理する必要があります。未処理の場合は、次回は再度アプリケーションへのepoll、なりません応答を呼び出し、このイベントに気づきます。

EPOLLIN(読み取り)

EPOLLOUT(書き込み可能)

EPOLLET(ETモード)

import socket
import re
import select


def service_client(new_socket, request):
    """为这个客户端返回数据"""

    # 1. 接收浏览器发送过来的请求 ,即http请求  
    # GET / HTTP/1.1
    # .....
    # request = new_socket.recv(1024).decode("utf-8")
    # print(">>>"*50)
    # print(request)

    request_lines = request.splitlines()
    print("")
    print(">"*20)
    print(request_lines)

    # GET /index.html HTTP/1.1
    # get post put del
    file_name = ""
    ret = re.match(r"[^/]+(/[^ ]*)", request_lines[0])
    if ret:
        file_name = ret.group(1)
        # print("*"*50, file_name)
        if file_name == "/":
            file_name = "/index.html"

    # 2. 返回http格式的数据,给浏览器
    
    try:
        f = open("./html" + file_name, "rb")
    except:
        response = "HTTP/1.1 404 NOT FOUND\r\n"
        response += "\r\n"
        response += "------file not found-----"
        new_socket.send(response.encode("utf-8"))
    else:
        html_content = f.read()
        f.close()

        response_body = html_content

        response_header = "HTTP/1.1 200 OK\r\n"
        response_header += "Content-Length:%d\r\n" % len(response_body)
        response_header += "\r\n"

        response = response_header.encode("utf-8") + response_body

        new_socket.send(response)


def main():
    """用来完成整体的控制"""
    # 1. 创建套接字
    tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

    # 2. 绑定
    tcp_server_socket.bind(("", 7890))

    # 3. 变为监听套接字
    tcp_server_socket.listen(128)
    tcp_server_socket.setblocking(False)  # 将套接字变为非堵塞

    # 创建一个epoll对象!!!
    epl = select.epoll()

    # 将监听套接字对应的fd注册到epoll中
    epl.register(tcp_server_socket.fileno(), select.EPOLLIN)

    fd_event_dict = dict()  # 定义字典用来存放fd对应的套接字

    while True:

        fd_event_list = epl.poll()  # 默认会堵塞,直到 os监测到数据到来 通过事件通知方式 告诉这个程序,此时才会解堵塞。返回event和fd组成的列表

        # [(fd, event), (套接字对应的文件描述符, 这个文件描述符到底是什么事件 例如 可以调用recv接收等)]
        for fd, event in fd_event_list:
            # 等待新客户端的链接
            if fd == tcp_server_socket.fileno():  # 接收到新的客户端链接。产生新的套接字。将其注册到epoll中
                new_socket, client_addr = tcp_server_socket.accept()
                epl.register(new_socket.fileno(), select.EPOLLIN)
                fd_event_dict[new_socket.fileno()] = new_socket  # 存放fd对应的套接字
            elif event == select.EPOLLIN:  # 事件类型为EPOLLIN,有数据
                # 判断已经链接的客户端是否有数据发送过来
                recv_data = fd_event_dict[fd].recv(1024).decode("utf-8")  # fd_event_dict[fd]根据套接字取socket
                if recv_data:
                    service_client(fd_event_dict[fd], recv_data)
                else:  # 收到空数据
                    fd_event_dict[fd].close()
                    epl.unregister(fd)
                    del fd_event_dict[fd]  # 删除字典对应的ky


    # 关闭监听套接字
    tcp_server_socket.close()


if __name__ == "__main__":
    main()

 

 

公開された50元の記事 ウォン称賛10 ビュー6615

おすすめ

転載: blog.csdn.net/qq_23996069/article/details/104076227