socket 模擬 HTTP請求


一、socket介紹

網絡由下往上分為物理層、數據鏈路層、網絡層、傳輸層、會話層、表示層和應用層。IP協議對應於網絡層,TCP協議對應於傳輸層,而HTTP協議對應於應用層。socket則是對TCP/IP協議的封裝和應用。也可以說,TPC/IP協議是傳輸層協議,主要解決數據如何在網絡中傳輸,而HTTP是應用層協議,主要解決如何包裝數據。

關於TCP/IP和HTTP協議的關系:

我們在傳輸數據時,可以只使用(傳輸層)TCP/IP協議,但是那樣的話,如果沒有應用層,便無法識別數據內容,如果想要使傳輸的數據有意義,則必須使用到應用層協議,應用層協議有很多,比如HTTP、FTP、TELNET等,也可以自己定義應用層協議。WEB使用HTTP協議作應用層協議,以封裝HTTP文本信息,然后使用TCP/IP做傳輸層協議將它發到網絡上。

而socket是對TCP/IP協議的封裝,Socket本身並不是協議,而是一個調用接口(API),通過Socket,我們才能使用TCP/IP協議。實際上,Socket跟TCP/IP協議沒有必然的聯系。Socket編程接口在設計的時候,就希望也能適應其他的網絡協議。所以說,Socket的出現只是使得程序員更方便地使用TCP/IP協議棧而已,是對TCP/IP協議的抽象,從而形成了我們知道的一些最基本的函數接口,比如create、listen、connect、accept、send、read和write等等。

socket和TCP/IP協議關系:

TCP/IP只是一個協議棧,就像操作系統的運行機制一樣,必須要具體實現,同時還要提供對外的操作接口。這個就像操作系統會提供標准的編程接口,比如win32編程接口一樣,TCP/IP也要提供可供程序員做網絡開發所用的接口,這就是Socket編程接口。

HTTP和socket關系:

HTTP是轎車,提供了封裝或者顯示數據的具體形式;Socket是發動機,提供了網絡通信的能力。傳輸層的TCP是基於網絡層的IP協議的,而應用層的HTTP協議又是基於傳輸層的TCP協議的,而Socket本身不算是協議,就像上面所說,它只是提供了一個針對TCP或者UDP編程的接口。

二、socket建立網絡鏈接步驟

建立Socket連接至少需要一對套接字,其中一個運行於客戶端,稱為ClientSocket ,另一個運行於服務器端,稱為ServerSocket 。
套接字之間的連接過程分為三個步驟:服務器監聽,客戶端請求,連接確認。
1,服務器監聽:服務器端套接字並不定位具體的客戶端套接字,而是處於等待連接的狀態,實時監控網絡狀態,等待客戶端的連接請求。
2,客戶端請求:指客戶端的套接字提出連接請求,要連接的目標是服務器端的套接字。為此,客戶端的套接字必須首先描述它要連接的服務器的套接字,指出服務器端套接字的地址和端口號,然后就向服務器端套接字提出連接請求。
3,連接確認:當服務器端套接字監聽到或者說接收到客戶端套接字的連接請求時,就響應客戶端套接字的請求,建立一個新的線程,把服務器端套接字的描述發給客戶端,一旦客戶端確認了此描述,雙方就正式建立連接。而服務器端套接字繼續處於監聽狀態,繼續接收其他客戶端套接字的連接請求。

具體步驟:

  • 服務器根據地址類型(ipv4,ipv6)、socket類型、協議創建socket
  • 服務器為socket綁定ip地址和端口號
  • 服務器socket監聽端口號請求,隨時准備接收客戶端發來的連接,這時候服務器的socket並沒有被打開
  • 客戶端創建socket
  • 客戶端打開socket,根據服務器ip地址和端口號試圖連接服務器socket
  • 服務器socket接收到客戶端socket請求,被動打開,開始接收客戶端請求,直到客戶端返回連接信息。這時候socket進入阻塞狀態,所謂阻塞即accept()方法一直到客戶端返回連接信息后才返回,開始接收下一個客戶端諒解請求
  • 客戶端連接成功,向服務器發送連接狀態信息
  • 服務器accept方法返回,連接成功
  • 客戶端向socket寫入信息
  • 服務器讀取信息
  • 客戶端關閉
  • 服務器端關閉

三、socket 模擬 HTTP請求

import socket
from urllib.parse import urlparse


class ParserUrl(object):
    '''
    對url進行解析,並返回域名和路徑
    '''

    def __init__(self, url):
        self.url = url

    def get_host_path(self):
        parser_url = urlparse(self.url)
        host, path = parser_url.netloc, parser_url.path
        if path:
            return host, path
        else:
            return host, '/'


class SocketHttp(object):
    '''
    需要一個ParserUrl對象,獲取host和path
    建立連接返回http response字符串
    http數據傳遞的時是以字節為單位的,所以需要編碼
    '''

    def __init__(self, parser_url):
        self.host, self.path = parser_url.get_host_path()


    def set_socket(self):
        self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.client.connect((self.host, 80))
        self.client.send("GET {} HTTP/1.1\r\nHost:{}\r\nConnection:close\r\n\r\n".format(self.path, self.host).encode("utf8"))

    @property
    def data(self):
        self.set_socket()
        data = b""
        while True:
            d = self.client.recv(1024)
            if d:
                data += d
            else:
                break
        self.close_socket()
        return data.decode("utf8")

    @property
    def html_content(self):
        html_data = self.data
        return html_data.split("\r\n\r\n")[1]

    def close_socket(self):
        self.client.close()


if __name__ == "__main__":
    url = ParserUrl('https://www.baidu.com/')
    socket_http = SocketHttp(url)
    print(socket_http.html_content)

返回結果

返回正常的字符串


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM