1. 程式人生 > >http協議、web伺服器、併發伺服器(上)

http協議、web伺服器、併發伺服器(上)

目錄

1. HTTP格式

每個HTTP請求和響應都遵循相同的格式,一個HTTP包含Header和Body兩部分,其中Body是可選的。HTTP協議是一種文字協議,所以,它的格式也非常簡單。

1.1 HTTP GET請求的格式:

GET /path HTTP/1.1
Header1: Value1
Header2: Value2
Header3: Value3

1.2 HTTP POST請求的格式:

POST /path HTTP/1.1
Header1: Value1
Header2: Value2
Header3: Value3

body data goes here...

當遇到連續兩個\r\n時,Header部分結束,後面的資料全部是Body。

1.3 HTTP響應的格式:

HTTP/1.1 200 OK
Header1: Value1
Header2: Value2
Header3: Value3

body data goes here...

HTTP響應如果包含body,也是通過\r\n\r\n來分隔的。

請再次注意,Body的資料型別由Content-Type頭來確定,如果是網頁,Body就是文字,如果是圖片,Body就是圖片的二進位制資料。

當存在Content-Encoding時,Body資料是被壓縮的,最常見的壓縮方式是gzip,所以,看到Content-Encoding: gzip時,需要將Body資料先解壓縮,才能得到真正的資料。壓縮的目的在於減少Body的大小,加快網路傳輸。

2. Web靜態伺服器-顯示固定的頁面

import socket


def handle_client(new_client):
        """處理客戶端請求"""

        recv_data = new_client.recv(1024)

        print(recv_data)
    
        # 組裝響應的內容
        response_headers = "HTTP/1.1 200 OK\r\n"
        response_headers += "\r\n"
        response_body = "6666"
    
        response = response_headers + response_body
        new_client.send(response.encode("utf-8"))

        new_client.close()


def main():
    
        # 建立套接字
        tcp_socket_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
        # 繫結本地訊息
        tcp_socket_server.bind(("", 8090))

        # 套接字由主動變被動
        tcp_socket_server.listen(128)
    
        while True:
    
                # 接收新的請求
                new_client, client_addr = tcp_socket_server.accept()

                handle_client(new_client)

        # 關閉套接字
        tcp_socket_server.close()


if __name__ == "__main__":

        main()

如上的程式碼僅僅只是向瀏覽器傳送了簡單的文字內容:6666

在瀏覽器中訪問:

3. Web靜態伺服器-顯示需要的頁面

import socket
import re


def handle_client(new_client):
    """處理客戶端請求"""

    recv_data = new_client.recv(1024).decode("utf-8")
    
    # GET / HTTP/1.1
    request_header_lines = recv_data.splitlines()
    for line in request_header_lines:
        print(line)
    http_request_line = request_header_lines[0];
    get_file_name = re.match("[^/]+(/[^ ]*)", http_request_line).group(1)
    print("file name is -> " + get_file_name)   

    # print(recv_data)
    if get_file_name == "/":
        get_file_name = "/index.html"   

    root_path = "./html"
    get_file_name = root_path + get_file_name;
    try:
        f = open(get_file_name, "rb")
    except:
        response_headers = "HTTP/1.1 404 NOT FOUND\r\n"
        response_headers += "\r\n"
        response_body = b"page not found"
    else:   
    
        # 組裝響應的內容
        response_headers = "HTTP/1.1 200 OK\r\n"
        response_headers += "\r\n"
        response_body = f.read()
        f.close()
    
    new_client.send(response_headers.encode("utf-8"))
    new_client.send(response_body)

    new_client.close()


def main():
    
    # 建立套接字
    tcp_socket_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    tcp_socket_server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 
    
    # 繫結本地訊息
    tcp_socket_server.bind(("", 8090))

    # 套接字由主動變被動
    tcp_socket_server.listen(128)
    
    while True:
        
        # 接收新的請求
        new_client, client_addr = tcp_socket_server.accept()

        handle_client(new_client)

    # 關閉套接字
    tcp_socket_server.close()


if __name__ == "__main__":

    main()

在瀏覽器中訪問:

4. Web靜態伺服器-多程序版

import socket
import re
import multiprocessing


