python 黏包现象及其解决方案
一、数据缓冲区
缓冲区(buffer),它是内存空间的一部分。也就是说,在内存空间中预留了一定的存储空间,这些存储空间用来缓冲输入或输出的数据,这部分预留的空间就叫做缓冲区,显然缓冲区是具有一定大小的。
二、为什么要缓冲区 (详情参考:https://www.cnblogs.com/mlgjb/p/7991903.html)
1.可以解除高速设备与低速设备的不匹配,高速设备需要等待低速设备的制约关系,数据可以直接送往缓冲区,高速设备不用再等待低速设备,提高了计算机的效率。
2.可以减少数据的读写次数,如果每次数据只传输一点数据,就需要传送很多次,这样会浪费很多时间,因为开始读写与终止读写所需要的时间很长,如果将数据送往缓冲区,待缓冲区满后再进行传送会大大减少读写次数,这样就可以节省很多时间。
三、粘包现象
1、小数据传输粘包
源代码:
import socket
server=socket.socket()
server.bind(('127.0.0.1',8001)) server.listen()
conn,addr=server.accept()
msg_1=conn.recv(1024).decode("utf-8")
msg_2=conn.recv(1024).decode("utf-8")
print("客户端>>>",msg_1)
print("客户端>>>",msg_2)
conn.close()
server.close()
黏包现象服务端
import socket
import time
client=socket.socket()
client.connect(('127.0.0.1', 8001))
client.send(b'')
# time.sleep(0.1) 加入时间缓冲,让每次传入和接受的数据有序
client.send(b'')
client.close()
黏包现象客户端
黏包现象粗略的解释:数据在传输过程中,未来得及按照先后次序传输和接受,数据都进入了数据缓冲区中,再接受的数据时候,不知道按照怎么的数据长度接受,就按照给定的1024的长度接收,因此出现了黏包.
解决方案:
方案一、传输过程中每次都告诉对方应该如何接收数据。
方案二、把传输和接受的间隔加大,保证每次都能顺利的满足一个接着一个传输。(time.sleep(0.1))
2、大数据传输粘包(模拟cmd指令)
源代码:
import subprocess
import socket
import time
server=socket.socket()#创建socket对象 server.bind(('127.0.0.1',8001))#绑定ip_port
server.listen()#监听
conn,addr=server.accept()#等待连接,获取连接通道和地址 while 1:
time.sleep(0.1)#减少内存占用
cmd_msg=conn.recv(1024).decode("utf-8")
#判断是否结束doc指令读取
if cmd_msg=="exit" or cmd_msg=="exit()":
break
else:
#创建读取cmd指令对象
obj_sub=subprocess.Popen(
cmd_msg,
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
out_info=obj_sub.stdout.read()
err_info=obj_sub.stderr.read() if err_info:
#不存在该命令则输出错误信息,并打印字节数
conn.send(err_info)
print(len(err_info))
else:
#存在输入的命令则发送指令执行的结果,并打印字节数
conn.send(out_info)
print(len(out_info)) conn.close()
server.close()
大数据黏包现象服务器
import socket
client=socket.socket()#创建客户端对象
client.connect(('127.0.0.1', 8001))#连接服务器的ip_port while 1: cmd=input("cmd>>>")
client.send(cmd.encode("utf-8"))
#判断是否为退出指令
if cmd=="exit" or cmd=="exit()":
break
else:
# 接受客服端返回信息
out=client.recv(1024).decode("gbk")
print(out) client.close()
大数据黏包现象客户端
结果显示:
C:\Python36\python.exe "F:/qishi/day 28 黏包 合法性链接/黏包现象/黏包现象2客户端.py"
cmd>>>ipconfig -all Windows IP 配置 主机名 . . . . . . . . . . . . . : DESKTOP-MT7JLPA
主 DNS 后缀 . . . . . . . . . . . :
节点类型 . . . . . . . . . . . . : 混合
IP 路由已启用 . . . . . . . . . . : 否
WINS 代理已启用 . . . . . . . . . : 否 无线局域网适配器 WLAN: 媒体状态 . . . . . . . . . . . . : 媒体已断开连接
连接特定的 DNS 后缀 . . . . . . . :
描述. . . . . . . . . . . . . . . : Qualcomm Atheros AR9485WB-EG Wireless Network Adapter
物理地址. . . . . . . . . . . . . : BC-30-7D-96-2D-2B
DHCP 已启用 . . . . . . . . . . . : 是
自动配置已启用. . . . . . . . . . : 是 无线局域网适配器 本地连接* 1: 媒体状态 . . . . . . . . . . . . : 媒体已断开连接
连接特定的 DNS 后缀 . . . . . . . :
描述. . . . . . . . . . . . . . . : Microsoft Wi-Fi Direct Virtual Adapter
物理地址. . . . . . . . . . . . . : 1E-30-7D-96-2D-2B
DHCP 已启用 . . . . . . . . . . . : 是
自动配置已启用. . . . . . . . . . : 是 以太网适配器 以太网: 连接特定的 DNS 后缀 . . . . .
cmd>>>dir
. . :
描述. . . . . . . . . . . . . . . : Realtek PCIe GBE Family Controller
物理地址. . . . . . . . . . . . . : C4-54-44-F5-84-6A
DHCP 已启用 . . . . . . . . . . . : 是
自动配置已启用. . . . . . . . . . : 是
本地链接 IPv6 地址. . . . . . . . : fe80::64a8:6af:a71b:edad%5(首选)
IPv4 地址 . . . . . . . . . . . . : 192.168.12.51(首选)
子网掩码 . . . . . . . . . . . . : 255.255.255.0
获得租约的时间 . . . . . . . . . : 2018年11月26日 15:01:29
租约过期的时间 . . . . . . . . . : 2018年11月27日 15:01:28
默认网关. . . . . . . . . . . . . : 192.168.12.254
DHCP 服务器 . . . . . . . . . . . : 192.168.12.254
DHCPv6 IAID . . . . . . . . . . . : 63198276
DHCPv6 客户端 DUID . . . . . . . : 00-01-00-01-23-7C-84-72-C4-54-44-F5-84-6A
DNS 服务器 . . . . . . . . . . . : 114.114.114.114
TCPIP 上的 NetBIOS . . . . . . . : 已启用 cmd>>>
客户端显示结果
C:\Python36\python.exe "F:/qishi/day 28 黏包 合法性链接/黏包现象/黏包现象2服务器.py"
ipconfig -all 命令执行结果长度 1912
dir 命令执行结果长度 505
服务端显示结果
现象简述:首先执行了一次ipconfig -all,他的结果数据长度是1912,而我们接受的数据长度是1024,之后又执行了一次dir,dir结果长度是505,我们再一次接受的数据,依旧是ipconfig -all 结果数据.出现了黏包现象.
黏包现象成因:执行的数据进入缓冲区,并且数据的大小大于接受数据的大小,因此我们一次接受只能接受1024个字节,之后又执行一个命令后,命令的结果又会进入数据缓冲区,所有我们接受的数据显示的是上一个命令残余的数据.我们每次接受的数据都是1024,还有可能出现多命令的数据同时出现的风险.
解决方案:
方案一、按照发送数据的长度接受数据,由于缓冲区数据容量有限,我们采用循环接受数据的方法接受大数据。
方案二、把数据的长度信息与数据信息合并一次性发给接受端,接收端先提出数据长度,再按照数据长度接受数据。
方案一源代码:
import subprocess
import socket
import time
server=socket.socket()#创建socket对象 server.bind(('127.0.0.1',8001))#绑定ip_port
server.listen()#监听
conn,addr=server.accept()#等待连接,获取连接通道和地址 while 1:
time.sleep(0.1)#减少内存占用
cmd_msg=conn.recv(1024).decode("utf-8")
#判断是否结束doc指令读取
if cmd_msg=="exit" or cmd_msg=="exit()":
break
else:
#创建读取cmd指令对象
obj_sub=subprocess.Popen(
cmd_msg,
shell=True,
stdout=subprocess.PIPE,#标准化输出
stderr=subprocess.PIPE #标准化错误输出
)
out_info=obj_sub.stdout.read() err_info=obj_sub.stderr.read() if err_info:
#不存在该命令则输出错误信息,并打印字节数
data_len = len(err_info)
all_send_datalen=0
print(f"{cmd_msg} 命令执行结果长度", data_len)
#把数据长度发送给接收端
conn.send(str(data_len).encode("utf-8"))
while all_send_datalen<data_len:#当发送的数据小于数据总长就不断的发送
#递增式改变截取位置
every_send_data=err_info[all_send_datalen:all_send_datalen+1024]
conn.send(err_info)
all_send_datalen+=len(every_send_data) else:
#存在输入的命令则发送指令执行的结果,并打印字节数
data_len = len(out_info) all_send_datalen = 0
print(f"{cmd_msg} 命令执行结果长度:", data_len) # 把数据长度发送给接收端
conn.send(str(data_len).encode("utf-8"))
while all_send_datalen < data_len: # 当发送的数据小于数据总长就不断的放送
# 递增式改变截取位置
#每1024个字节发一次
every_send_data = out_info[all_send_datalen:all_send_datalen + 1024]
conn.send(every_send_data) all_send_datalen += len(every_send_data) conn.close()
server.close()
大数据黏包服务器
import socket
client=socket.socket()#创建客户端对象
client.connect(('127.0.0.1', 8001))#连接服务器的ip_port while 1: cmd=input("cmd>>>")
client.send(cmd.encode("utf-8"))
#判断是否为退出指令
if cmd=="exit" or cmd=="exit()":
break
else:
# 客客户端接受返回信息
data_len=client.recv(1024).decode("utf-8")
int_data_len=int(data_len)
print(int_data_len)
#接受的字节个数,计数比较
all_recv_datalen=0
# 用于接收到的字节拼接
all_data=b''
#循环接受数据
while all_recv_datalen<int_data_len:
# 每1024个字节接收一次
every_recv_data=client.recv(1024)
all_recv_datalen+=len(every_recv_data)
all_data += every_recv_data
# 输出打印
print(all_data.decode("gbk")) client.close()
大数据黏包客户端
方案二struct打包:
struct操作简介
import struct
#打包pack
#struct.pack(格式,数据)
# a=231546789
# b=struct.pack("i",a)
# print(b)#b'\xa5\x1f\xcd\r' #解包unpack,结果是元祖
#struct.unpack(格式,数据)
c=struct.unpack("i",b'\xa5\x1f\xcd\r')
print(c) #(231546789,)
a=c[0]
print(a)#
源码:
import socket
import subprocess
import struct
server = socket.socket()
ip_port = ('127.0.0.1',8001)
data_full_len = 0 #统计发送数据的长度
server.bind(ip_port)
server.listen()
conn,addr = server.accept()
while 1:
from_client_cmd = conn.recv(1024).decode('utf-8') sub_obj = subprocess.Popen(
from_client_cmd,
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
#subprocess对象.read 得到的结果是bytes类型的
cmd_res = sub_obj.stdout.read()
data_len = len(cmd_res) #总数据长度
data_len_str = str(data_len)
print('结果长度>>>',data_len) #将真实数据长度打包成4个字节的数据
struct_data_len = struct.pack('i',data_len) conn.send(struct_data_len + cmd_res)
大数据黏包struct服务器
import json
import socket
import struct
client = socket.socket()
ip_port = ('127.0.0.1',8001)
client.connect(ip_port)
all_recv_len = 0
all_data_byte = b'' while 1:
client_cmd = input('请输入系统指令>>>')
client.send(client_cmd.encode('utf-8'))
#先接收4个字节,这4个字节是真实数据长度加工成的
recv_data_len = client.recv(4)
#将4个字节长度的数据,解包成后面真实数据的长度
real_data_len = struct.unpack('i',recv_data_len)[0] print(real_data_len) server_result = client.recv(real_data_len) print(server_result.decode('gbk'))
大数据黏包struct客户端
加强版:
import struct
import socket
import subprocess
import time server=socket.socket()
server.bind(("127.0.0.1",8001))
server.listen()
conn,addr=server.accept()
while 1:
time.sleep(0.1)
cmd_msg=conn.recv(1024).decode("utf-8")
obj_sub=subprocess.Popen(
cmd_msg,
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
cmd_out=obj_sub.stdout.read()
cmd_erro=obj_sub.stderr.read()
if obj_sub.stdout:
out_len=len(cmd_out)
print(cmd_msg+"\t"+str(out_len))
b_out_len=struct.pack("i",out_len)
conn.send(b_out_len+cmd_out) else:
err_len = len(cmd_erro)
int(cmd_msg + "\t" + str(err_len))
b_err_len = struct.pack("i", err_len)
conn.send(b_err_len+cmd_erro)
conn.close()
server.close()
完整版服务器
import struct
import socket
import subprocess
import time client=socket.socket()
client.connect(("127.0.0.1", 8001))
while 1:
time.sleep(0.1)
cmd=input("cmd>>>").encode("utf-8")
client.send(cmd)
#接受数据
data_len_pack=client.recv(4)
data_len=struct.unpack("i",data_len_pack)[0]
print(data_len)
data=client.recv(data_len).decode("gbk")
print(data)
client.close()
完整版客服端
python 黏包现象及其解决方案的更多相关文章
- python黏包现象
#黏包:发送端发送数据,接收端不知道应如何去接收造成的一种数据混乱现象. #关于分包和黏包: #黏包:发送端发送两个字符串"hello"和"word",接收方却 ...
- python 黏包现象
一.黏包 1.tcp有黏包现象 表现两种情况 发送的数据过小且下面还有一个发送数据,这两个数据会一起发送 发送的数据过大,超过最大缓存空间,超出的部分在下一次发送的时候发送 原因: tcp是面向流的, ...
- python 全栈开发,Day35(TCP协议 粘包现象 和解决方案)
一.TCP协议 粘包现象 和解决方案 黏包现象让我们基于tcp先制作一个远程执行命令的程序(命令ls -l ; lllllll ; pwd)执行远程命令的模块 需要用到模块subprocess sub ...
- day28 1.缓冲区 2.subprocess 3.黏包现象 4.黏包现象解决方案 5.struct
1.缓冲区: 输入缓冲区 输出缓冲区 2. subprocess的使用import subprocess sub_obj = subprocess.Popen('ls', #系统指令shell=Tr ...
- Python 之网络编程之socket(2)黏包现象和socketserver并发
一:黏包 ###tcp协议在发送数据时,会出现黏包现象. (1)数据粘包是因为在客户端/服务器端都会有一个数据缓冲区, 缓冲区用来临时保存数据,为了保证能够完整的接收到数据,因此缓冲区 ...
- python中黏包现象
#黏包:发送端发送数据,接收端不知道应如何去接收造成的一种数据混乱现象. #关于分包和黏包: #黏包:发送端发送两个字符串"hello"和"word",接收方却 ...
- Python网络编程基础 ❷ 基于upd的socket服务 TCP黏包现象
TCP的长连接 基于upd的socket服务 TCP黏包现象
- 铁乐学Python_Day34_Socket模块2和黏包现象
铁乐学Python_Day34_Socket模块2和黏包现象 套接字 套接字是计算机网络数据结构,它体现了C/S结构中"通信端点"的概念. 在任何类型的通信开始之前,网络应用程序必 ...
- 黏包现象之TCP
老师的博客:http://www.cnblogs.com/Eva-J/articles/8244551.html#_label5 server #_*_coding:gbk*_ from socket ...
随机推荐
- [转]图解Docker容器和镜像
本文转自:https://www.cnblogs.com/wangqiaomei/p/5818636.html 图解Docker容器和镜像 这篇文章希望能够帮助读者深入理解Docker的命令,还有容器 ...
- Python正则进阶
目录 1.Python正则表达式模块 1.1 正则表达式处理字符串主要有四大功能 1.2 Python中re模块使用正则表达式的两种方法 1.3 正则表达式对象的常用方法 1.4 匹配对象的属性与方法 ...
- Chrome插件开发,美化网页上的文件列表。chrome-extension,background
上一篇文章 通过“content-scripts”的方式向页面注入js和css来美化页面,但是有一个弊端:一旦配置好需要注入的页面,之后如果这个页面地址以后发生变化,或者要新加一些URL进来,那么得修 ...
- c# 大批量用户访问数据库报错
报错信息:There is already an open DataReader associated with this Connection which must be closed first ...
- C#通过窗体属性缩小一定尺寸时,无法再缩小窗体尺寸问题
问题:通过窗体属性缩小窗体尺寸时,发现改变到一定大小时无法再缩小.条件:在代码中设置窗体的尺寸由窗体属性里设置的宽高决定,但实际通过窗体属性设置窗体大小,一定大小时无法再缩小,那是因为我们没有把窗体属 ...
- [日常]总结2016年7月入职至2016年7月26号微盘所遇bug
2016年刚入职后在新浪微盘项目上所遇到的问题: 1.前端接口的程序不同版本问题,版本号在程序路径中区分,比如2.4.2/lib/sdk/api/weipan/Client.php 2.文件夹接口的m ...
- JSJ—编译器与虚拟机哪个重要?
阅读本文约“2分钟” 熟悉Java的朋友都知道虚拟机还有编译器,那么它们各自主要的功能是什么?谁比较重要呢?让我们来了解一下这两位美女的故事. 虚拟机可以说就是Java,她能让程序运行起来. 但是编译 ...
- mybatis笔记02
目录 0. 文章目录 1. Mybatis映射文件 1.1 输入映射 1.2 输出映射 1.3 resultMap 2. 动态SQL 2.1 if和where 2.2 foreach循环 2.3 sq ...
- SSM 设置静态资源处理
使用weblogic的虚拟路径virtual-directory-mapping 在页面上展示文件服务器上的图片 <weblogic-web-app> ......省略其他部分 <c ...
- 小tips:JS之浅拷贝与深拷贝
浅拷贝: function extendCopy(p) { var c = {}; for (var i in p) { c[i] = p[i]; } return c; } 深拷贝: functio ...