python学习day32 黏包 struct模块
為什么會出現(xiàn)黏包問題??
首先只有在TCP協(xié)議中才會出現(xiàn)黏包現(xiàn)象
是因為TCP協(xié)議是面向流的協(xié)議
在發(fā)送的數(shù)據(jù) 傳輸過程中 有緩存機(jī)制 來避免數(shù)據(jù)丟失
因此 在連續(xù)發(fā)送小數(shù)據(jù)的時候 以及接收大小不符的時候都容易出現(xiàn)黏包現(xiàn)象
本質(zhì)還是因為我們在接受數(shù)據(jù)的時候不知道發(fā)送的數(shù)據(jù)的長短
怎么解決黏包問題?
在接收端發(fā)送要發(fā)送的數(shù)據(jù)的大小
一種是不帶struct??一種是帶struct? 定制協(xié)議
?
黏包
http://www.cnblogs.com/Eva-J/articles/8244551.html#_label5
?注意:只有TCP有粘包現(xiàn)象,UDP永遠(yuǎn)不會粘包
黏包成因:
多個send可能會發(fā)生黏包現(xiàn)象
優(yōu)化算法不優(yōu)化
發(fā)生黏包兩種現(xiàn)象:
情況一 發(fā)送方的緩存機(jī)制
發(fā)送端需要等緩沖區(qū)滿才發(fā)送出去,造成粘包(發(fā)送數(shù)據(jù)時間間隔很短,數(shù)據(jù)了很小,會合到一起,產(chǎn)生粘包)
情況二 接收方的緩存機(jī)制
接收方不及時接收緩沖區(qū)的包,造成多個包接收(客戶端發(fā)送了一段數(shù)據(jù),服務(wù)端只收了一小部分,服務(wù)端下次再收的時候還是從緩沖區(qū)拿上次遺留的數(shù)據(jù),產(chǎn)生粘包)
?
?如何解決黏包?
存在的問題: 多了一次交互。程序的運(yùn)行速度遠(yuǎn)快于網(wǎng)絡(luò)傳輸速度,所以在發(fā)送一段字節(jié)前,先用send去發(fā)送該字節(jié)流長度,這種方式會放大網(wǎng)絡(luò)延遲帶來的性能損耗struct模塊
該模塊可以把一個類型,如數(shù)字,轉(zhuǎn)成固定長度的bytes
這個模塊可以把要發(fā)送的數(shù)據(jù)長度轉(zhuǎn)換成固定長度的字節(jié)。這樣客戶端每次接收消息之前只要先接受這個固定長度字節(jié)的內(nèi)容看一看接下來要接收的信息大小,那么最終接受的數(shù)據(jù)只要達(dá)到這個值就停止,就能剛好不多不少的接收完整的數(shù)據(jù)了。
?
import structret = struct.pack('i', 2049) # pack方法,將對象轉(zhuǎn)換成固定字節(jié)長度bytes類型 num = struct.unpack('i', ret) # 解包 print(num) # 元組 print(num[0]) # 數(shù)字?
?
連續(xù)send? 連續(xù)receive
?
我們在網(wǎng)絡(luò)上傳輸?shù)乃袛?shù)據(jù),都叫數(shù)據(jù)包
數(shù)據(jù)包中的數(shù)據(jù),都叫報文
報文里不只有你的數(shù)據(jù) ip地址 mac地址 端口號
所有的報文都有報頭? 相當(dāng)于協(xié)議 接收多少字節(jié) 什么順序 等等
報頭可以自己定制
根據(jù)報頭來解包接收的數(shù)據(jù)
復(fù)雜的應(yīng)用上就會用到定制報頭
比如:傳輸文件的時候
文件名、大小、類型、路徑
?
網(wǎng)絡(luò)傳輸中,處處有有協(xié)議,協(xié)議就是一堆報文和報頭 ———字節(jié)
協(xié)議的解析過程我們不需要關(guān)心
我們也可以自定制協(xié)議
?
?實現(xiàn)一個大文件的上傳或下載:
客戶端作發(fā)送端:
import socket import os import json import structsk = socket.socket() sk.connect(('127.0.0.1',8090))# 發(fā)文件 # 定制報頭 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_sizejson_head = json.dumps(head) # 字典轉(zhuǎn)成字符串 bytes_head = json_head.encode('utf-8') # 字符串轉(zhuǎn)bytes類型 head_len = len(bytes_head) # 報頭的長度 pack_len = struct.pack('i', head_len) # 報頭長度轉(zhuǎn)成固定的4字節(jié)長度 sk.send(pack_len) # 先發(fā)報頭長度 sk.send(bytes_head) # 再發(fā)報頭內(nèi)容 # 然后再發(fā)文件內(nèi)容: buffer = 1024 with open(filepath, 'rb') as f:while file_size:if file_size >= buffer:content = f.read(buffer)sk.send(content)file_size -= bufferelse:content = f.read(file_size)sk.send(content)break sk.close服務(wù)端:
import socket import os import json import structsk = 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 -= bufferelse:f.write(conn.recv(filesize))breakconn.close sk.close?
轉(zhuǎn)載于:https://www.cnblogs.com/happyfan/p/10450905.html
總結(jié)
以上是生活随笔為你收集整理的python学习day32 黏包 struct模块的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Visual Studio 快捷键使用方
- 下一篇: selenium+python自动化80