TCP協議 TCP是傳輸控制協議,建立雙向通道。 三次握手,建立連接 客戶端向服務端發送建立連接的請求 服務端接收請求返回確認信息給客戶端,並向客戶端發送建立連接的請求 客戶端接收請求返回確認信息給服務端 反饋機制: 一次請求必須有一次響應 。即收到請求後,必須告知對方已收到請求。 四次揮手,斷開連 ...
TCP協議
TCP是傳輸控制協議,建立雙向通道。
三次握手,建立連接
- 客戶端向服務端發送建立連接的請求
- 服務端接收請求返回確認信息給客戶端,並向客戶端發送建立連接的請求
- 客戶端接收請求返回確認信息給服務端
反饋機制:一次請求必須有一次響應。即收到請求後,必須告知對方已收到請求。
四次揮手,斷開連接
- 客戶端向服務端發送斷開連接的請求
- 服務端接收請求返回確認信息發給客戶端
- 服務端確認所有數據接收完畢以後,發送斷開連接的請求給客戶端
- 客戶端接收請求返回確認信息給服務端
socket套接字通信
定義:python內置的模塊,又稱套接字,用來封裝互聯網協議(應用層以下的層)
作用:實現互聯網協議應用層以下的工作,提高開發效率
使用方式:見代碼
- 服務端server.py
import socket
# 獲取socket 對象
server = socket.socket()
# 綁定服務端ip 地址和埠
# 127.0.0.1是迴環地址,表示本機ip
server.bind(('127.0.0.1', 8080))
# 半連接池,表示可以同時讓多少個客戶端訪問。
# 一個客戶端正在交互,剩下的等待交互,listen(n):n+1個客戶端
server.listen(5)
# 阻塞,直到客戶端訪問,返回連接請求和客戶端IP
conn, client = server.accept()
# 接收客戶端發送的信息並列印
# 接收預設最大位元組數:1024(可根據記憶體自行調整)
client_data = conn.recv(1024).decode('utf-8')
print(f"來自客戶端的消息:{client_data}")
# 向客戶端發送消息
send_msg = input("請輸入指令>>>>>:").strip().encode('utf-8')
conn.send(send_msg)
# 關閉連接
conn.close()
# 關閉服務
server.close()
- 客戶端client.py
import socket
# 創建socket對象
client = socket.socket()
# 向服務端請求連接
client.connect(('127.0.0.1', 9527))
# 向服務端發送數據,send只接收二進位數據
client_msg = input("請輸入要發送給服務端的信息>>>>:").strip()
client.send(client_msg.encode('utf-8'))
# 接收服務端返回的數據
# 接收預設最大位元組數:1024(可根據記憶體自行調整)
client_data = client.recv(1024).decode('utf-8')
print(f"來自服務端的消息:{client_data}")
# 關閉連接
client.close()
註意:
- 先啟動服務端,再啟動客戶端
- 一次數據請求必須有一次響應,服務端和客戶端不能同時發送請求或同時接收請求
粘包現象
- 現象一:數據多次發送時間間隔短,且量少時,接送一次讀取了信息,後續讀取記錄為空
# 客戶端.py
import socket
server = socket.socket()
server.connect(("127.0.0.1", 9527))
# 連續發送
server.send(b"hello")
server.send(b"hello")
server.send(b"hello")
server.close()
# 服務端.py
import socket
from socket import SOL_SOCKET
from socket import SO_REUSEADDR
server = socket.socket()
server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
server.bind(("127.0.0.1", 9527))
server.listen(5)
conn, client = server.accept()
data1 = conn.recv(1024)
data2 = conn.recv(1024)
data3 = conn.recv(1024)
print(data1)
print(data2)
print(data3)
conn.close()
server.close()
輸出結果
b'hellohellohello'
b''
b''
- 現象二:當發送數據的位元組數超出每次接收的最大限制數,會將上次沒有接收完的記錄在下次接收
# 客戶端.py
import socket
client = socket.socket()
client.connect(('127.0.0.1', 9527))
client.send(b'hello world!')
client.send(b'lift is smart!')
client.close()
# 服務端.py
import socket
from socket import SOL_SOCKET
from socket import SO_REUSEADDR
server = socket.socket()
server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
server.bind(('127.0.0.1', 9527))
server.listen(5)
conn, client = server.accept()
re_data1 = conn.recv(5).decode('utf-8')
re_data2 = conn.recv(5).decode('utf-8')
print(re_data1)
print(re_data2)
conn.close()
server.close()
輸出結果
hello
worl
struct模塊—解決粘包問題
定義:python內置的模塊,可以將 固定長度的數據,打包成固定格式的長度
作用:將真實數據,做成一個固定長度的報頭,客戶端發送給服務端,服務端可以接收報頭(反之亦然)。然後對報頭進行解包,獲取真實數據的長度,進行接收即可。
使用,以現象二舉例(實質都一樣解決)
# 客戶端.py
import socket
import struct
client = socket.socket()
client.connect(('127.0.0.1', 9527))
msg1 = 'hello world!'
# 使用struct模塊中的pack方法,模式‘i’表示4個位元組
# 將要發送的數據長度打包成一個header
header1 = struct.pack('i', len(msg1))
# 先將報頭髮送給服務端
client.send(header1)
# 再將真實數據發送給服務端
client.send(msg1.encode('utf-8'))
# 服務端.py
import socket
import struct
server = socket.socket()
server.bind(('127.0.0.1', 9527))
server.listen(5)
conn, client = server.accept()
# 讀取報頭
header = conn.recv(4)
# 使用struct.unpack 解析真實數據長度
header_len = struct.unpack('i', header)[0]
# 讀取真實數據
re_data = conn.recv(header_len)
print(re_data.decode('utf-8'))
conn.close()
server.close()