python网络编程2-黏包问题

一、复习

# ip地址:一台机器在网络上的位置
# 公网ip 私网ip
# TCP协议:可靠,面向连接的,耗时长#三次握手#四次挥手
# UDP协议:不可靠,无连接,效率高
# ARP协议:通过ip找mac的过程
# ip协议属于网络osi中的网络层
# TCP协议和UDP协议属于传输层
# arp协议属于数据链路层

二、黏包(第一条和第二条数据合并发送)

tcp:不会丢包会黏包

udp:会丢包不会黏包

tcp黏包案例:

server_tcp端:

#server端
import socket
sk=socket.socket()
sk.bind(('127.0.0.1',8090))
sk.listen()conn,addr=sk.accept()
while True:cmd=input('>>>')conn.send(cmd.encode('utf-8'))ret1=conn.recv(1024).decode('utf-8')ret2 = conn.recv(1024).decode('utf-8')print(ret1)print(ret2)conn.close()
sk.close()

client_tcp端:

#client端
import socket
import subprocess
sk=socket.socket()sk.connect(('127.0.0.1',8090))
while True:cmd=sk.recv(1024).decode('utf-8')ret=subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)std_out='stdout:'+(ret.stdout.read()).decode('gbk')std_err='stderr:'+(ret.stderr.read()).decode('gbk')sk.send(std_out.encode('utf-8'))sk.send(std_err.encode('utf-8'))
sk.close()

运行结果:

udp丢包案例:(udp起server和client)

server_udp端:


import socket
sk = socket.socket(type=socket.SOCK_DGRAM)
ip_port = ('127.0.0.1',8090)
sk.bind(ip_port)
msg, addr = sk.recvfrom(10240)
while True:cmd = input('>>>')if cmd == 'q':breaksk.sendto(cmd.encode('utf-8'),addr)msg1,addr = sk.recvfrom(20480)print(msg1.decode('utf-8'))msg2, addr = sk.recvfrom(20480)print(msg2.decode('utf-8'))
sk.close()

client_udp端:

import socket
import subprocess
sk = socket.socket(type=socket.SOCK_DGRAM)
ip_port = ('127.0.0.1',8090)
sk.sendto(b'hi',ip_port)
while True:cmd,addr = sk.recvfrom(1024)cmd = cmd.decode('utf-8')ret = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)std_out = 'stdout:'+(ret.stdout.read()).decode('gbk')std_err = 'stderr:'+(ret.stderr.read()).decode('gbk')print(std_out)print(std_err)sk.sendto(std_out.encode('utf-8'),addr)sk.sendto(std_err.encode('utf-8'),addr)
sk.close() #不会黏包,会丢包

运行结果:

运行结果显示,如果udp的接收数据量大小满足发送数据量大小,那么就不会丢包,若是不满足发送数据量大小,则就会报错。而不是丢包

三 、黏包的触发

情况一:发送方的缓存机制:发送端要等缓冲区满才发送出去。造成黏包(发送数据时间间隔很短,数据很小,会合并一起造成粘包)

如:

#server端
import socket
sk=socket.socket()
sk.bind(('127.0.0.1',8080))
sk.listen()
conn,addr=sk.accept()
ret=conn.recv(12)
ret2=conn.recv(12)
print(ret)
print(ret2)
conn.close()
sk.close()#关闭时会发空消息
# 多个send小的数据连在一起,可能会发生黏包现象,是tcp内部的优化算法引起的
#client端
import socket
sk=socket.socket()
sk.connect(('127.0.0.1',8080))
sk.send(b'hello')
import time
# time.sleep(0.01)
sk.send(b'world')
sk.send(b'dd')
sk.close()

运行结果:(有时会黏包有时不会)

情况二:接收方的缓冲机制

接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只接收了一小部分,服务器下次再接收时还是从缓冲区拿上次遗留的数据,产生粘包)

如:

# server端
import socket
sk=socket.socket()
sk.bind(('127.0.0.1',8080))
sk.listen()conn,addr=sk.accept()
ret=conn.recv(2)
ret2=conn.recv(10)
print(ret)
print(ret2)
conn.close()
sk.close()# 黏包的本职问题:不知道发送数据的长度
#  连续的小数据包会被合并
# client端
import socket
sk=socket.socket()
sk.connect(('127.0.0.1',8080))sk.send(b'hello,egg')
sk.close()

运行结果:

总结

黏包现象只发生在tcp协议中:

1.从表面上看,黏包问题主要是因为发送方和接收方的缓存机制、tcp协议面向流通信的特点。

2.实际上,主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的

