socket模組和黏包問題
阿新 • • 發佈:2022-04-15
socket套接字簡介
編寫cs架構的程式 實現資料互動
OSI七層相當複雜
socket套接字是一門技術
socket模組>>>:提供了快捷方式 不需要自己處理每一層
"""
以後我們寫軟體連socket的影子都看不到 因為被封裝起來
socket是最底層的原理 很多框架都封裝了 其實我們不需要深入研究
"""
socket模組
cs架構的軟體無論是在編寫還是執行 都應該先考慮服務端 # 服務端 import socket server = socket.socket() # 買手機 """ 通過檢視原始碼得知 括號內不寫引數預設就是基於網路的遵循TCP協議的套接字 """ server.bind(('127.0.0.1', 8080)) # 插電話卡 """ 服務端應該具備的特徵 固定的地址 ... 127.0.0.1是計算機的本地迴環地址 只有當前計算機本身可以訪問 """ server.listen(5) # 開機 """ 半連線池(暫且忽略 先直接寫 後面講) """ sock, addr = server.accept() # 等待並接聽電話 沒有人來就原地等待(程式阻塞) """ listen和accept對應TCP三次握手服務端的兩個狀態 """ print(addr) # 客戶端的地址 data = sock.recv(1024) # 聽別人說話 print(data.decode('utf8')) sock.send('你好啊'.encode('utf8')) # 回覆別人說的話 """ recv和send接收和傳送的都是bytes型別的資料 """ sock.close() # 掛電話 server.close() # 關機 # 客戶端 import socket client = socket.socket() # 產生一個socket物件 client.connect(('127.0.0.1', 8080)) # 根據服務端的地址連結 client.send(b'hello sweet heart!!!') # 給服務端傳送訊息 data = client.recv(1024) # 接收服務端回覆的訊息 print(data.decode('utf8')) client.close() # 關閉客戶端 一邊是recv那麼另一邊必須是send 兩邊不能相同 否則就'冷戰'了
通訊迴圈
1.利用input獲取使用者輸入 2.將雙方用於資料互動的程式碼迴圈起來 while True: data = sock.recv(1024) # 聽別人說話 print(data.decode('utf8')) msg = input('請回復訊息>>>:').strip() sock.send(msg.encode('utf8')) # 回覆別人說的話 while True: msg = input('請輸入你需要傳送的訊息>>>:').strip() client.send(msg.encode('utf8')) # 給服務端傳送訊息 data = client.recv(1024) # 接收服務端回覆的訊息 print(data.decode('utf8'))
程式碼優化及連結迴圈
1.傳送訊息不能為空 統計長度並判斷即可 2.反覆重啟服務端可能會報錯>>>:address in use 這個錯在蘋果電腦報的頻繁 windows頻率較少 from socket import SOL_SOCKET,SO_REUSEADDR server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) # 在bind前加 3.連結迴圈 """ 如果是windows 客戶端異常退出之後服務端會直接報錯 處理方式 異常處理 如果是mac或linux 服務端會接收到一個空訊息 處理方式 len判斷 """ 客戶端如果異常斷開 服務端程式碼應該重新回到accept等待新的客人 # 目前我們的服務端只能實現一次服務一個人 不能做到同事服務多個 學了併發才可以實現
半連線池
listen(5)
# py檔案默認同一時間只能執行一次 如果想單獨分開執行多次
# 半連線池
設定的最大等待人數 >>>: 節省資源 提高效率
黏包問題
data1 = conn.recv(1024)
print(data1)
data2 = conn.recv(1024)
print(data2)
data3 = conn.recv(1024)
print(data3)
client.send(b'hello')
client.send(b'jason')
client.send(b'kevin')
"""
三次列印的結果
b'hellojasonkevin'
b''
b''
"""
# TCP協議的特點
會將資料量比較小並且時間間隔比較短的資料整合到一起傳送
並且還會受制於recv括號內的數字大小(核心問題!!!)
流式協議:跟水流一樣不間斷
"""
問題產生的原因其實是因為recv括號內我們不知道即將要接收的資料到底多大
如果每次接收的資料我們都能夠精確的知道它的大小 那麼肯定不會出現黏包
"""
思路:
困擾我們的核心問題是不知道即將要接收的資料多大
如果能夠精準的知道資料量多大 那麼黏包問題就自動解決了!!!
解決黏包問題
方向:精準獲取資料的大小
# struct模組
import struct
data1 = 'hello world!'
print(len(data1)) # 12
res1 = struct.pack('i', len(data1)) # 第一個引數是格式 寫i就可以了
print(len(res1)) # 4
ret1 = struct.unpack('i', res1)
print(ret1) # (12,)
data2 = 'hello baby baby baby baby baby baby baby baby'
print(len(data2)) # 45
res2 = struct.pack('i', len(data2))
print(len(res2)) # 4
ret2 = struct.unpack('i', res2)
print(ret2) # (45,)
"""
pack可以將任意長度的數字打包成固定長度
unpack可以將固定長度的數字解包成打包之前資料真實的長度
思路:
1.先將真實資料打包成固定長度的包
2.將固定長度的包先發給對方
3.對方接收到包之後再解包獲取真實資料長度
4.接收真實資料長度
"""
終極解決方案
1.先構造一個字典
內部存檔了真實資料相關的資訊
大小 名稱 簡介 ...
2.對字典做打包處理
3.將固定長度的資料(字典)傳送給對方
4.傳送真實的字典資料
5.傳送真實的真正資料
1.先接收固定長度的字典包
2.解析出字典的真實長度
3.接收字典資料
4.從字典資料中解析出各種資訊
5.接收真實的資料