一、数据缓冲区

  缓冲区(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 黏包现象及其解决方案的更多相关文章

  1. python黏包现象

    #黏包:发送端发送数据,接收端不知道应如何去接收造成的一种数据混乱现象. #关于分包和黏包: #黏包:发送端发送两个字符串"hello"和"word",接收方却 ...

  2. python 黏包现象

    一.黏包 1.tcp有黏包现象 表现两种情况 发送的数据过小且下面还有一个发送数据,这两个数据会一起发送 发送的数据过大,超过最大缓存空间,超出的部分在下一次发送的时候发送 原因: tcp是面向流的, ...

  3. python 全栈开发,Day35(TCP协议 粘包现象 和解决方案)

    一.TCP协议 粘包现象 和解决方案 黏包现象让我们基于tcp先制作一个远程执行命令的程序(命令ls -l ; lllllll ; pwd)执行远程命令的模块 需要用到模块subprocess sub ...

  4. day28 1.缓冲区 2.subprocess 3.黏包现象 4.黏包现象解决方案 5.struct

    1.缓冲区: 输入缓冲区  输出缓冲区 2. subprocess的使用import subprocess sub_obj = subprocess.Popen('ls', #系统指令shell=Tr ...

  5. Python 之网络编程之socket(2)黏包现象和socketserver并发

    一:黏包 ###tcp协议在发送数据时,会出现黏包现象.     (1)数据粘包是因为在客户端/服务器端都会有一个数据缓冲区,     缓冲区用来临时保存数据,为了保证能够完整的接收到数据,因此缓冲区 ...

  6. python中黏包现象

    #黏包:发送端发送数据,接收端不知道应如何去接收造成的一种数据混乱现象. #关于分包和黏包: #黏包:发送端发送两个字符串"hello"和"word",接收方却 ...

  7. Python网络编程基础 ❷ 基于upd的socket服务 TCP黏包现象

    TCP的长连接 基于upd的socket服务 TCP黏包现象

  8. 铁乐学Python_Day34_Socket模块2和黏包现象

    铁乐学Python_Day34_Socket模块2和黏包现象 套接字 套接字是计算机网络数据结构,它体现了C/S结构中"通信端点"的概念. 在任何类型的通信开始之前,网络应用程序必 ...

  9. 黏包现象之TCP

    老师的博客:http://www.cnblogs.com/Eva-J/articles/8244551.html#_label5 server #_*_coding:gbk*_ from socket ...

随机推荐

  1. 【转】JAVA解压.TAR.Z及.ZIP文件

     解压.ZIP文件 package app.qdupr.Method; import java.io.File; import java.io.FileOutputStream; import jav ...

  2. 第一册:lesson ninety one.

    原文:  Poor lan. Has lan sold his house yet? Yes,he has. He sold it last week. Has he moved to his new ...

  3. Webservice开发、引用

    一.Webservice开发 1.在解决方案右键添加新建项目,新建空的web应用程序 2.在新建的项目右键添加新建项选择web服务 3.这里就是webservice 里的方法,可以添加自己需要的方法( ...

  4. CIL中间语言浅谈

    CIL中间语言 通用中间语言(Common Intermediate Language,简称CIL)(曾经被称为微软中间语言或MSIL)是一种属于通用语言架构和.NET框架的低阶(lowest-lev ...

  5. Java学习笔记之——String和Arrays常用方法

    一.String常用方法 1.subString(int beginIndex,int endIndex) 截取字符串 从beginIndex开始截取,截取endIndex-beginIndex的长度 ...

  6. 浅谈spring中AOP以及spring中AOP的注解方式

    AOP(Aspect Oriented Programming):AOP的专业术语是"面向切面编程" 什么是面向切面编程,我的理解就是:在不修改源代码的情况下增强功能.好了,下面在 ...

  7. xpath语法笔记

    xpath是在xml文档中查找信息的语言, 其语法整理如下: 路径表达式(选取节点) div div下的所有子节点 /div 根节点下的div //div 匹配所有的div . 当前节点 .. 父节点 ...

  8. 理解Promise的3种姿势

    译者按: 对于Promise,也许你会用了,却并不理解:也许你理解了,却只可意会不可言传.这篇博客将从3个简单的视角理解Promise,应该对你有所帮助. 原文: Three ways of unde ...

  9. js 两数组去除重复数值

    //两数组去除重复数值 mergeArray: function(arr1, arr2) { for (var i = 0; i < arr1.length; i++) { for (var j ...

  10. Android为TV端助力 遥控器的映射

    第一编写kl文件时先在盒子上输入getevent -v查看设备信息,设备信息里有vendor.product.version, 假如分别是xxxx,yyyy,zzzz,那么你的文件名就要命名为Vend ...