TCP 和 UDP 的區別以及優缺點?
▍簡述
- TCP:有連線,所以握手過程會消耗資源,過程為可靠連線,不會丟失資料,適合大資料量交換
- UDP:非可靠連線,會丟包,沒有校驗,速度快,無須握手過程
TCP | UDP | |
---|---|---|
是否連線 | 面向連線 | 面向非連線 |
傳輸可靠性 | 可靠的 | 不可靠的 |
應用場合 | 傳輸大量資料 | 少量資料 |
速度 | 慢 | 快 |
▍TCP
- 是網際網路上最常用的協定,這種協定較為可靠 TCP 為每個封包分配一個唯一的識別碼和一個序號,這些號碼能讓接收端識別封包的完整性,以及封包的順序。
- 當接收端收到封包後,如果順序正確,會向發送端傳送一個確認信號(Acknowledgement),以此確認接收端已經收到封包。
- 發送端傳送另一個封包。
- 如果封包遺失或發送順序錯誤,接收端會保持沈默,不發送確認信號。這表示發送端需要重新傳送封包。
▍UDP
- 不需要唯一識別碼和序號就能完成相同的工作。
- 這種協定以串流方式傳送資料,發送端不會等待接收端的確認信號,會繼續不斷發送封包資料。
- UDP 幾乎沒有錯誤修正功能,也不在乎封包遺失,因此很容易出錯,但傳輸速度比 TCP 更快。
- 串流媒體、VoIP 語音、網路遊戲等服務經常使用 UDP 協定,這網路應用不太需要可靠性機制
參考資料:https://nordvpn.com/zh-tw/blog/tcp-udp-bijiao/
Tcp 連線數限制
在 Linux 系統中,如果兩個機器要通訊,那麼相互之間需要建立 TCP 連線,為了讓雙方互相認識,Linux 系統用一個四元組來唯一標識一個 TCP 連線: {local ip, local port, remote ip, remote port},即本機 IP、本機埠、遠端 IP、遠端埠,IP 和埠就相當於小區地址和門牌號,只有拿到這些資訊,通訊的雙方才能互相認知。
在 Linux 系統中,表示埠號(port)的變數佔 16 位,這就決定了埠號最多有 2的16次方個,即65,536個,另外埠 0 有特殊含義不給使用,這樣每個伺服器最多就有 65,535 個埠可用。因此,65,535 代表 Linux 系統支援的 TCP 埠號數量,在 TCP 建立連線時會使用。
▍Linux 伺服器只作為客戶端
這時候每發起一個 TCP 請求,系統就會指定一個空間的本地埠給你用,而且是獨佔式的,不會被別的 TCP 連線搶走,這樣最多可以建立 65535 個連線,每個連線都與不同的伺服器進行互動。
▍Linux 伺服器只作為服務端
這種場景下,服務端就會固定的監聽本地埠 port,等著客戶端來向它發起請求。為了計算簡單,我們假設伺服器端的 IP 跟埠是多對一的,這樣 TCP 四元組裡面就有 remote ip 和 remote port 是可變的,因此最大支援建立 TCP 個數為 2的32次方(IP地址是32位的)乘以 2的16次方(port是16位的)等於 2的48次方。
參考:https://www.juduo.cc/club/550281.html
強行關閉客戶端和服務器之間的連接?
在 socket 通信過程中不算循環檢測一個全局變量(開關標記變量),一旦標記變量變為關閉,則調用 socket 的 close 方法,循環結束,從而達到關閉連接的目的。
python 實現
此章節以 Server 端和 Client 端做 TCP 和 UDP 比較區別
Server 端怎麼收取用戶的訊息
- TCP-Server 需要監聽用戶訊息,流程:
- 建立 socket:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
,並指定socket.AF_INET (TCP)
的通訊協定 - 綁定 socket 到本地 IP 與 port:
s.bind()
- (TCP才有)開始監聽:
s.listen()
- (TCP才有)等待與接受客戶端的請求連線:
s.accept()
- 接收客戶端傳來的資料:
s.recv()
- 傳送給對方發送資料:
s.send()
、s.sendall()
- 傳輸完畢後,關閉 socket:
s.close()
import socket # 1.創建socket對象 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 2.將socket綁定到指定地址 address = ('127.0.0.1', 10140) s.bind(address) # 3.接收連接請求 s.listen(5) # 4.等待客戶請求一個連接 # 調用accept方法時,socket會進入“waiting”狀態。 # 接受方法返回一個包含所有元素的元組(連接,地址)。 # 第一個元素連接是新的socket對象,服務器必須通過它與客戶通信; # 第二個元素地址是客戶的互聯網地址。 ss, addr = s.accept() print 'got connect from', addr # 5.處理:服務器和客戶端通過send和recv通信方法 # 發送方法返回已發送的字節個數。 # 調用接收時間,服務器必須指定一個還原,它對應可通過本次方法調用來接收的最大數據量。 # recv 方法在接收數據時會進入“阻塞”狀態,最終返回一個字符串,用它表示收到的數據。 # 如果發送的數據量超過了接收所允許的,數據會被截短。 # 多餘的數據將緩衝於接收端以後調用recv時,多餘的數據會從刪除刪除。 while True: ra = ss.recv(512) print 'client:', ra ss.send('received') ss.close() #传输结束,关闭连接 s.close()
- 建立 socket:
- UDP-Server:不需要監聽訊息
import socket address = ('127.0.0.1', 10141) s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.bind(address) while True: data, addr = s.recvfrom(2048) print "received:", data, "from", addr s.close()
Client 端怎麼發送訊息
- TCP Client 流程:
- 建立socket:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- 連線至遠端地址:
s.connect()
- 傳送資料:
s.send()
、s.sendall()
- 接收資料:
s.recv()
- 傳輸完畢後,關閉socket:
s.close()
import socket target_host = 'www.google.com' target_port= 80 # 建立一個 socket 物件(引數 AF_INET 表示標準 IPv4 地址或主機名,SOCK_STREAM 表示 TCP 客戶端) client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 將客戶端連線到伺服器 client.connect((target_host, target_port)) # 向伺服器傳送資料 client.send("GET / HTTP/1.1\r\nHost: google.com\r\n\r\n") # 接收返回的資料 response = client.recv(4096) # 列印返回資料 print(response)
- 建立socket:
- UDP Client 流程:
- 因為 UDP 是一個無連線狀態的傳輸協議,所以不需要在此之前呼叫 connect() 函式。
import socket target_host = '127.0.0.1' target_port= 80 # 建立一個 socket 物件(引數 SOCK_DGRAM 表示 UDP 客戶端) client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 向伺服器傳送資料 client.sendto("AAABBBCCC", (target_host, target_port)) # 接收返回的資料 data, addr = client.recvfrom(4096) # 列印返回資料 print(data)