def handle_client(new_client):
    """處理客戶端請求"""

    recv_data = new_client.recv(1024).decode("utf-8")
    
    # GET / HTTP/1.1
    request_header_lines = recv_data.splitlines()
    for line in request_header_lines:
        print(line)
    http_request_line = request_header_lines[0];
    get_file_name = re.match("[^/]+(/[^ ]*)", http_request_line).group(1)
    print("file name is -> " + get_file_name)   

    # print(recv_data)
    if get_file_name == "/":
        get_file_name = "/index.html"   

    root_path = "./html"
    get_file_name = root_path + get_file_name;
    try:
        f = open(get_file_name, "rb")
    except:
        response_headers = "HTTP/1.1 404 NOT FOUND\r\n"
        response_headers += "\r\n"
        response_body = b"page not found"
    else:   
    
        # 組裝響應的內容
        response_headers = "HTTP/1.1 200 OK\r\n"
        response_headers += "\r\n"
        response_body = f.read()
        f.close()
    
    new_client.send(response_headers.encode("utf-8"))
    new_client.send(response_body)

    new_client.close()


def main():
    
    # 建立套接字
    tcp_socket_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    tcp_socket_server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 
    
    # 繫結本地訊息
    tcp_socket_server.bind(("", 8090))

    # 套接字由主動變被動
    tcp_socket_server.listen(128)
    
    while True:
        
        # 接收新的請求
        new_client, client_addr = tcp_socket_server.accept()
        
        p = multiprocessing.Process(target=handle_client, args=(new_client,))
        p.start()
        
        new_client.close()
        
    # 關閉套接字
    tcp_socket_server.close()


if __name__ == "__main__":

    main()

5. Web靜態伺服器-多執行緒版

import socket
import re
import threading


def handle_client(new_client):
    """處理客戶端請求"""

    recv_data = new_client.recv(1024).decode("utf-8")
    
    # GET / HTTP/1.1
    request_header_lines = recv_data.splitlines()
    for line in request_header_lines:
        print(line)
    http_request_line = request_header_lines[0];
    get_file_name = re.match("[^/]+(/[^ ]*)", http_request_line).group(1)
    print("file name is -> " + get_file_name)   

    # print(recv_data)
    if get_file_name == "/":
        get_file_name = "/index.html"   

    root_path = "./html"
    get_file_name = root_path + get_file_name;
    try:
        f = open(get_file_name, "rb")
    except:
        response_headers = "HTTP/1.1 404 NOT FOUND\r\n"
        response_headers += "\r\n"
        response_body = b"page not found"
    else:   
    
        # 組裝響應的內容
        response_headers = "HTTP/1.1 200 OK\r\n"
        response_headers += "\r\n"
        response_body = f.read()
        f.close()
    
    new_client.send(response_headers.encode("utf-8"))
    new_client.send(response_body)

    new_client.close()


def main():
    
    # 建立套接字
    tcp_socket_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    tcp_socket_server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 
    
    # 繫結本地訊息
    tcp_socket_server.bind(("", 8090))

    # 套接字由主動變被動
    tcp_socket_server.listen(128)
    
    while True:
        
        # 接收新的請求
        new_client, client_addr = tcp_socket_server.accept()
        
        t = threading.Thread(target=handle_client, args=(new_client,))
        t.start()
        
    # 關閉套接字
    tcp_socket_server.close()


if __name__ == "__main__":

    main()

6. Web靜態伺服器-gevent版

import socket
import re
import gevent
from gevent import monkey


monkey.patch_all()


def handle_client(new_client):
    """處理客戶端請求"""

    recv_data = new_client.recv(1024).decode("utf-8")
    
    # GET / HTTP/1.1
    request_header_lines = recv_data.splitlines()
    for line in request_header_lines:
        print(line)
    http_request_line = request_header_lines[0];
    get_file_name = re.match("[^/]+(/[^ ]*)", http_request_line).group(1)
    print("file name is -> " + get_file_name)   

    # print(recv_data)
    if get_file_name == "/":
        get_file_name = "/index.html"   

    root_path = "./html"
    get_file_name = root_path + get_file_name;
    try:
        f = open(get_file_name, "rb")
    except:
        response_headers = "HTTP/1.1 404 NOT FOUND\r\n"
        response_headers += "\r\n"
        response_body = b"page not found"
    else:   
    
        # 組裝響應的內容
        response_headers = "HTTP/1.1 200 OK\r\n"
        response_headers += "\r\n"
        response_body = f.read()
        f.close()
    
    new_client.send(response_headers.encode("utf-8"))
    new_client.send(response_body)

    new_client.close()


def main():
    
    # 建立套接字
    tcp_socket_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    tcp_socket_server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 
    
    # 繫結本地訊息
    tcp_socket_server.bind(("", 8090))

    # 套接字由主動變被動
    tcp_socket_server.listen(128)
    
    while True:
        
        # 接收新的請求
        new_client, client_addr = tcp_socket_server.accept()
        
        g = gevent.spawn(handle_client, new_client)
        g.join()
        
    # 關閉套接字
    tcp_socket_server.close()


if __name__ == "__main__":

    main()