爲何會出現黏包問題? html
首先只有在TCP協議中才會出現黏包現象python
是由於TCP協議是面向流的協議算法
在發送的數據 傳輸過程當中 有緩存機制 來避免數據丟失json
所以 在連續發送小數據的時候 以及接收大小不符的時候都容易出現黏包現象緩存
本質仍是由於咱們在接受數據的時候不知道發送的數據的長短網絡
怎麼解決黏包問題?socket
在接收端發送要發送的數據的大小性能
一種是不帶struct 一種是帶struct 定製協議優化
黏包spa
http://www.cnblogs.com/Eva-J/articles/8244551.html#_label5
注意:只有TCP有粘包現象,UDP永遠不會粘包
黏包成因:
多個send可能會發生黏包現象
優化算法不優化
發生黏包兩種現象:
狀況一 發送方的緩存機制
發送端須要等緩衝區滿才發送出去,形成粘包(發送數據時間間隔很短,數據了很小,會合到一塊兒,產生粘包)
接收方不及時接收緩衝區的包,形成多個包接收(客戶端發送了一段數據,服務端只收了一小部分,服務端下次再收的時候仍是從緩衝區拿上次遺留的數據,產生粘包)
如何解決黏包?
存在的問題:
多了一次交互。程序的運行速度遠快於網絡傳輸速度,因此在發送一段字節前,先用send去發送該字節流長度,這種方式會放大網絡延遲帶來的性能損耗
struct模塊
該模塊能夠把一個類型,如數字,轉成固定長度的bytes
這個模塊能夠把要發送的數據長度轉換成固定長度的字節。這樣客戶端每次接收消息以前只要先接受這個固定長度字節的內容看一看接下來要接收的信息大小,那麼最終接受的數據只要達到這個值就中止,就能恰好很少很多的接收完整的數據了。
import struct ret = struct.pack('i', 2049) # pack方法,將對象轉換成固定字節長度bytes類型 num = struct.unpack('i', ret) # 解包 print(num) # 元組 print(num[0]) # 數字
連續send 連續receive
咱們在網絡上傳輸的全部數據,都叫數據包
數據包中的數據,都叫報文
報文裏不僅有你的數據 ip地址 mac地址 端口號
全部的報文都有報頭 至關於協議 接收多少字節 什麼順序 等等
報頭能夠本身定製
根據報頭來解包接收的數據
複雜的應用上就會用到定製報頭
好比:傳輸文件的時候
文件名、大小、類型、路徑
網絡傳輸中,到處有有協議,協議就是一堆報文和報頭 ———字節
協議的解析過程咱們不須要關心
咱們也能夠自定製協議
實現一個大文件的上傳或下載:
客戶端做發送端:
import socket import os import json import struct sk = socket.socket() sk.connect(('127.0.0.1',8090)) # 發文件 # 定製報頭 head = {'filepath':r'H:\python\day32', 'filename':r'05 python fullstack s9day32 strcuct模塊定製報頭的理論.mp4', 'filesize':None} file_path = os.path.join(head['filepath'],head['filename']) file_size = os.path.getsize(filepath) head['filesize'] = file_size json_head = json.dumps(head) # 字典轉成字符串 bytes_head = json_head.encode('utf-8') # 字符串轉bytes類型 head_len = len(bytes_head) # 報頭的長度 pack_len = struct.pack('i', head_len) # 報頭長度轉成固定的4字節長度 sk.send(pack_len) # 先發報頭長度 sk.send(bytes_head) # 再發報頭內容 # 而後再發文件內容: buffer = 1024 with open(filepath, 'rb') as f: while file_size: if file_size >= buffer: content = f.read(buffer) sk.send(content) file_size -= buffer else: content = f.read(file_size) sk.send(content) break sk.close
服務端:
import socket import os import json import struct sk = socket.socket() sk.bind(('127.0.0.1',8090)) sk.listen() conn, addr = sk.accept() # 接收 head_len = conn.recv(4) # 接收報頭長度 head_len = struct.unpack('i', head_len)[0] # 解包成元組 第一個 json_head = conn.recv(head_len).decode('utf-8') head = json.loads(json_head) # 報頭 filesize = head['filesize'] buffer = 1024 # 寫入文件 with open(head['filename'], 'wb') as f: while filesize: if filesize >= buffer: content = conn.recv(buffer) f.write(content) filesize -= buffer else: f.write(conn.recv(filesize)) break conn.close sk.close