粘包,自定义协议,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.并返回终端执行命令 ...
随机推荐
- supper网盘快速下载器
本人搬砖党喜欢和大家分享一些快速文档 废话少说 很好用,亲测.对有需求的人 速度很快 软件永久有效下载链接:链接: https://pan.baidu.com/s/1g6LIk4mw18Bov0U7D ...
- CMMI的软件工程13-16章读书笔记
一.软件测试 软件测试是为了发现程序中的错误而执行的过程.测试只能证明软件有错,而不能保证软件程序没错. 1. 软件版本 Alpha版 公司内测版本 Beta版 对外公测版本 发布版 正式发布版本 ...
- 【转载】MSVC中C++ UTF8中文编码处理探究
字符集 先说一个名词:字符集,没听过的先百度一下,其实就是一种将字符编码的格式,像我们常说的ASCII,UTF8,GBK都是常用的字符集. 首先要清楚,从你在编辑器里输入一个UTF8汉字开始,到最终在 ...
- PHP中获取时间的下一周下个月的方法
PHP中获取时间的下一周,下个月等通常用于定制服务的时候使用,比如包月会员,包年等等 //通常用于定制服务的时候使用,比如包月会员,包年等等 //获取当前时间过一个月的时间,以DATETIME格式显示 ...
- celery异步任务队列入门
参考: Celery入门 任务调度delay&apply_async celery 简要概述 Celery 中文手册 Celery动态添加定时任务 全网最细之Celery 4.x动态添加定时任 ...
- 试题管理/在线课程/模拟考试/能力评估报告/艾思在线考试系统www.aisisoft.cn
艾思软件发布在线考试系统, 可独立部署, 欢迎咨询索要测试账号 一. 主要特点: ThinkPHP前后端分离框式开发 主要功能有: 在线视频课程, 模拟考试, 在线考试, 能力评估报告, 考试历史错题 ...
- Rstudio R get filename full path
FILENAME=basename(rstudioapi::getActiveDocumentContext()$path) FULLPATH=dirname(rstudioapi::getActiv ...
- FFT及NTT
FFT--快速傅里叶变换(附NTT--快速数论变换) FFT是一种能在O(nlogn)时空复杂度内求解两个函数卷积的优秀算法. 算法思想(DFT): 对于函数 \(f(x)=\Sigma_{i=0}^ ...
- 手写 Java HashMap 核心源码
手写 Java HashMap 核心源码 手写 Java HashMap 核心源码 上一章手写 LinkedList 核心源码,本章我们来手写 Java HashMap 的核心源码. 我们来先了解一下 ...
- Springboot jar 打包脚本和启动脚本
说明: SpringBoot极大的提高了工作效率,集成了各大厂优秀的组件,好处就不多说了,使用配置也非常方便,本文主要讲解如何使用更方便的方式打包发布,利用SpringBoot的新特性内置tomcat ...