四、黏包的解决方案

解决方案一:

问题的根源在于,接收端不知道发送端将要传送的字节流的长度,所以解决粘包的方法就是围绕,如何让发送端在发送数据前,把自己将要发送的字节流总大小让接收端知晓,然后接收端来一个死循环接收完所有数据。

# server
# server 下发命令 给client
import socket
sk=socket.socket()
sk.bind(('127.0.0.1',8080))
sk.listen()
conn,addr=sk.accept()
while True:cmd=input(">>>")if cmd=='q':conn.send(b'q')breakconn.send(cmd.encode('gbk'))num=conn.recv(1024).decode('utf-8')conn.send(b'ok')res=conn.recv(int(num)).decode('gbk')print(res)
conn.close()
sk.close()
# client
# 接收server端的命令后在自己的机器上执行
import socket
import subprocesssk=socket.socket()
sk.connect(('127.0.0.1',8080))
while True:cmd=sk.recv(1024).decode('gbk')if cmd=='q':breakres=subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)std_out=res.stdout.read()std_err=res.stderr.read()sk.send(str((len(std_out)+len(std_err))).encode('utf-8'))sk.recv(1024)  #oksk.send(std_out)sk.send(std_err)
sk.close()# 好处:确定我到底要接收多大的数据
# recv的大小一般不超过4096
# 要在文件中配置一个配置项:就是每次recv的大小 buffer=4096
# 当我们发送大数据量的时候,要明确的告诉接收方要发送多大数据以便接收方能准确接收到所有数据
# 多用在文件传输过程中#大文件的传输一定是按照字节读 每次读固定的字节# 传输的过程中 一边读一边传 接收端:一边收一边写# send大文件之前,告知大小。大小-4096-4096....-->0  文件传输完# recv大文件,先接受大小。再recv2048.不会丢 大小-2048-2048  -->0  文件接收完
# 不好的地方:多了一次交互
# 5个g数据
# send 和sendto在超过一定范围后,都会报错

运行结果:

存在的问题: 程序的运行速度远快于网络传输速度,所以在发送一段字节前,先用send去发送该字节流长度,这种方式会放大网络延迟带来的性能损耗

解决方案进阶

刚刚的方法,问题在于我们我们在发送

我们可以借助一个模块,这个模块可以把要发送的数据长度转换成固定长度的字节。这样客户端每次接收消息之前只要先接受这个固定长度字节的内容看一看接下来要接收的信息大小,那么最终接受的数据只要达到这个值就停止,就能刚好不多不少的接收完整的数据了。

struct模块

该模块可以把一个类型,如数字,转成固定长度的bytes

>>> struct.pack('i',1111111111111)struct.error: 'i' format requires -2147483648 <= number <= 2147483647 #这个是范围

# server 下发命令 给client
import socket
import struct
sk=socket.socket()
sk.bind(('127.0.0.1',8080))
sk.listen()
conn,addr=sk.accept()
while True:cmd=input(">>>")if cmd=='q':conn.send(b'q')breakconn.send(cmd.encode('gbk'))num=conn.recv(4) #4  2048num = struct.unpack('i', num)[0]res=conn.recv(int(num)).decode('gbk')  #2048print(res)
conn.close()
sk.close()
# 连续send两个小数据
# 两个recv,第一个recv特别小
# 远程执行命令的程序:ipconfig--> 2000,只接收1024.就会缓存,下次继续接收上次未接收完的数据#连续send两个小数据2+8=10
# 2
# 8
#两个recv,第一个recv特别小
# recv(数据的长度)
# 接收server端的命令后在自己的机器上执行
import socket
import subprocess
import structsk=socket.socket()
sk.connect(('127.0.0.1',8080))
while True:cmd=sk.recv(1024).decode('gbk')if cmd=='q':breakres=subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)std_out=res.stdout.read()std_err=res.stderr.read()len_num=len(std_out)+len(std_err)num_by=struct.pack('i',len_num)sk.send(num_by)  # 4  2048sk.send(std_out)  # 1024sk.send(std_err)   #1024
sk.close()

运行结果:

五、ftp发送视频

sever:

