为何会出现黏包问题? 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