粘包,自定义协议,struct模块,粘包解决终极大招
粘包:
1.粘包问题出现的原因: (udp不会出现粘包问题)
1.1.tcp是流式协议,数据像水流一样黏在一起,没有任何边界区分
1.2.收数据没收干净,有残留,就会下一次结果混淆在一起去(客户端接受限制,发送端数据量太大)
2.解决粘包问题思路:
1.当时短连接的情况下,不用考虑粘包的情况
2.如果发送数据无结构,如文件传输,这样发送方只管发送,接收方只管接收存储就ok,也不用考虑粘包
3.如果双方建立长连接,需要在连接后一段时间内发送不同结构数据
3.1发送端给每个数据包添加包首部,首部中应该至少包含数据包的长度,这样接收端在接收到数据后,通过读取包首部的长度字段,便知道每一个数据包的实际长度了。
3.2发送端将每个数据包封装为固定长度(不够的可以通过补0填充),这样接收端每次从接收缓冲区中读取固定长度的数据就自然而然的把每个数据包拆分开来。
3.3可以在数据包之间设置边界,如添加特殊符号,这样,接收端通过这个边界就可以将不同的数据包拆分开。
struct模块:
import struct
# info = b'hello big baby'
# print(len(info)) # 数据真实的长度(bytes) 14
# res = struct.pack('i', len(info)) # 将数据打包成固定的长度 i是固定的打包模式
# print(len(res)) # 打包之后长度为(bytes) 4 报头
# real_len = struct.unpack('i', res)
# print(real_len) # (14,) 根据固定长度的报头 解析出真实数据的长度
# desc = b'hello my baby I will take you to play big ball'
# print(len(desc)) # 数据真实的长度(bytes) 46
# res1 = struct.pack('i', len(desc))
# print(len(res1)) # 打包之后长度为(bytes) 4 报头
# real_len1 = struct.unpack('i', res1)
# print(real_len1) # (46,) 根据固定长度的报头 解析出真实数据的长度
服务端:
'''终极解决方案:字典作为报头打包 效果更好 数字更小'''
import socket
import subprocess #Popen(子进程的创建和管理都靠他处理)
import struct
phone = socket.socket() #默认TCP协议
phone.bind(('127.0.0.1',8080)) #绑定ip加端口
phone.listen(5) #半连接池大小
while True:
conn,client_addr = phone.accept() #conn为三次握手的双向通路,client_addr为ip+端口
while True:
try:
cmd = conn.recv(1024)
if len(cmd) == 0:break
obj = subprocess.Popen(cmd.decode('gbk'), #shell命令,可以是字符串或者序列类型(list,元组)
shell=True, #如果该参数为 True,将通过操作系统的 shell 执行指定的命令。
stdout=subprocess.PIPE, #输出
stderr=subprocess.PIPE #错误句柄
)
stdout_res = obj.stdout.read()
stderr_res = obj.stderr.read()
total_size = len(stdout_res) + len(stderr_res)
#先发头部信息(固定长度的bytes):对数据描述的信息
#int-->固定长度的bytes
header = struct.pack('i',total_size) #按照指定格式将字节流转换为Python指定的数据类型(pick为字符串类型),短的用i,长的用Q
conn.send(header)
#再发真实数据
conn.send(stdout_res)
conn.send(stderr_res)
except Exception:
break
conn.close()
客户端:
import socket
import struct
client = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #流式协议--> tcp协议
client.connect(('127.0.0.1',8080)) #进行3次握手
while True:
msg = input('输入要发送的消息:').strip()
if len(msg) == 0:continue #解决输入空格的bug
client.send(msg.encode('utf8')) #编码以二进制的形式发送
#解决粘包问题的思路
# 2.1:先收固定长度的头:解析出数据的描述信息,包括数据的总大小total_size
header = client.recv(4) #如果是Q的话就是8
# total_size = xxxx
total_size = struct.unpack('i',header)[0] #解包
# recv_size = 0,循环接受,每接受一次,recv_size+=接受的长度
recv_size = 0
# 直到recv_size = total_size,结束循环
while recv_size < total_size:
recv_data = client.recv(1024)
recv_size+=len(recv_data)
print(recv_data.decode('gbk'),end='')
print('')
client.close()
解决粘包的终极大招:
################################模板
服务端:
#解决粘包问题终极版
import socket
import subprocess #Popen(子进程的创建和管理都靠他处理)
import struct
import json
phone = socket.socket() #默认TCP协议
phone.bind(('127.0.0.1',8081)) #绑定ip加端口
phone.listen(5) #半连接池大小
while True:
conn,client_addr = phone.accept() #conn为三次握手的双向通路,client_addr为ip+端口
while True:
try:
cmd = conn.recv(1024)
if len(cmd) == 0:break
obj = subprocess.Popen(cmd.decode('gbk'), #shell命令,可以是字符串或者序列类型(list,元组)
shell=True, #如果该参数为 True,将通过操作系统的 shell 执行指定的命令。
stdout=subprocess.PIPE, #输出
stderr=subprocess.PIPE #错误句柄
)
stdout_res = obj.stdout.read()
stderr_res = obj.stderr.read()
total_size = len(stdout_res) + len(stderr_res)
#1.制作头部
header_dic = {
'filename':'a.txt', #文件名称
'total_size':total_size, #文件长度
'md5':'21561adas61d6'
}
json_str = json.dumps(header_dic) #数据类型转成字符串
json_str_bytes = json_str.encode('gbk') #字符串转成二进制
#2.先发头部信息(固定长度的bytes):对数据描述的信息
#int-->固定长度的bytes
header = struct.pack('i',len(json_str_bytes)) #按照指定格式将字节流转换为Python指定的数据类型(pick为字符串类型),短的用i,长的用Q
conn.send(header) #发送头的长度 4个字节
#3.发头信息
conn.send(json_str_bytes)
#再发真实数据
conn.send(stdout_res)
conn.send(stderr_res)
except Exception:
break
conn.close()
客户端:
#解决粘包问题终极版
import socket
import struct
import json
client = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #流式协议--> tcp协议
client.connect(('127.0.0.1',8081)) #进行3次握手
while True:
msg = input('输入要发送的消息:').strip()
if len(msg) == 0:continue #解决输入空格的bug
client.send(msg.encode('gbk')) #编码以二进制的形式发送
# 接收端
# 1.先收到4个字节,从中提取接下来头的长度
header = client.recv(4)
#2.接收头,并解析
header_len = struct.unpack('i',header)[0] #header_len为二进制的长度
json_str_bytes = client.recv(header_len) #接收
json_str = json_str_bytes.decode('gbk') #二进制转成字符串
header_dic = json.loads(json_str) #字符串转成数据类型
print(header_dic)
total_size = header_dic['total_size'] #得到头的长度
#接收真实的数据
recv_size = 0
# 直到recv_size = total_size,结束循环
while recv_size < total_size:
recv_data = client.recv(1024)
recv_size+=len(recv_data)
print(recv_data.decode('gbk'),end='')
print('')
client.close()
运行得到的结果为:
{'filename': 'a.txt', 'total_size': 65, 'md5': '21561adas61d6'}
'weqeqw' 不是内部或外部命令,也不是可运行的程序
或批处理文件。
粘包,自定义协议,struct模块,粘包解决终极大招的更多相关文章
- 2、粘包现象(struct模块)
昨天我们所做的套接字是有漏洞的,它会出现粘包现象,没有发现这个问题的我们今天会进行演示.今天也会稍微讲解一下基于udp的套接字. 一.基于udp的套接字 udp是无链接的,先启动哪一端都不会报错 ud ...
- 29、粘包现象(struct模块)
昨天我们所做的套接字是有漏洞的,它会出现粘包现象,没有发现这个问题的我们今天会进行演示.今天也会稍微讲解一下基于udp的套接字. 本篇导航: 基于udp的套接字 粘包现象 粘包 解决粘包方法 stru ...
- socker通信-struct模块-粘包问题
Socket概念 Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口.在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对 ...
- 黏包现象、struct模块和解决黏包问题的流程、UDP协议、并发编程理论、多道程序设计技术及进程理论 _
目录 黏包现象 二.struct模块及解决黏包问题的流程 三.粘包代码实战 UDP协议(了解) 并发编程理论 多道技术 进程理论 进程的并行与并发 进程的三状态 黏包现象 什么是粘包 1.服务端连续执 ...
- 11月17日内容总结——黏包现象、struct模块和解决黏包问题的流程、UDP协议、并发编程理论、多道程序设计技术及进程理论
目录 一.黏包现象 什么是黏包 黏包现象产生的原因 二.struct模块及解决黏包问题的流程 struct模块 解决黏包问题初级版本 解决过程中遇到的问题 解决黏包问题终极解决方案 三.粘包代码实战 ...
- struct模块-黏包的解决方法
黏包的解决方案 解决方案一 问题的根源在于,接收端不知道发送端将要传送的字节流的长度,所以解决粘包的方法就是围绕,如何让发送端在发送数据前,把自己将要发送的字节流总大小让接收端知晓,然后接收端来一个死 ...
- 2、网络并发编程--套接字编程、黏包问题、struct模块、制作简易报头、上传文件数据
昨日内容回顾 面向对象复习(json序列化类) 对象.类.父类的概念 三大特性:封装 继承 多态 双下开头的方法(达到某个条件自动触发) __init__:对象实例化自动触发 __str__:对象执行 ...
- 黏包现象、struct模块、并行与并发
1.黏包现象 1.黏包现象产生的背景: 1.1 服务端连续执行三次recv 1.2 客户端连续执行三次send 执行上述操作会发现服务端一次性接收到了客户端三条消息,而后面两次什么都没接收到,该现象称 ...
- SpringBoot包扫描之多模块多包名扫描和同类名扫描冲突解决
前言 我们在开发springboot项目时候,创建好SpringBoot项目就可以通过启动类直间启动,运行一个web项目,非常方便简单,不像我们之前通过Spring+SpringMvc要运行启动一个w ...
- 网络编程 --- subprocess模块,struct模块,粘包,UDP协议,socket_server模块
目录 subprocess模块 struct模块 粘包 UDP协议 socket_server模块 subprocess模块 作用: 1.可以帮你通过代码执行操作系统的终端命令 2.并返回终端执行命令 ...
随机推荐
- Alibaba Cloud Linux 3.2104 64位安装php7.2.12
1 安装php所需要的扩展 yum -y install libxml2 libxml2-devel openssl openssl-devel bzip2 bzip2-devel curl curl ...
- 常用功能系列---【JWT生成Token实现接口登录认证方案思路】
JWT生成Token实现接口登录认证方案思路 方案一(双token实现无感刷新) 在token中,refreshToken的作用主要是避免token过期时,前端用户突然退出登录,跳转至登录页面. 但是 ...
- Javaheima20
Java 学习内容 单元测试 反射 注解 动态代理 开发好的系统中存在很多的方法,如和对这些方法的正确性进行测试 如何在程序运行时取得到Class对象,然后取获得Class中的每个成分 注解是什么,具 ...
- Ndisuio win10 注册表下载
看了一下应该没有什么隐私内容,丢失这个注册表信息会让网络服务无法启动,新建txt,复制后修改后缀为.reg,双击录入,在管理员权限下cmd中输入 netsh winsock reset 重启 Wind ...
- 单向链表&有关类和对象
// Test515.cpp: 定义控制台应用程序的入口点.// #include "stdafx.h"#include <iostream>using namespa ...
- mysql数据迁移,通用windows->linux,linux->windows
1. 将用到的数据库文件夹直接拷贝到目标文件夹,mysql5.7在linux中默认在var/lib/mysql,将data下ibdata1也要拷贝进去 2. linux下需要将所有者root改为mys ...
- vs调试导入功能时莫名自动结束调试
新装了vs2022,默认下图所示的地方打勾,导致调试文件导入功能时触发了自动结束调试,真是坑爹~~
- unlua
安装 复制 Plugins 目录到你的UE工程根目录. 重新启动你的UE工程 注意点 新建工程后,必须重新拷贝插件,重新编译.不能从老项目中拷贝,会崩溃~ 加载c++类和蓝图类 -- c++类 loc ...
- SQL Injection(SQL注入)
什么是SQL注入? SQL(结构化查询语言)注入,通常称为 SQLi,是对 Web 应用程序数据库服务器的攻击,会导致执行恶意查询.当 Web 应用程序使用未经正确验证的用户输入与数据库通信时,攻击者 ...
- Leftpad事件 我们是不是早已忘记该如何好好地编程?
多年前的Leftpad 撤包事件使得React . Babel 和许多流行的npm模块都受到波及,无法正常运行. 这些受到影响的模块都引入了一个叫做 left-pad 的模块. 以下就是这十一行代码: ...