import socket
import struct
import json
buffer=1024
# ip地址和端口号需要写在配置文件中
sk=socket.socket()
sk.bind(('127.0.0.1',8080))
sk.listen()conn,addr=sk.accept()
# 接收
head_len=conn.recv(4)   # 报头长度
head_len=struct.unpack('i',head_len)[0]
head_json=conn.recv(head_len).decode('utf-8')
head=json.loads(head_json)
filesize=head['filesize']
with open(head['filename'],'wb') as f:while filesize:print(filesize)if filesize>=buffer:content=conn.recv(buffer)f.write(content)filesize-=bufferelse:content=conn.recv(filesize)f.write(content)filesize=0break
f.close()
conn.close()
sk.close()
#发送端
import socket
import os
import json
import struct
sk=socket.socket()
sk.connect(('127.0.0.1',8080))
# 发送文件
buffer=2046
# 改成4096文件大小会变:发送和接收时间不匹配
# 读操作快,写速度慢。缓冲数据多head={'filepath':r'E:\test','filename': '[反贪风暴3]BD国语.mp4','filesize':None}
file_path=os.path.join(head['filepath'],head['filename'])
filesize=os.path.getsize(file_path)
head['filesize']=filesize
json_head=json.dumps(head) #字典转成了字符串
byte_head=json_head.encode('utf-8')  # 字符串转成了bytes
head_len=len(byte_head)   # 报头的长度
pack_len=struct.pack('i',head_len)
sk.send(pack_len)  # 先发报头的长度
sk.send(byte_head)  # 再发送bytes类型的报头
with open(file_path,'rb') as f:while filesize:print(filesize)if filesize>=buffer:content=f.read(buffer)   # 每次读取出来的大小sk.send(content)filesize-=bufferelse:content=f.read(filesize)sk.send(content)filesize=0break
f.close()
sk.close()

参考自https://www.cnblogs.com/Eva-J/articles/8244551.html#_label5

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/568338.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Python实战】chinesecalendar模块处理中国股市交易日期

我们知道中国上海证券交易所和深圳证券交易所日常交易日期为星期一到星期五&#xff0c;并且法定节假日不交易。 我们可以通过chinesecalendar模块判断某一天是否为工作日&#xff08;中国法定工作日&#xff0c;调休的时候有可能周末也是工作日&#xff09;&#xff0c;通过da…

【Git】码云Gitee.com管理项目全流程

Gitee.com 是 OSCHINA.NET 推出的代码托管平台,支持 Git 和 SVN,提供免费的私有仓库托管。目前已有超过 500 万的开发者选择 Gitee。 官网&#xff1a;https://gitee.com/ 为了在Gitee.com上管理项目&#xff0c;首先需要在上面注册用户、设置SSH公钥&#xff0c;下面简单介绍一…

【Jmeter篇】jmeter+Ant+Jenkins实现自动化测试集成(一)

一.简介 1、什么是ant? ant是构建工具&#xff0c;把代码从某个地方拿来&#xff0c;编译&#xff0c;再拷贝到某个地方去等等操作 JMeterAnt是比较常见的自动化测试框架&#xff0c;因为JMeter、Ant都是由java开发的&#xff0c;所以此性能测试框架具有良好的跨平台性&am…

python网络编程3-socketserver模块

一、复习 # 解决黏包问题 #为什么会出现黏包问题# 首先只有在TCP协议中才会出现黏包现象# 是因为TCP协议是面向流的协议# 在发送的数据传输过程中有缓存机制来避免数据丢失# 因此在连续发送小数据的时候&#xff0c;以及接收大小不符的时候都容易出现尿包现象# 本质还是因为我…

【测试】测试开发成长学习路线--引导篇

转载自&#xff1a;https://blog.csdn.net/u011541946/article/details/73382294 我相信&#xff0c;有很多测试人员会不断问自己&#xff0c;自己到底要不要坚持做测试&#xff0c;测试的职业发展到底怎么样&#xff1f;如果你还在迷茫&#xff0c;在到处找各种大牛问类似的问…

【Jmeter篇】jmeter Ant Jenkins接口自动化测试集成之半路逆转(二)

一、更改方案 由于上一篇文章【Jmeter篇】jmeterAntJenkins实现自动化测试集成&#xff08;一&#xff09;https://blog.csdn.net/qq_36502272/article/details/102682966中build.xml在cmd下执行ant正常且生成jtl文件和html文件&#xff0c;但是在jenkins构建多次一直失败&…

pycharm无法导入本地模块问题

导入语句&#xff1a; 办法一 根本不起作用 办法二&#xff1a; 在python安装位置的D:\usedtool\python\Lib\site-packages下建立一个.pth文件 里面把项目地址写上&#xff1a; 最后运行程序。虽然有红线但是运行无误。。搞定

【Jmeter篇】jmeter+Ant+Jenkins接口自动化测试集成之报告优化(三)

