作者主要是為了用python構建實時網絡通信程序。
概念性的東西越簡單越好理解,因此,下面我從晚上摘抄的概念 + 我的理解。
什么是網絡通信?
更確切地說,網絡通信是兩臺計算機上的兩個進程之間的通信。比如,瀏覽器進程和新浪服務器上的某個Web服務進程在通信,而QQ進程是和騰訊的某個服務器上的某個進程在通信。
而網絡編程就是針對網絡通信的編程。
1.網絡編程基礎
1.1 基本概念與協議
TCP/IP協議棧
網絡編程的基礎是 理解數據在網絡中的傳輸過程,這通常通過OSI模型和TCP/IP協議棧進行解釋。而TCP/IP協議棧則是實際應用中更為廣泛的模型,包含物理層、數據鏈路層、網絡層、傳輸層、會話層、表示層和應用層。
計算機為了聯網,需要規定通信協議,一開始是不統一的,相互不同協議之間的計算機交互是困難的。因此,就出現了互聯網協議簇,任何網絡只要遵守這個協議,就可以連入互聯網。
互聯網協議,包括上百種協議,最重要的當屬TCP/IP協議,因此,大家把互聯網協議簡稱TCP/IP協議。
通信的時候,雙方必須知道對方的標識,好比發郵件必須知道對方的郵件地址。互聯網上每個計算機的唯一標識就是IP地址,類似123.123.123.123
。如果一臺計算機同時接入到兩個或更多的網絡,比如路由器,它就會有兩個或多個IP地址,所以,IP地址對應的實際上是計算機的網絡接口,通常是網卡。
IP協議負責把數據從一臺計算機通過網絡發送到另一臺計算機。數據被分割成一小塊一小塊,然后通過IP包發送出去。由于互聯網鏈路復雜,兩臺計算機之間經常有多條線路,因此,路由器就負責決定如何把一個IP包轉發出去。IP包的特點是按塊發送,途徑多個路由,但不保證能到達,也不保證順序到達。
TCP協議則是建立在IP協議之上的。TCP協議負責在兩臺計算機之間建立可靠連接,保證數據包按順序到達。TCP協議會通過握手建立連接,然后,對每個IP包編號,確保對方按順序收到,如果包丟掉了,就自動重發。
許多常用的更高級的協議都是建立在TCP協議基礎上的,比如用于瀏覽器的HTTP協議、發送郵件的SMTP協議等。
一個TCP報文除了包含要傳輸的數據外,還包含源IP地址和目標IP地址,源端口和目標端口。
端口有什么作用?在兩臺計算機通信時,只發IP地址是不夠的,因為同一臺計算機上跑著多個網絡程序。一個TCP報文來了之后,到底是交給瀏覽器還是QQ,就需要端口號來區分。每個網絡程序都向操作系統申請唯一的端口號,這樣,兩個進程在兩臺計算機之間建立網絡連接就需要各自的IP地址和各自的端口號。
一個進程也可能同時與多個計算機建立鏈接,因此它會申請很多端口。
TCP
TCP (Transmission Control Protocol) 是面向連接的、提供端到端可靠的數據流(flow of data)。TCP 提供超時重發,丟棄重復數據,檢驗數據,流量控制等功能,保證數據能從一端傳到另一端。
“面向連接”就是在正式通信前必須要與對方建立起連接。這一過程與打電話很相似,先撥號振鈴,等待對方摘機說“喂”,然后才說明是誰。
端到端的數據流,建立在IP協議之上,提供超時重發、重復數據校驗、流量控制燈基本功能。
保證數據能從一端到另一端。
三次握手
TCP 是基于連接的協議,也就是說,在正式收發數據前,必須和對方建立可靠的連接。一個 TCP 連接必須要經過三次“握手”才能建立起來,簡單的講就是:
- 主機 A 向主機 B 發出連接請求數據包:“我想給你發數據,可以嗎?”;
- 主機 B 向主機 A 發送同意連接和要求同步(同步就是兩臺主機一個在發送,一個在接收,協調工作)的數據包:“可以,你來吧”;
- 主機 A 再發出一個數據包確認主機 B 的要求同步:“好的,我來也,你接著吧!”
三次“握手”的目的是使數據包的發送和接收同步,經過三次“對話”之后,主機 A 才向主機 B 正式發送數據。
A和B要通過 TCP通信的流程:
A 連接B,B被連接允許A連接,A接受到響應確認連接B。 -> A和B可以通信了。
UDP
UDP (User Datagram Protocol) 面向無連接的,主機發送獨立的數據報(datagram)給其他主機,不保證數據到達。由于 UDP 在傳輸數據報前不用在客戶和服務器之間建立一個連接,且沒有超時重發等機制,故而傳輸速度很快。
而無連接是一開始就發送信息(嚴格說來,這是沒有開始、結束的),只是一次性的傳遞,是先不需要接受方的響應,因而在一定程度上也無法保證信息傳遞的可靠性了,就像寫信一樣,我們只是將信寄出去,卻不能保證收信人一定可以收到。
通信時,可以不用與目標主機建立連接,就能發送數據。
A和B通信,直接發送就可以,不用管對方是否? "可連接"。
TCP 是面向連接的,有比較高的可靠性, 一些要求比較高的服務一般使用這個協議,如FTP、Telnet、SMTP、HTTP、POP3 等,而 UDP 是面向無連接的,使用這個協議的常見服務有 DNS、SNMP、QQ 等。對于 QQ 必須另外說明一下,QQ2003 以前是只使用UDP協議的,其服務器使用8000端口,偵聽是否有信息傳來,客戶端使用 4000 端口,向外發送信息(這也就不難理解在一般的顯IP的QQ版本中顯示好友的IP地址信息中端口常為4000或其后續端口的原因了),即QQ程序既接受服務又提供服務,在以后的 QQ 版本中也支持使用 TCP 協議了。
端口
一般來說,一臺計算機具有單個物理連接到網絡。數據通過這個連接去往特定的計算機。然而,該數據可以被用于在計算機上運行的不同應用。那么,計算機知道哪個應用程序轉發數據?通過使用端口。
在互聯網上傳輸的數據是通過計算機的標識和端口來定位的。計算機的標識是 32-bit 的 IP 地址。端口由一個 16-bit 的數字。
在諸如面向連接的通信如 TCP,服務器應用將套接字綁定到一個特定端口號。這是向系統注冊服務用來接受該端口的數據。然后,客戶端可以在與服務器在服務器端口會合,如下圖所示:
TCP 和 UDP 協議使用的端口來將接收到的數據映射到一個計算機上運行的進程。
在基于數據報的通信,如 UDP,數據報包中包含它的目的地的端口號,然后 UDP 將數據包路由到相應的應用程序,如本圖所示的端口號:
端口號取值范圍是從 0 到 65535 (16-bit 長度),其中范圍從 0 到 1023 是受限的,它們是被知名的服務所保留使用,例如 HTTP (端口是 80)和 FTP (端口是20、21)等系統服務。這些端口被稱為眾所周知的端口(well-known ports)。您的應用程序不應該試圖綁定到他們。
嗯 ...,網絡通信具體來講,是兩個進程之間的通信,很顯然我們只要目標主機的IP是沒法跟對方通信的,因此我們還必須知道目標進程運行占用的端口,通過 ip + port來與其通信。
2. Socket
Socket(套接字):是在網絡上運行兩個程序之間的雙向通信鏈路的一個端點。socket綁定到一個端口號,使得 TCP 層可以標識數據最終要被發送到哪個應用程序。
正常情況下,一臺服務器在特定計算機上運行,??并具有被綁定到特定端口號的 socket。服務器只是等待,并監聽用于客戶發起的連接請求的 socket 。
在客戶端:客戶端知道服務器所運行的主機名稱以及服務器正在偵聽的端口號。建立連接請求時,客戶端嘗試與主機服務器和端口會合。客戶端也需要在連接中將自己綁定到本地端口以便于給服務器做識別。本地端口號通常是由系統分配的。
如果一切順利的話,服務器接受連接。一旦接受,服務器獲取綁定到相同的本地端口的新 socket ,并且還具有其遠程端點設定為客戶端的地址和端口。它需要一個新的socket,以便它可以繼續監聽原來用于客戶端連接請求的 socket 。
在客戶端,如果連接被接受,則成功地創建一個套接字和客戶端可以使用該 socket 與服務器進行通信。
客戶機和服務器現在可以通過 socket 寫入或讀取來交互了。
端點是IP地址和端口號的組合。每個 TCP 連接可以通過它的兩個端點被唯一標識。這樣,你的主機和服務器之間可以有多個連接。
Socket是對互聯網通信協議(如TCP/IP)的封裝和應用,它提供了一個調用接口(API),通過這個接口我們可以使用這些協議棧來與目標計算機進行通信。
在具體的編程語言中,針對不同的通信協議會有不同的Socket實現。當你需要與目標計算機進行基于某個協議的通信時,你可以調用相應協議的Socket實現。這些Socket實現會根據協議的要求進行數據的封裝和解封裝,以便你能夠與目標計算機進行通信。
因此,通過創建針對特定協議的Socket實現,你就可以使用Socket進行基于該協議的通信了。
具體舉一下例子:
如果抖音有一個通信協議,并且針對Java提供了一個具體的SDK,那么這個SDK中應該包含了一個Socket的實現,用于與抖音服務器進行通信。
當你創建這個Socket時,你可以使用SDK提供的API來與抖音服務器建立連接、發送和接收數據。SDK會根據抖音的通信協議對數據進行封裝和解封裝,以便你能夠與抖音服務器進行通信。
因此,通過創建這個Socket,你就可以使用SDK提供的API與抖音服務器進行通信了。
服務器指一個管理資源并為用戶提供服務的計算機軟件,通常分為文件服務器、數據庫服務器和應用程序服務器。嚴格來說,服務器本身就是計算機硬件,并在其中運行的管理軟件的一種管理資源,就如同電腦。按硬件還會分塔式服務器、卡片機、小型機。是一個大的概念,任何一臺電腦安裝了軟件的服務器端就可以叫服務器。
2.Python網絡編程
Socket
python提供了內置socket庫,從而能夠進行網路通信。下面我們直接展示一個案例。
下面簡單利用了sockert庫,來建立一個服務器,并且建立了一個客戶端,去訪問服務器并進行數據交互。
來源于菜鳥編程:?Python 網絡編程 | 菜鳥教程 (runoob.com)
Server.py
import socket
if __name__ == '__main__':# 建立一個服務端server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)server.bind(('localhost', 6999)) # 綁定要監聽的端口server.listen(5) # 開始監聽 表示可以使用五個鏈接排隊while True: # conn就是客戶端鏈接過來而在服務端為期生成的一個鏈接實例conn, addr = server.accept() # 等待鏈接,多個鏈接的時候就會出現問題,其實返回了兩個值print(conn, addr)while True:try:data = conn.recv(1024) # 接收數據print('recive:', data.decode()) # 打印接收到的數據conn.send(data.upper()) # 然后再發送數據except ConnectionResetError as e:print('關閉了正在占線的鏈接!')breakconn.close()
client.py
import socket
if __name__ == '__main__':client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 聲明socket類型,同時生成鏈接對象client.connect(('localhost', 6999)) # 建立一個鏈接,連接到本地的6969端口while True:# addr = client.accept()# print '連接地址:', addrmsg = '歡迎訪問菜鳥教程!' # strip默認取出字符串的頭尾空格client.send(msg.encode('utf-8')) # 發送一條信息 python3 只接收btye流data = client.recv(1024) # 接收一個信息,并指定接收的大小 為1024字節print('recv:', data.decode()) # 輸出我接收的信息client.close() # 關閉這個鏈接
WebSocket
?PS:以下全引用于HTML 菜鳥編程:?HTML5 WebSocket | 菜鳥教程 (runoob.com)
WebSocket 是 HTML5 開始提供的一種在單個 TCP 連接上進行全雙工通訊的協議。
WebSocket 使得客戶端和服務器之間的數據交換變得更加簡單,允許服務端主動向客戶端推送數據。在 WebSocket API 中,瀏覽器和服務器只需要完成一次握手,兩者之間就直接可以創建持久性的連接,并進行雙向數據傳輸。
在 WebSocket API 中,瀏覽器和服務器只需要做一個握手的動作,然后,瀏覽器和服務器之間就形成了一條快速通道。兩者之間就直接可以數據互相傳送。
現在,很多網站為了實現推送技術,所用的技術都是 Ajax 輪詢。輪詢是在特定的的時間間隔(如每1秒),由瀏覽器對服務器發出HTTP請求,然后由服務器返回最新的數據給客戶端的瀏覽器。這種傳統的模式帶來很明顯的缺點,即瀏覽器需要不斷的向服務器發出請求,然而HTTP請求可能包含較長的頭部,其中真正有效的數據可能只是很小的一部分,顯然這樣會浪費很多的帶寬等資源。
HTML5 定義的 WebSocket 協議,能更好的節省服務器資源和帶寬,并且能夠更實時地進行通訊。
瀏覽器通過 JavaScript 向服務器發出建立 WebSocket 連接的請求,連接建立以后,客戶端和服務器端就可以通過 TCP 連接直接交換數據。
當你獲取 Web Socket 連接后,你可以通過?send()?方法來向服務器發送數據,并通過?onmessage?事件來接收服務器返回的數據。
以下 API 用于創建 WebSocket 對象。
我們可以看出,websocket協議是基于tcp協議的全雙工通信協議。
它,允許服務端主動向客戶端推送數據。而且,只需要一次握手,就可建立一次長連接。
與傳統?AJAX輪訓方法實現推送技術相比,發送請求的次數減少,(數據每一次更新,就代表一次ajax的請求和響應,這是相對比較浪費帶寬資源的,通過websocket,客戶端和服務端可以建立一次長連接完成數據的交互。)