Python socket粘包问题(最终解决办法)
套接字:
就是将传输层以下的协议封装成子接口
对于应用程序来说只需调用套接字的接口,写出的程序自然是遵循tcp或udp协议的
实现第一个功能个:
实现:通过客户端向服务端发送命令,调取windows下面的cmd窗口,将服务端执行命令的结构,返回并显示在
客户端窗口上。
subprocess:
1.可以将执行结果返回
2.返回值是bytes类型
(基于这两点,可以应用在server端,将服务端的返回直接以bytes的格式直接send给客户端,
实现在客户端的显示)
问题1:粘包问题
粘包问题:实际是由TCP协议在传输数据时的2大特性来的
TCP协议又叫流式协议,在数据传输上,只要客户端发送相应的数据请求,
服务端会将数据像流水一样不断的send给客户端
基于这个特点,就会存在一个问题,当客户端给服务端发送一条命令,服务端成功接收并将命令的
结果返回到客户端的时候,由于客户端recv()的数量限制,可以一次不能完全取出,
这个时候就会存在,下次输入命令客户端首先拿到的返回值就是上次残留的没有收完的数据
基于粘包问题的解决思路就是:
发数据之前先把报头发给对方,让对方先知道要收的报头的长度,后面再传数据文件
自定义报头:
为甚么要自定义报头:
因为struck path(‘i’,3443242)
1.’i‘:类型不同,后面数字的长度大小也不同,大小是有限的(当超出范围时会报错)
2.因为报头里面含有的内容可能不仅仅只有total_siz还有filename、hash等等,知识单纯的把total_size
当做报头传入不合理,所以我们要自定义报头
Server端配置
from socket import *
import socket,subprocess,struct,json server = socket.socket(AF_INET, SOCK_STREAM)
server.bind(('127.0.0.1', 8080))
server.listen(5) while True:
conn,client = server.accept()
print(client)
while True:
try:
cmd = conn.recv(1024)
if len(cmd) == 0: break
obj=subprocess.Popen(
cmd.decode('utf-8'),
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE, )
out=obj.stdout.read()
err=obj.stderr.read() #制作报头
header_dic={
'filename':'a.txt',
'total_size':len(out)+len(err),
'hash':'abc32i5o24' }
#对包头进行序列化
header_json=json.dumps(header_dic) #字符串格式
header_bytes=header_json.encode('utf-8') #1.先发型报头的长度 len(header_bytes) struck为固定的4个字节
conn.send(struct.pack('i',len(header_bytes)))
#2.发送报头
conn.send(header_bytes)
#3.发送真是数据
conn.send(out)
conn.send(err) except ConnectionResetError:
break
conn.close() server.close()
Client端配置
from socket import *
import socket,struct,json
client=socket.socket(AF_INET,SOCK_STREAM)
client.connect(('127.0.0.1',8080))
while True:
cmd=input('输入你要操作的命令:')
client.send(cmd.encode('utf-8'))
if len(cmd) == 0:continue #1.先收报头的四个字节,首先拿到报头传来的长度-》bytes
header=client.recv(4) #i类型足够了 header为bytes类型
header_size=struct.unpack('i',header)[0] #拿到元祖形式,取第一个就是整个报头的长度
print(header_size) #为报头的长度值
#2.再收报头(对应服务端的conn.send(header_bytes))
header_bytes=client.recv(header_size) #根据报头的固定长度去收接收 #3.解析包头(就是将header_bytes文件先解码成json格式)
header_str=header_bytes.decode('utf-8')
header_dic=json.loads(header_str)
print(header_dic) total_size=header_dic['total_size']
print(total_size) recv_size=0 #定义一个初始的接收变量为0,只是个计数变量,为了统计与总的total_size的len大小
res=b''
while recv_size < total_size:
recv_data=client.recv(1024) #每次传过来的recv_data是bytes类型
res+=recv_data
recv_size+=len(recv_data) #循环增加每次接收值的长度 # cmd=client.recv(1024)
print(res.decode('gbk')) client.close()
粘包问题的最终解决方案,分析:
服务端:
目的为了自定义报头(报头中不仅包含长度,可能还有文件名等信息) subprocess
... 1.制作报头(字典形式)
header_dic={
'filename':'a.txt',
'total_size':len(out)+len(err),
'hash':'abc32i5o24' } 2.通过json将报头序列化再encode为bytes类型
header_json=json.dumps(header_dic) #字符串类型
header_bytes=header_json.encode('utf-8') #bytes类型 3.发送报头的长度,struck目的是固定封装好的报头为4个字节长度 =====对应客户端刚开始 header=client.recv(4)
struck: 1.将数字转为bytes类型,保证发送过去的是bytes类型 2.固定4个字节 第一次发:conn.send(struck.pack('i',len(header_bytes))) 先传给客户端固定了收的时候报头的长度,不至于报头和其他内容粘在一起 4.发送报头 =======对应客户端接收报头 header_bytes=client.recv(header_size)
第二次发:conn.send(header_bytes) 5.发送真实的数据
conn.send(out)
conn.send(err) 客户端:(bytes--int) 1.通过服务端返回的字节,拿到报头的的长度
header=client.recv(4) #header是4个bytes字节
header_size=struck.unpack('i',header)[0] #字节头-拿到int大小 2.再收报头
header_bytes=client.recv(header_size) #bytes类型 3.解析报头(1.先把内存中存放的bytes类型,用decode('utf-8')解码为字符串)
header_json=header_bytes.decode('utf-8')
header_dic=json.loads(header_json) #json反序列化出字典格式 (对应server第2步) print(header_dic) 4.拿到字典中的报头大小
total_size=header_dic['total_size']
print(total_size)
struck功能辅助理解:所以客户端刚开始接收4 大小是足够把报头接收完的。
#
struck工鞥理解:
import struct
#将整型转成bytes
res=struct.pack('i',1232435436)
print(res,len(res)) #res:为bytes类型,还是固定长度4(一般情况已经可以包含很多) #将bytes转成整型
aa=struct.unpack('i',res)
print(aa,aa[0],type(aa)) # print(len(res)) #4 一般情况多数bytes 4个长度足够了 """结果
b'\xecxuI' 4
(1232435436,) 1232435436 <class 'tuple'>
4
"""
Python socket粘包问题(最终解决办法)的更多相关文章
- Python socket粘包解决
socket粘包: socket 交互send时,连续处理多个send时会出现粘包,soket会把两条send作为一条send强制发送,会粘在一起. send发送会根据recv定义的数值发送一个固定的 ...
- Python socket粘包问题(初级解决办法)
server端配置: import socket,subprocess,struct from socket import * server=socket(AF_INET,SOCK_STREAM) s ...
- Python socket 粘包
目录 1 TCP的三次握手四次挥手 0 1.1 三次握手 1 1.2 四次挥手 2 2 粘包现象 3 2.1 基于TCP制作远程执行命令操作(win服务端) 4 2.1 基于TCP制作远程执行命令操作 ...
- python socket粘包及实例
1.在linux中经常出现粘包的出现(因为两个send近靠着,造成接受到的数据是在一起的.)解决方法: 在服务端两send的中间中再添加一个recv(),客户端添加一个send(),服务端收到信息确认 ...
- socket粘包现象加解决办法
socket粘包现象分析与解决方案 简单远程执行命令程序开发(内容回顾) res = subprocess.Popen(cmd.decode('utf-8'),shell=True,stderr=su ...
- 百万年薪python之路 -- socket粘包问题解决
socket粘包问题解决 1. 高大上版解决粘包方式(自定制包头) 整体的流程解释 整个流程的大致解释: 我们可以把报头做成字典,字典里包含将要发送的真实数据的描述信息(大小啊之类的),然后json序 ...
- Socket粘包问题
这两天看csdn有一些关于socket粘包,socket缓冲区设置的问题,发现自己不是很清楚,所以查资料了解记录一下: 一两个简单概念长连接与短连接:1.长连接 Client方与Server方先建立通 ...
- [转]关于Socket粘包问题
这两天看csdn有一些关于socket粘包,socket缓冲区设置的问题,发现自己不是很清楚,所以查资料了解记录一下: 一两个简单概念长连接与短连接:1.长连接 Client方与Server方先建立通 ...
- Python之粘包
Python之粘包 让我们基于tcp先制作一个远程执行命令的程序(1:执行错误命令 2:执行ls 3:执行ifconfig) 注意注意注意: res=subprocess.Popen(cmd.deco ...
随机推荐
- iOS中转义后的html标签如何还原
最近用swift做一个公司的小项目,遇到一个问题,就是通过api获取的html文本中的标签都已经被转义了, <p class="MsoNormal" align=" ...
- getuser
Help on function getuser in module getpass: getuser() Get the username from the environment or pa ...
- Servlet详解之两个init方法的作用
在Servlet中 javax.servlet.GenericServlet类 继承自java.lang.Object 实现了Serializable,,servlet ,ServletConfig ...
- java object默认的基本方法
java object默认的基本方法中没有copy(),含有如下9个方法: getClass(), hashCode(), equals(), clone(), toString(), notify ...
- Paper: TranE
论文标题:Translating Embeddings for Modeling Multi-relational Data 标题翻译:多元关系数据翻译嵌入建模 摘要: 考虑多元关系数据的实体和关系在 ...
- 用户输入和while循环
函数input()的工作原理 message=input('Tell me something,and I will repeat it back to you:') print(message) 编 ...
- Dojo操作dom元素的样式
1.使用dom-style的set方法,可以直接设置dom元素的样式属性,这和使用dom元素的style属性效果一样. 2.使用dom-class的replace方法可以替换某个dom元素的样式,ad ...
- js完成打印功能
最近在做项目要求实现打印功能,我采用js方式来实现 window.print();会弹出打印对话框,打印的是window.docunemt.body.innerHTML中的内容,可以局部打印,也可以全 ...
- 关于lua 5.3 服务端热更新流程
脚本的热更新的流程都大同小异, 第一步先保存旧代码的块的数据, 第二部加载新的代码块,第三步将旧代码块的局部和全局数据拷贝到新代码块的对应的 变量中. 在服务器热更新中,主要考虑热更的内容是什么, 一 ...
- iOS跳转到各种系统设置界面
定位服务 定位服务有很多APP都有,如果用户关闭了定位,那么,我们在APP里面可以提示用户打开定位服务.点击到设置界面设置,直接跳到定位服务设置界面.代码如下: //定位服务设置界面 NSURL *u ...