增加更加详细测试报告及优化jenkins测试报告样式显示 1、下载优化模板 jmeter-results-shanhe-me.xsl&#xff0c;拷贝到jmeter的C:\jmeter\apache-jmeter-5.0\extras目录中 2、设置测试输出报告要输出的内容&#xff1a;同样在jmeter.properties中&#xff0c;设置需要输出的…

3333

一、安装Email Ext Recipients Column Plugin和Email Extension Plugin插件 二、设置QQ邮箱授权码 https://service.mail.qq.com/cgi-bin/help?subtype1&&id28&&no1001256 三、系统管理-系统设置 1、Jenkins Location设置主机名和系统管理员邮件地址 2、Ex…

【JMeter】Threads(users)3种类型

线程&#xff08;用户&#xff09;Threads(Users) &#xff1a;线程组元件是任何一个测试计划的开始点。在一个测试计划中的所有元件都必须在某个线程下。所有的任务都是基于线程组。 有三种类型&#xff1a;分别代表测试准备工作&#xff0c;测试执行&#xff0c;测试完成后操…

python __new__中单例的作用

__new__() 方法的特性&#xff1a; __new__() 方法是在类准备将自身实例化时调用。__new__() 方法始终都是类的静态方法&#xff0c;即使没有被加上静态方法装饰器。类的实例化和它的构造方法通常都是这个样子&#xff1a; class MyClass(object):def __init__(self, *args, *…

【JMeter】Thread Group下的组件Sampler取样器

我们创建了线程组Thread Group后&#xff0c;会在它下面add组件Sampler取样器。 1. 取样器用来向服务器发送请求&#xff0c;记录响应信息&#xff0c;响应时间的最小单元。 2. 用监听器可以查看取样结果&#xff0c;也可以结合断言进一步验证响应接口是否符合预期。 3. 取样器…

python并发编程1-进程

主进程和子进程 运行结果&#xff1a; 一旦进程创建后&#xff0c;进程就由操作系统调度 代码解析&#xff1a; 子进程与父进程 所以主进程的父进程就是pycharm args传参 一个参数 两个参数 join作用&#xff08;创建多个线程&#xff09;&#xff1a; 不一定哪个进程先执行…

python并发编程2-进程

一、信号量 # 多进程中的组件 # ktv # 4个 # 一套资源 同一时间 只能被n个人访问 # 某一段代码 同一时间 只能被n个进程执行from multiprocessing import Process,Semaphore import time import random def ktv(i,sem):sem.acquire()print(%s走进ktv %i)time.sleep(random.ran…

Python Pytest装饰器@pytest.mark.parametrize用例数据驱动(三)

一、测试用例用excel管理&#xff0c;存放地址&#xff1a;C:\Users\wangli\PycharmProjects\Test\test\files\apiCase.xls 二、代码实现如下&#xff1a; 1、封装读取excel用例数据 2、Pytest装饰器pytest.mark.parametrize(参数名,list)实现登录模块2条测试用例数据驱动 im…

python并发编程3-进程

复习&#xff1a; # 锁 # 多个进程在同一时间只有一个进程能进入代码去执行# 信号量 Semaphore from multiprocessing import Semaphore # 用锁的原理实现的。内置了一个计数器 #在同一时间 只能有指定数量的进程执行某一段被控制住的代码#事件 # wait阻塞收到事件状态控制的同…

python并发编程4-线程

进程的出现 原来一台服务器只能执行一个任务。 进程的出现&#xff0c;可以让一台服务器处理多个任务。多个任务间进行切换&#xff0c;记录每个任务当前执行到哪里&#xff0c;记录有哪些数据。然后进行切换 每个进程区分开每个任务所能占有的内存空间 进程的缺点 线程的出现…

【Fiddler篇】FreeHttp无限篡改http报文数据调试和mock服务

目录 引言 FreeHttp起源 FreeHttp 插件安装FreeHttp 基本界面一&#xff1a;规则匹配区 1.1&#xff1a;『get http sesion in left session list』获取Session信息1.2&#xff1a;『select url filter method』Url匹配方式1.3&#xff1a;『edit advanced http filter』高级匹…

echarts实现双Y轴之散点和折线图

代码&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta charset"utf-8"><title></title><script src"echarts.js"></script> </head><body><div id"box" st…

python列表对应元素合并为列表及判断一个列表是几维

一、合并对应元素 1、两个列表合并 a[1,2,3,4,5] b[2,3,4,5,6] d[] for i in range(len(a)):c []c.append(a[i])c.append(b[i])d.append(c) print(d) 运行结果&#xff1a; 2、一个列表垂直合并 3、一个列表顺序合并 date[] date_temp1[1545225954.721;1545225955.115, …