「Python網絡編程」簡述HTTP協議/模擬實現百度頁面(五)

博主前言:

上次博客講述了迭代器和生成器的相關知識點,在實現多任務的能力方面已經很不錯了,接下來就是要勤加練習,掌握各種實現多任務的方式。

1. HTTP

HTTP(Hyper Text Transfer Protocol),全稱超文本傳輸協議。
HTTP是因特網上應用最爲廣泛的一種網絡傳輸協議,所有的WWW文件都必須遵守這個標準。

1.1 HTTP的特性

  1. HTTP是一個基於TCP/IP通信協議來傳遞數據(HTML文件, 圖片文件等),默認端口:80
  2. HTTP是無連接:無連接的含義是限制每次連接只處理一個請求。服務器處理完客戶的請求,並收到客戶的應答後,即斷開連接。
  3. HTTP是無狀態:HTTP協議是無狀態協議。無狀態是指協議對於事務處理沒有記憶能力。缺少狀態意味着如果後續處理需要前面的信息,則它必須重傳,這樣可能導致每次連接傳送的數據量增大。另一方面,在服務器不需要先前信息時它的應答就較快。
  4. HTTP的請求數據包括請求頭(request header)和請求體(request body)在這裏插入圖片描述
  5. HTTP的響應數據包括響應頭(response header)和響應體(response body)在這裏插入圖片描述

1.2 TCP的三次握手和四次揮手

因爲HTTP是建立在TCP/IP通信協議上的用來傳輸數據的協議。
所以HTTP是穩定的,可靠的協議。
在學習TCP協議時,我們說TCP是可靠的協議,UDP是不可靠的協議。
其原理是因爲TCP通信時會有一個三次握手四次揮手過程。

客戶端服務器請準備爲我通信(SYN)我準備好了(ACK=SYN+1),你呢(SYN)我也準備好了(ACK=SYN+1)客戶端服務器

以上過程就是TCP在數據傳輸之前會先進行的一種面向連接的過程。俗稱:三次握手。
首先,當客戶端套接字鏈接服務器時,客戶端會向服務器發送一個SYN碼,意思差不多就是:你好,我要來給你通信了,你準備好了嗎?
然後,這時,服務器收到客戶端發送的SYN碼,就會將SYN碼+1,回送給客戶端,也就是ACK碼,同時還要再發送一個新的SYN碼,代表說:我準備好了,你準備好發送了嗎?
最後,客戶端將服務器回送的新的SYN碼+1,再發送給服務器。表示:我也準備好了,開始通信吧。
哈哈,是不是很奇妙的過程。
因爲套接字是全雙工的,所以當客戶端和服務器之間數據通信完後,在斷開連接時,又會執行四次揮手的操作,確保客戶端和服務器的套接字發送和接收的功能都關閉。

客戶端服務器客戶端調用close(),關閉發送功能收到,此時我關閉接收功能我關閉發送功能我關閉接收功能客戶端服務器

想一想爲什麼服務器關閉接收功能和關閉發送功能不能同時回送給客戶端,要分爲兩次進行?

1.3 請求方法

根據 HTTP 標準,HTTP 請求可以使用多種請求方法。
HTTP1.0 定義了三種請求方法: GET, POSTHEAD方法。
HTTP1.1 新增了六種請求方法:OPTIONSPUTPATCHDELETETRACECONNECT方法。

序號 方法 描述
1 GET 請求指定的頁面信息,並返回實體主體
2 HEAD 類似於 GET 請求,只不過返回的響應中沒有具體的內容,用於獲取報頭
3 POST 向指定資源提交數據進行處理請求
4 PUT 從客戶端向服務器傳送的數據取代指定的文檔的內容
5 DELETE 請求服務器刪除指定的頁面
6 CONNECT HTTP/1.1 協議中預留給能夠將連接改爲管道方式的代理服務器
7 OPTIONS 允許客戶端查看服務器的性能
8 TRACE 回顯服務器收到的請求,主要用於測試或診斷
9 PATCH 是對 PUT 方法的補充,用來對已知資源進行局部更新

1.4 狀態碼

HTTP狀態碼由三個十進制數字組成,第一個十進制數字定義了狀態碼的類型,後兩個數字沒有分類的作用。HTTP狀態碼共分爲5種類型:

1** 信息,服務器收到請求,需要請求者繼續執行操作
2** 成功,操作被成功接收並處理
3** 重定向,需要進一步的操作以完成請求
4** 客戶端錯誤,請求包含語法錯誤或無法完成請求
5** 服務器錯誤,服務器在處理請求的過程中發生了錯誤

2. 模擬實現百度頁面

百度的HTML文件以及上傳至百度網盤,讀者可以點擊獲取。
百度網盤: 點擊獲取.

import socket
import re

def service_client(new_socket,client_addr):
    # 接收客戶端的請求頭
    request = new_socket.recv(1024).decode("gbk")
    request_str = str(request)
    # 使用正則表達式,提取要訪問的文件名字
    file_name = re.search(r"/[^ ]*",request_str)
    # 回送響應頭和數據
    try:
        f = open("./baidu"+file_name.group(),"rb")
    except:
        if file_name.group() == "/":
            f = open("./baidu/index.html","rb")
            response_content = f.read()
            f.close()
            response_header = b"HTTP /1.1 200 OK\r\n"
            response_header += b"\r\n"
            new_socket.send(response_header)
            new_socket.send(response_content)
        else:
            response_header = b"HTTP /1.1 404 NOT FOUND\r\n"
            response_header += b"\r\n"
            response_content = "<h1> --- FILE NOT FOUND --- </h1>"
            new_socket.send(response_header)
            new_socket.send(response_content.encode("gbk"))
    else:
        response_content = f.read()
        f.close()
        response_header = b"HTTP /1.1 200 OK\r\n"
        response_header += b"\r\n"
        new_socket.send(response_header)
        new_socket.send(response_content)

    new_socket.close()

def main():
    # 1. 創建套接字
    main_socket = socket.socket()
    # 2. 綁定
    main_socket.bind(("",5678))
    # 3. 讓套接字變成監聽套接字
    main_socket.listen(128)
    while True:
        # 4. 等待客戶端鏈接
        new_socket,client_addr = main_socket.accept()
        # 5. 爲客戶端服務
        service_client(new_socket,client_addr)
    # 關閉監聽套接字
    main_socket.close()

if __name__ == '__main__':
    main()

將百度的HTML文件包解壓到程序的當前路徑下,運行上述代碼就能實現模擬實現百度頁面。
運行上述代碼,當我們用瀏覽器訪問127.0.0.1:5678,就能成功顯示出百度的頁面了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章