Socket(套接字) 介紹 UDP 和 TCP 協議之前,先熟悉下 socket 的基本概念。 基本概念 Socket 是通信的基石,是支持 TCP/IP 協議的網路通信的基本操作單元,在網路通信過程中端點的一種抽象表示。網路中使用 Socket 傳輸數據是一種特殊的網路 I/O。 工作模式 打開 ...
Socket(套接字)
介紹 UDP 和 TCP 協議之前,先熟悉下 socket 的基本概念。
- 基本概念
Socket 是通信的基石,是支持 TCP/IP 協議的網路通信的基本操作單元,在網路通信過程中端點的一種抽象表示。網路中使用 Socket 傳輸數據是一種特殊的網路 I/O。
- 工作模式
打開open -> 讀寫write/read -> 關閉close
- 五種信息
socket 包括了數據傳輸必須的五元組,分別為源IP、源埠、目的IP、目的埠和協議號
- 通信機制
基於流(stream)或者基於數據報(datagram)
- python 中使用
import socket
socket.socket(...)
UDP
UDP(User Datagram Protocol) 用戶數據報協議,是一種面向無連接的協議,提供簡單不可靠的信息傳輸服務,發送後不會確認信息是否到達。
UDP 通信模型中,在通信開始之前,不需要建立相關的鏈接,只需要發送數據即可,類似於生活中"寫信"。
- 使用 socket 使用 UDP 的收發數據,先看原理圖
- 客戶端代碼實現
# 客戶端 發送數據
# 1.創建udp套接字
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
while True:
send_data = input("請輸入要發送的數據:")
if send_data == 'exit':
break
# 2.發送數據
udp_socket.sendto(send_data.encode(), ("192.168.1.110", 8080)) # 對方的ip和port
# 3.關閉套接字
udp_socket.close()
- 服務端代碼實現
# 服務端 接收數據
HOST = '' # 空為localhost
PORT = 7788
ADDR = (HOST, PORT)
BUFSIZE = 1024 # 一次接收最大的尺寸
# 1.創建udp套接字
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 2.綁定本地套接字信息
udp_socket.bind(ADDR) # 綁定必須是本機的ip和port
while True:
# 3.接收數據
data, addr = udp_socket.recvfrom(BUFSIZE) # 返回接收數據和發送方的地址信息
if data.decode() == 'exit':
print("退出")
break
# 列印接收的數據
print(f"{addr[0]}:{addr[1]}", data.decode())
# 4.關閉套接字
udp_socket.close()
套接字是可以同時收發數據,是全雙工狀態,在調用
recvform
之前收到數據,操作系統會將數據暫時儲存起來,等到程式調用recvform
取出數據,在調用recvform
之後未接收到數據,程式會進入阻塞狀態,等待數據的到來。
TCP
TCP(Transmission Control Protocol)傳輸控制協議,是一種面向連接的,可靠的、基於位元組流的傳輸層通信協議。
TCP 通信需要經過創建連接、數據傳送、終止連接三個步驟。
TCP 通信模型中,在通信開始之前,一定要先建立相關的鏈接,才能發送數據,類似於生活中"打電話"。 這種連接是一對一的,因此TCP不適用於廣播的應用程式,基於廣播的應用程式請使用 UDP 協議。
特點:
面向連接(使用三次握手建立連接,連接已創建才作傳輸,採用四次揮手斷開連接。)
- 有序數據傳輸
- 重發丟失的數據包
- 捨棄重覆的數據包
- 無差錯的數據傳輸
阻塞/流量控制
使用 socket 實現 TCP 的收發數據,先看原理圖
- 客戶端代碼實現
# 客戶端
IPADDR = "192.168.1.110"
PORT = 7788
# 創建套接字
tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 連接伺服器,創建連接
tcp_socket.connect((IPADDR, PORT))
# 收發數據
send_data = input("輸入發送的數據:")
tcp_socket.send(send_data.encode())
# 關閉套接字,關閉連接
tcp_socket.close()
- 服務端代碼實現
# 服務端
IPADDR = “” # 預設localhost
PORT = 7788
BUFSIZE = 1024 # 一次接收最大的尺寸
print('server start...')
# 創建套接字(監聽套接字) =》買個手機
tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 綁定本地信息 =》 插入手機卡
tcp_socket.bind((IPADDR, PORT))
# 將手機設為正常,能夠響鈴 =》 讓預設的套接字由主動變為監聽
tcp_socket.listen(128) # 同一時間,最大允許連接128個客戶端
while True: # 迴圈為多個客戶端服務
print("waitting connected...")
# 等待電話到來 =》 等待別人電話的到來,此時程式會被阻塞,等待客戶端連接
new_client_socket, client_addr = tcp_socket.accept() # 新的專門為客戶端服務的socket,客戶端的addr
# 接收客戶端發送過來的請求,此時程式會阻塞等待客戶端發送數據
print("waitting recive data...")
while True: # 迴圈為同一個客戶端服務多次
recv_data = new_client_socket.recv(BUFSIZE)
# 如果recv 解堵塞,有兩種方式
# 1.客戶端發送過來數據
# 2.客戶端調用close斷開連接
if recv_data:
print(f"【{client_addr[0]}:{client_addr[1]}】", recv_data.decode())
# 回送一部分數據給客戶端
new_client_socket.send('收到'.encode())
else:
break
# 關閉套接字
new_client_socket.close()
print("connect closed")
tcp_socket.close()
print('server end...')
監聽套接字負責等待有新的客戶端進行連接
accept
產生的新的套接字用來為客戶端服務 如果將監聽套接字關閉了,那麼會導致不能再次等待新客戶端的到來,accept()
會失敗 關閉accept
返回的新的套接字,意味著不再為這個客戶端服務。