一、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)
返回結果:
返回正常的字符串