1. 程式人生 > 其它 >socket模組和黏包問題

socket模組和黏包問題

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.接收真實的資料