20181226粘包问题

粘包问题
一、什么是粘包:

粘包指的是数据与数据之间没有明确的分界线,导致程序不能正确读取数据。

TCP或UDP协议下,程序要将收发的数据交由操作系统处理,操作系统会设立缓冲区, 用于收发各个程序的数据

UDP(用户数据报协议): 是无连接的、面向消息的,面向消息的通信是有信息保护边界的。 基于数据包收发数据,数据包之间相互独立。 存在的问题: 数据包过大时,受制于发送方系统限制,数据包可能无法发送。 受制于接收方的系统缓存大小,当数据包超过限制时,数据包会丢失

TCP(传输控制协议): 可以传输较大数据并保证完整性, 是面向流的、面向连接的,但面向流的通信是无信息保护边界的。

两者区别: TCP是基于数据流的,收发的消息不能为空,客户端和服务器端都要添加空消息的处理机制,防止程序卡死 UDP是基于数据报的,即使输入空内容,也不是空消息,UDP协议会封装消息头。

 

二、粘包为什么会发生:

粘包只会发生在TCP下: 1、当单个数据包较小时,接收方可能一次读取多个包的数据 2、当整体数据较大时接收方可能一次仅读取一个包的一部分内容 3、另外TCP协议为了提高效率,增加了一种优化机制,会将数据较小且发送间隔较短的数据合并发送, 该机制也会导致发送方将两个数据包粘在一起发送

粘包问题产生的根本原因是接收方无法预知接受到的数据长度

 

 

三、如何解决粘包

自定义报头。

具体思路:

发送端: 1.先将所有的额外信息打包到一个头中 2.然后先发送头部数据 3.最后发送真实数据

接收端: 1.接收固定长度的头部长度数据 2.根据长度数据获取头部数据 3.根据头部数据获取真实数据

代码如下:

# 服务器端
import socket
import subprocess
import struct
import json

serve = socket.socket()
serve.bind(("127.2.1.1",9898))
serve.listen()
while True:
   client,addr = serve.accept()
   while True:
       try:
           cmd = client.recv(1024).decode("utf-8")
           if not cmd:continue  # 接受命令不能为空
           p = subprocess.Popen(cmd,
                                shell=True,
                                stdout=subprocess.PIPE,
                                stderr=-1
                                )  # subprocess调取子进程
           data = p.stdout.read()
           err_data = p.stderr.read()
           length = len(data)+len(err_data)  # 得到数据长度
           head = {"size":length,"xxx":"yyy"} # 自定义报头
           head_data = json.dumps(head).encode("utf_8")  # 序列化报头
           len_head_data = struct.pack("i",len(head_data))  # 以元组的形式打包,struct是bytes类型
           client.send(len_head_data)  # 先发送报头长度,此处信息长度固定,i模式为4
           client.send(head_data)  #发送报头
           client.send(data)  #发送数据,正常输出和错误输出一般只会发出一个
           client.send(err_data)
       except ConnectionResetError:
           client.close()
           break
serve.close()

 

# 客户端
import socket
import json
import struct

client = socket.socket()
client.connect(("127.2.1.1",9898))
while True:
   msg = input(">>>:").strip()
   if not msg:continue  # 命令不能为空
   client.send(msg.encode("utf-8"))  # 发送命令,编码为utf-8
   data = client.recv(4)  # 先接收4个bytes长度的数据,得到报头的长度
   head_length = struct.unpack("i",data)[0]   #收到的是一个元组,报头的长度
   head_data = client.recv(head_length).decode("utf-8")  #再次接收长度为报头的数据,获取报头
   head = json.loads(head_data)  # 反序列化报头
   data_length = head["size"]  # 获取字典中报头的长度

   size = 0
   res = b""  #预设空的bytes变量
   while size < data_length:
       real_data = client.recv(1024)
       size += len(real_data)
       res += real_data  # 变量完整相加,即得到所需数据
   print(res.decode("gbk"))   # win平台下,解码为gbk
client.close()
四、简单的视频上传和下载

1、应该使用TCP协议,因为必须保证文件的完整性

2、上传与下载都是bytes类型,而视频也正好是bytes类型

3、逻辑思路

自定义报头 ---》发送文件名----》文件大小-----》MD5值

读取文件数据,发送给对方

服务器端代码:

import socket
import struct
import json

server = socket.socket()
server.bind(("127.0.0.1",6767))
server.listen()
client,addr = server.accept()

f = open("用户上传文件","wb")  # 创建一个新的文件,注意此时会放到程序运行的当前文件夹下,且没有后缀

head_len = client.recv(4)  # 接收固定长度的报头长度数据
json_len = struct.unpack("i",head_len)[0] # 解码,获得元组eg:(43,),43即为报头的长度
json_str = client.recv(json_len).decode("utf-8")  # 获取报头信息并解码
head = json.loads(json_str)  # 反序列化,得到真正的报头信息


recv_size = 0
while recv_size < head["size"]:  # 获取文件大小
   data = client.recv(1024)
   f.write(data)  # 没有关闭状态下,会继续写入而不是覆盖
   recv_size += len(data)
print("接收完成")
f.close()  # 关闭文件
serve.close()  # 关闭程序

客户端代码:

import json
import struct
import socket
import os

client = socket.socket()
client.connect(("127.0.0.1",6767))

filepath = r"C:UserswangtDocumentsday276 操作系统与进程发展史.mp4"  # 获取文件路径
f = open(filepath,"rb")  # 只读模式打开文件

head = {"size":os.path.getsize(filepath),"filename":"操作系统与进程发展史.mp4"}  # 关注报头获取文件大小的方式

json_data = json.dumps(head).encode("utf-8")  # 报头序列化,用utf-8编码

json_len = struct.pack("i",len(json_data))  # 报头长度打包
client.send(json_len)  # 发送报头长度
client.send(json_data)  # 发送报头数据

while True:
   data = f.read(1024)  # 循环读取文件内容,每次读取1024bytes
   if not data:
       break
   client.send(data)
print("上传完成")

f.close()
client.close()

 

原文地址:https://www.cnblogs.com/realadmin/p/10181315.html