Python 的黏包问题
Client 端内的代码: #Author:BigBao
#Date:2018/7/4
import socket
import struct
client=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
client.connect(('127.0.0.1',8012))
while True:
cmd=input('>>>: ').strip()
if not cmd:continue
client.send(cmd.encode('utf-8'))
# 这里我们先收取报头,4个字节
header=client.recv(4)
total_size=struct.unpack('i',header)[0]
# 接收真实的数据,直到收干净为止
recv_size=0
res=b''
while recv_size < total_size:
recv_data=client.recv(1024)
res+=recv_data
recv_size+=len(recv_data)
print(res.decode('gbk'))
client.close()
Server 端的代码 #Author:BigBao
#Date:2018/7/4
import socket
import subprocess
import struct server=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.bind(('127.0.0.1',8012))
server.listen(5) while True: # 建立连接循环
conn,client_addr=server.accept()
while True: # 建立通信循环
try:
cmd=conn.recv(1024)
if not cmd:break
obj=subprocess.Popen(cmd.decode('utf-8'),shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
stdout=obj.stdout.read()
stderr=obj.stderr.read()
# 发送固定长度的报文头,目前报文头里值包含数据的大小,接下来制定报头
total_size=len(stdout)+len(stderr)
conn.send(struct.pack('i',total_size)) # 这里发送过去的数据是一个二进制数据,且长度为4,但是这里要是total_size 过大的话,这里就不行了,会报错struct.error: argument out of range
conn.send(stdout)
conn.send(stderr)
except ConnectionResetError:
break
conn.close()
server.close() 上面我们说到了自定义报头的时候struct.pack('i',total_size) 这个方法在传输的数据过大的时候他就会报错,所以并不能完全的解决我们的黏包问题,只适用在我们传输小数据量的时候。
接下来看一下Python的最终解决黏包问题的解决方案:
#Author:BigBao
#Date:2018/7/6
import struct
import json
# obj=struct.pack('i',555565555)
# print(obj,len(obj)) # i 格式打包的个数是有限的
'''
解决黏包问题的主要思想就是:
在我们发送数据流的时候,我们先给对方发送这个数据流的长度\大小,也就是说
我们需要自定一个固定大小的报头,然后发送给对方,这个报头根据我们的需求来定义,
这个报头的内容比如说可以有:数据的长度,文件的名称(数据是文件的情况下)、md5值等等
按照上面的需求,我们一般把报头定义成一个字典格式,但是我们如何把一个字典发送给对方呢,
这个时候我们就要使用json,我们把字典转换成json串,json.dumps我们得到的是一个字符串格式的对象。
我们不能直接发送字符串,所以我们需要给他编码一下 obj.encode('utf-8').
比如说我们看一下下面的例子,
'''
header_dic={
'filename':'xxx.mp4',
'total_size':2342342374829374928374323423423449238749238742938749238749234923648763481256481736491287418375109837512653189237489123751928307,
'md5':'8f6fbf8347faa4924a76856701edb0f3'
}
header_json=json.dumps(header_dic) # 这样我们得到一个json对象,我们可以查看一下这是一个字符串类型
# print(header_json,type(header_json))
header_bytes=header_json.encode('utf-8') # 我们给他编码一下,得到一个bytes的对象
# 这里我们要注意我们能做成报头的大前提是。报头的大小固定,这里我们要是随意修改一下header_dir 中的total_size 的话,
# 这里len(header_bytes)的大小就会发生变化,所以这里我们就要用到struct模块的pack功能了
obj=struct.pack('i',len(header_bytes))
print(obj,len(obj)) # 这里我们发现我们修改字典中的total_size 的话,这里的len(obj) 不会发生变化,我们看到他的大小一直都是4.满足我们的需求 print(struct.unpack('i',obj))
这里我们得到的是一个集合(209,) 所以我们取[0] 位置的数据就是报头的长度
print(struct.unpack('i',obj)[0]) #1、这里我们先把报头的长度发送给对方
#2、然后我们发送报头
#3、最后发送数据
#Author:BigBao
#Date:2018/7/4
import socket
import subprocess
import struct
import json server=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.bind(('127.0.0.1',8012))
server.listen(5)
print('服务端启动')
while True: # 建立连接循环
conn,client_addr=server.accept()
while True: # 建立通信循环
try:
cmd=conn.recv(1024)
if not cmd:break
obj=subprocess.Popen(cmd.decode('utf-8'),shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
stdout=obj.stdout.read()
stderr=obj.stderr.read()
# 制定报头:这里我们目前能用到的是header_dic里的total_size
header_dic={
'total_size':len(stdout)+len(stderr),
'filename':'xxx.mp4',
'md5sum':'8f6fbf8347faa4924a76856701edb0f3'
}
# 发送报头的长度
header_json=json.dumps(header_dic)
header_bytes=header_json.encode('utf-8') conn.send(struct.pack('i',len(header_bytes)))
# 发送报头
conn.send(header_bytes)
# 发送数据
conn.send(stdout)
conn.send(stderr)
except ConnectionResetError:
break
conn.close()
server.close()
#Author:BigBao
#Date:2018/7/4
import socket
import struct
import json
client=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
client.connect(('127.0.0.1',8012))
while True:
cmd=input('>>>: ').strip()
if not cmd:continue
client.send(cmd.encode('utf-8'))
# 我们先接受报头的长度的字节格式
obj=client.recv(4)
header_size=struct.unpack('i',obj)[0] # 这里unpack 我们得到的是一个集合(类似是这样的 (209,) 所以我们应该去第一个位置的值,这个值就是报头的长度),我们得到了报头的长度,然后我们在下面就接收这么长的字节数就可以得到报头对象了 # 接受报头,从上面我们得到了报头的长度,所以我们recv(报头的长度)就可以得到报头对象了
header_bytes=client.recv(header_size)
header_json=header_bytes.decode('utf-8')
header_dic=json.loads(header_json)
total_size=header_dic['total_size'] # 这样我们就得到了字节流的长度了 # 接收真实的数据,直到收干净为止
recv_size=0
res=b''
while recv_size < total_size:
recv_data=client.recv(1024)
res+=recv_data
recv_size+=len(recv_data)
print(res.decode('gbk'))
client.close()
根据老师的文章写的:
http://www.cnblogs.com/linhaifeng/articles/6129246.html#_label12
Python 的黏包问题的更多相关文章
- Python之黏包的解决
黏包的解决方案 发生黏包主要是因为接收者不知道发送者发送内容的长度,因为tcp协议是根据数据流的,计算机操作系统有缓存机制, 所以当出现连续发送或连续接收的时候,发送的长度和接收的长度不匹配的情况下就 ...
- Python之黏包
黏包现象 让我们基于tcp先制作一个远程执行命令的程序(命令ls -l ; lllllll ; pwd) res=subprocess.Popen(cmd.decode('utf-8'), shell ...
- python tcp黏包和struct模块解决方法,大文件传输方法及MD5校验
一.TCP协议 粘包现象 和解决方案 黏包现象让我们基于tcp先制作一个远程执行命令的程序(命令ls -l ; lllllll ; pwd)执行远程命令的模块 需要用到模块subprocess sub ...
- python之黏包和黏包解决方案
黏包现象主要发生在TCP连接, 基于TCP的套接字客户端往服务端上传文件,发送时文件内容是按照一段一段的字节流发送的,在接收方看来,根本不知道该文件的字节流从何处开始,在何处结束. 两种黏包现象: 1 ...
- python中黏包现象
#黏包:发送端发送数据,接收端不知道应如何去接收造成的一种数据混乱现象. #关于分包和黏包: #黏包:发送端发送两个字符串"hello"和"word",接收方却 ...
- Python学习笔记【第十四篇】:Python网络编程二黏包问题、socketserver、验证合法性
TCP/IP网络通讯粘包问题 案例:模拟执行shell命令,服务器返回相应的类容.发送指令的客户端容错率暂无考虑,按照正确的指令发送即可. 服务端代码 # -*- coding: utf- -*- # ...
- python 黏包现象及其解决方案
一.数据缓冲区 缓冲区(buffer),它是内存空间的一部分.也就是说,在内存空间中预留了一定的存储空间,这些存储空间用来缓冲输入或输出的数据,这部分预留的空间就叫做缓冲区,显然缓冲区是具有一定大小的 ...
- Python Socket通信黏包问题分析及解决方法
参考:http://www.cnblogs.com/Eva-J/articles/8244551.html#_label5 1.黏包的表现(以客户端远程操作服务端命令为例) 注:只有在TCP协议通信的 ...
- python学习之socket&黏包
7.4 socket [重要] 避免学习各层的接口,以及协议的使用, socket已经封装好了所有的接口,直接使用这些接口或者方法即可,方便快捷,提升开发效率. socket在python中就是一 ...
随机推荐
- The ECDSA host key for XXX has changed
运行Hadoop时出现了: 导致运行失败.仔细分析后发现,这是因为以前192.168.1.201的主机名为master,后来把192.168.1.202改名为master,由于两台主机的公钥不一样,所 ...
- linux下时间有关的函数和结构体
1.时间类型.Linux下常用的时间类型有6个:time_t,struct timeb, struct timeval,struct timespec,clock_t, struct tm. (1) ...
- px 和 em 的区别
相同点:px和em都是长度单位: 异同点:px的值是固定的,指定是多少就是多少,计算比较容易.em得值不是固定的,并且em会继承父级元素的字体大小.浏览器的默认字体高都是16px.所以未经调整的浏览器 ...
- JavaWeb应用项目中文乱码的解决
转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6383542.html 在把web项目成功部署到云主机后,通过浏览器成功地访问到了你的网站,却发现页面里本该由 ...
- 使用fuel6.0自己主动安装openstack-juno版本号(2)
上篇中fuel_master已经安装完毕.接下来我们介绍怎样新建一个openstack环境. 1,在web界面中新建openstack环境: 点击主界面的加入button,新建openstack环境. ...
- linux tftp 服务
TFTP(Trivial File Transfer Protocol)是基于UDP协议开发,用来在客户机与server之间进行简单文件传输的协议,提供不复杂.开销不大的文件传输服务. 一.改动/et ...
- 【Linux】awk详细介绍
awk简介 awk是一种使用方便且表现力很强的编程语言,它可以应用在多种不同的计算与数据处理任务中.由于awk天生提供对文件中文本分列进行处理,所以如果一个文件中的每行都被特定的分隔符(常见的是空格) ...
- 转 linux Crontab 使用
cron用法说明 cron的用法老是记不住,索性写下来备忘.下文内容大部分是根据<Cron Help Guide>翻译而来,有些部分是自己加上的. 全文如下: cron来源于希腊单词chr ...
- yml转properties
推荐一个在线工具,可以将yaml转换为properties,同时也支持反向转换 http://www.toyaml.com 非常好记的地址,to yaml,直接在地址栏里输入toyaml.com,省去 ...
- 完完全全彻底删除VMware_Workstation
vmware-workstation,卸载不干净.bat脚本自动处理dll.注册的表垃圾 Download https://pan.baidu.com/s/1dmrOs3rkR8cX5b0vTUOxH ...