黏包现象、struct模块、并行与并发
1.黏包现象
1.黏包现象产生的背景:
1.1 服务端连续执行三次recv
1.2 客户端连续执行三次send
执行上述操作会发现服务端一次性接收到了客户端三条消息,而后面两次什么都没接收到,该现象称为"黏包现象"。
2.黏包现象产生的原因:
2.1 谁不知道每次的数据到底多大
2.2 TCP也被称为流式协议:数据像流水一样绵绵不绝没有间断,所以无法断开。TCP会针对数据量较小且发送间隔较短的多条数据一次性合并打包发送
3.黏包现象如何避免:
3.1 我们可以控制服务端每次接收字节的数量,只需要在服务端recv()括号内填写上三次分别就饿瘦的字节数量即可。
如果第一次接收的字节数小于第一个发送词汇的字节数,那么第二次打印会从上次断开的地方接着打印
3.2 所以解决黏包现象最核心的问题在于明确即将接收的数据有多大,以及如何将长度变化的数据全部制作成固定长度的数据(多少个字节)
2.struct模块
1.struct模块可以将非固定长度的数字转为固定长度(len=4)的数字,语法结构为:struct.pack('i', len(变量名)),其中'i'是固定的参数,变量名指代的数据值一定要是二进制的。打包的过程也称为'报头'。
import struct
info = b'hello world'
print(len(info)) # 11
'''如果要在服务端接收info的话,只需要将recv后面括号内的数据改为11即可'''
res = struct.pack('i', len(info))
print(len(res)) # 4
desc = b'hello the wonderful world'
print(len(desc)) # 25
res1 = struct.pack('i', len(desc))
print(len((res1))) # 4
2.打包之后需要再解包,来拿到它真是的长度,语法结构为:struct.unpack('i', 变量名)。解压之后的数据类型是一个元祖,其中只有一个元素,拿到长度需要用索引
res_len = struct.unpack('i', res)
print(res_len) # (11,)
res1_len = struct.unpack('i', res1)
print(res1_len) # (25,)
3.思路到这里,我们其实可以初步的解决黏包现象,思路如下:
客户端:
1.将真实数据转成bytes类型并计算长度
2.利用struct模块将真实长度转成一个固定长度的报头
3.将固定长度的报头先发送给服务端,服务端只需要在recv括号内填写固定长度的报头数字即可
4.再发送真实数据
服务端:
1.服务端先接受固定长度的报头
2.利用struct模块反向解析出真实数据长度
3.recc接收真实数据长度即可
"""
单数上述方法并不完美,因为struct模块能打包的数据大小是有限的,换其他模式也无法解决该问题
import struct
res = struct.pack('i', 47564389768937)
print(res) # struct.error: argument out of range
"""
4.报头能否将更多的信息一起传递过去?例如文件名称、大小
黏包问题终极方案:
客户端:
1.制作真实数据的信息字典(数据字典、数据简介、数据名称),并转成二进制类型
2.利用struct模块制作字典的报头
3.发送固定长度的报头
4.发送字典数据
5.发送真实数据
服务端:
1.接收固定长度的字典报头
2.利用struct模块反向解析出字典的长度
3.根据字典真实的长度接收字典数据并处理成字典
4.接收字典的真实数据
3.struct模块实操
需求:不同电脑通过struct模块发送文件
服务端:
import socket
import struct
import json
sever = socket.socket()
sever.bind(('127.0.0.1', 8080))
sever.listen(5)
sock, addr = sever.accept()
# 1.接收固定长度的字典报头
data_dict_head = sock.recv(4)
# 2.根据报头解析出字典数据的长度
data_dict_len = struct.unpack('i', data_dict_head)[0]
# 3.接收字典数据
data_dict_bytes = sock.recv(data_dict_len)
data_dict = json.loads(data_dict_bytes)
# 4.获取真实数据的各项信息
total_size = data_dict.get('file_size')
with open(data_dict.get('file_name'), 'wb')as f:
f.write(sock.recv(total_size))
客户端:
import socket
import os
import json
import struct
client = socket.socket()
client.connect(('127.0.0.1', 8080))
# 1.获取真实数据大小
file_size = os.path.getsize(r'D:\上海python金牌班\20221117 day41 黏包现象、并行与并发\11111.txt')
data_dict = {
'flie_name': '11111.txt',
'file_size': file_size,
'file_desc': '内容很多,请耐心看完',
'file_info': '私人珍藏'
}
# 3.制作字典报头
data_dict_bytes = json.dumps(data_dict).encode('utf8')
data_dict_len = struct.pack('i', len(data_dict_bytes))
# 4.发送字典报头,报头本身也是bytes类型
client.send(data_dict_len)
# 5.发送字典
client.send(data_dict_bytes)
with open(r'D:\上海python金牌班\20221117 day41 黏包现象、并行与并发\11111.txt', 'rb') as f:
for line in f:
client.send(line)
4.UDP协议(了解):
1.UDP服务端和客户端'各自玩各自的',支持多个客户端
2.UDP不会出现多个消息发送合并
服务端代码:
import socket
sever = socket.socket(type=socket.SOCK_DGRAM)
sever.bind(('127.0.0.1', 8081))
while True:
data, addr = sever.recvfrom(1024)
print('客户端地址>>>:', addr)
print('上述地址罚送的消息>>>:', data.decode('utf8'))
msg = input('>>>').strip()
sever.sendto(msg.encode('utf8'), addr)
客户端1:
import socket
client = socket.socket(type=socket.SOCK_DGRAM)
sever_addr = ('127.0.0.1', 8081)
while True:
msg = input('>>>').strip()
client.sendto(msg.encode('utf8'), sever_addr)
data, addr = client.recvfrom(1024)
print(data.decode('utf8'), addr)
客户端2:
import socket
client = socket.socket(type=socket.SOCK_DGRAM)
sever_addr = ('127.0.0.1', 8081)
while True:
msg = input('>>>').strip()
client.sendto(msg.encode('utf8'), sever_addr)
data, addr = client.recvfrom(1024)
print(data.decode('utf8'), addr)
5.并发编程理论
研究网络编程其实就是在研究计算机的底层原理及发展史
'''计算机中真正干活的是CPU'''
计算机系统的发展史:
1.阶段一:穿孔卡片操作:程序员用穿孔卡片将输入传入计算机,该阶段工作方式有两个特点:1.用户独占全机;2.CPU利用率不高
2.阶段二:联机批处理系统(磁带存储):主机与输入机之间增加一个存储设备——磁带,在运行于主机上的监督程序的自动控制下,计算机可自动完成:成批地把输入机上的用户作业读入磁带,依次把磁带上的用户作业读入主机内存并执行并把计算结果向输出机输出。但是效率依旧不高
3.阶段三:脱机批处理系统:主机不是直接与慢速的输入/输出设备打交道,而是与速度相对较快的磁带机发生关系,有效缓解了主机与设备的矛盾。
不足:每次主机内存中仅存放一道作业,每当它运行期间发出输入/输出(I/O)请求后,高速的CPU便处于等待低速的I/O完成状态,致使CPU空闲。为改善CPU的利用率,又引入了多道程序系统。
6.多道程序技术
'''默认一台计算机只有一个CPU'''
1.单道技术:所有的程序排队执行,过程中不能重合
2.多道技术:所谓的多道程序技术,就是指允许多个程序同时进入内存并运行。即同时把多个程序放入内存,并允许它们交替在CPU中运行,它们共享系统中的各种硬、软件资源。当一道程序因I/O请求而暂停运行时,CPU便立即运转去运行另一道程序。
3.多道技术详细:
3.1 CPU在两种情况下会切换(结束此程序,让另一个程序占用CPU)
1 程序有IO操作
'''IO指输入或者输出'''
输入/输出操作:input、time.sleep、write
2 程序长时间占用CPU:雨露均沾,让每个程序都被CPU运行一下
3.2 保存状态:CPU每次切换走之前都需要保存当前的工作状态,下次切换回来基于上次的进度继续执行
"""
相当于一个资本家开了一家饭店,但是只雇了一个服务员,资本家为了利益最大化,让该服务员为第一桌点菜(客人已经想好点什么菜的情况下),点完立即给第二桌倒水,倒完水立即给第三桌上菜,一刻不停歇。
"""
7.进程理论
1.进程与程序的区别:
程序:没有被运行的程序(代码)
进程:正在运行的程序(有CPU才能运行)(程序需要在内存当中才能够被运行,所以进程都在内存中)
"""
在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。同一个程序执行两次,就会在操作系统中出现两个进程,所以我们可以同时运行一个软件,分别做不同的事情也不会混乱。
"""
2.进程的调度算法:
2.1 FCFS:先来先服务。例如有五个程序等待运行,谁先来的就先运行谁。但是对于运行时间较短的程序不友好,例如第一个来的程序耗时较长,第二个程序即使运行时间较短也只能等待第一个程序运行结束之后才能运行。
2.2 短作业优先调度:优先运行时间最短的程序,运行时间长即使来得早也要排队。
2.3 时间片轮转法+多级反馈队列(目前还在用):将时间均分,分给每个程序运行。如果有程序没有运行完,则会让该程序进入下一个队列,下一个队列每次分得的时间会更长,但是优先级越低。比如一个程序在第一层运行时没有运行完,被分到第二层继续运行,但此时如果有一个新的程序来到第一层,那么CPU会优先运行这一个程序,但当CPU再次运行第二层的程序时分得的时间更长,频率也会越低。
8.进程的并行与并发
1.并行:多个进程同时执行,必须要有多个CPU参与,单个CPU无法实现并行。
2.并发:多个进程看上去像是同时执行,单个CPU可以实现,多个CPU肯定也可以。例如CPU在运行一个程序时遇到了IO状态,然后保存状态后去运行另一个程序,看上去是同时执行但其实并没有(联系多道技术)(并发量评估了程序同时服务客户端数量的能力)。
'''可以简单的理解为餐厅有条不紊的运转,实则只有一个服务员'''
"""
判断以下语句正确与否:某程序员写的程序可以实现14亿并行量:错误
并行需要多个CPU,若改为并发则正确
"""
>>>目前国内能执行最高的并发量的软件:12306
9.进程的三状态
1.就绪态:当进程已准备好除CPU以外的所有必要的资源, 只要获得CPU即可执行,这时进程的状态称为就绪态。
2.运行态:当进程已获得CPU,其程序正在内存当中执行,此时的进程状态称为运行态。
3.阻塞态:进程运行过程中出现了IO操作,阻塞态无法直接进入运行态,需要先进入就绪态。
10.实操
需求:将本地视频发送给其他电脑,测试阶段也可用回送地址将视频传到另一个文件夹
客户端:
import os
import json
import socket
import struct
import time
client = socket.socket()
client.connect(('127.0.0.1', 8080))
file_size = os.path.getsize(r'D:\上海滩.mp4')
data_dict = {
'file_name': '上海滩.mp4',
'file_size': file_size,
'file_desc': '内容很多,请耐心看完',
'file_info': '私人珍藏'
}
data_dict_bytes = json.dumps(data_dict).encode('utf8')
data_dict_len = struct.pack('i', len(data_dict_bytes))
client.send(data_dict_len)
client.send(data_dict_bytes)
with open(r'D:\上海滩.mp4', 'rb') as f:
time.sleep(3)
for line in f:
client.send(line)
服务端:
import socket
import struct
import json
sever = socket.socket()
sever.bind(('127.0.0.1', 8080))
sever.listen(5)
sock, addr = sever.accept()
data_dict_head = sock.recv(4)
data_dict_len = struct.unpack('i', data_dict_head)[0]
data_dict_bytes = sock.recv(data_dict_len)
data_dict = json.loads(data_dict_bytes)
total_size = data_dict.get('file_size')
with open(data_dict.get('file_name'), 'wb') as f:
recv_size = 0
while recv_size < total_size:
data = sock.recv(1024)
f.write(data)
recv_size+= len(data)
黏包现象、struct模块、并行与并发的更多相关文章
- python tcp黏包和struct模块解决方法,大文件传输方法及MD5校验
一.TCP协议 粘包现象 和解决方案 黏包现象让我们基于tcp先制作一个远程执行命令的程序(命令ls -l ; lllllll ; pwd)执行远程命令的模块 需要用到模块subprocess sub ...
- python笔记8 socket(TCP) subprocess模块 粘包现象 struct模块 基于UDP的套接字协议
socket 基于tcp协议socket 服务端 import socket phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 买 ...
- 铁乐学Python_Day34_Socket模块2和黏包现象
铁乐学Python_Day34_Socket模块2和黏包现象 套接字 套接字是计算机网络数据结构,它体现了C/S结构中"通信端点"的概念. 在任何类型的通信开始之前,网络应用程序必 ...
- socket套接字模块及黏包现象
一.socket套接字模块 socket概念 socket层 理解socket Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口.在设计模式中,Socket其实就是一个门面模 ...
- Python 之网络编程之socket(2)黏包现象和socketserver并发
一:黏包 ###tcp协议在发送数据时,会出现黏包现象. (1)数据粘包是因为在客户端/服务器端都会有一个数据缓冲区, 缓冲区用来临时保存数据,为了保证能够完整的接收到数据,因此缓冲区 ...
- day28 1.缓冲区 2.subprocess 3.黏包现象 4.黏包现象解决方案 5.struct
1.缓冲区: 输入缓冲区 输出缓冲区 2. subprocess的使用import subprocess sub_obj = subprocess.Popen('ls', #系统指令shell=Tr ...
- 模拟ssh、黏包、hashlib模块
一.模拟ssh 1.subprocess模块 ipconfig -all dir subprocess模块是python从2.4版本开始引入的模块.主要用来取代 一些旧的模块方法,如os.system ...
- python网络编程-socket套接字通信循环-粘包问题-struct模块-02
前置知识 不同计算机程序之间数据的传输 应用程序中的数据都是从程序所在计算机内存中读取的. 内存中的数据是从硬盘读取或者网络传输过来的 不同计算机程序数据传输需要经过七层协议物理连接介质才能到达目标程 ...
- python 黏包现象及其解决方案
一.数据缓冲区 缓冲区(buffer),它是内存空间的一部分.也就是说,在内存空间中预留了一定的存储空间,这些存储空间用来缓冲输入或输出的数据,这部分预留的空间就叫做缓冲区,显然缓冲区是具有一定大小的 ...
- 模拟ssh、黏包、hashlib模块(MD5)
待补充..... 一.模拟ssh 二.黏包 1.黏包现象 让我们基于tcp先制作一个远程执行命令的程序(命令ls -l ; lllllll ; pwd) res=subprocess.Popen(cm ...
随机推荐
- LoadRunner11脚本小技能之同步/异步接口分离+批量替换请求头
最近在公司又进行了一次LoadRunner11性能测试,技能又get了一点,继续Mark起来!!! 一.异步/同步接口分离 之前在另一篇博文中有提到"事务拆分"的小节,即一个htm ...
- SpringBoot 01: JavaConfig + @ImportResource + @PropertyResource
springboot的前置知识:通过注解创建对象和读取配置文件 1. JavaConfig 设计思想 使用java类作为xml配置文件的替代,是配置spring容器的纯java的方式 可以创建java ...
- 使用DOS命令运行JAVA项目
使用DOS命令运行JAVA项目 找到生成项目的文件夹: 在地址前加上cmd+空格,进入命令窗口: 输入javac 类的名称.java,生成class文件: 输入java 类的名称: 运行成功:
- elasticsearch聚合之bucket terms聚合
目录 1. 背景 2. 前置条件 2.1 创建索引 2.2 准备数据 3. 各种聚合 3.1 统计人数最多的2个省 3.1.1 dsl 3.1.2 运行结果 3.2 统计人数最少的2个省 3.2.1 ...
- Ajax基础(中)
这节主要在上节的基础上学会如何使用Ajax 源码下载: 链接:https://pan.baidu.com/s/1kG-vACFxneAZqONdo97XrQ 提取码:k21y 在WebStorm中打开 ...
- hashcat 命令
hashcat --force --stdout -a 6 tmp.txt ?d?d?d?d?d?d?d > result.txt tips:将tmp.txt字典中的内容与7位随机掩码字符组合, ...
- C ++:树
C++:树 树的概念: 所谓"树"是输就结构的一种,树大概可以分为两大类: 有根树 和 无根树 有根树使有一个确定的根节点,反之为无根树 · 子节点:从树根开始,通过树边向下扩展的 ...
- linux学习相关资料整理
linux常用指令记录 Python3.9.9安装 supervisor安装与监控nginx 使用supervisor监控mysql supervisor监控tomcat配置文件 nginx-1.22 ...
- 【每日一题】【第n个 n-->0】19./NC53 【删除】链表的倒数第 N 个结点-211123/220127
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点. 答案: import java.util.*; /* * public class ListNode { * int val; * ...
- [奶奶看了都会]ChatGPT保姆级注册教程
大家好,我是小卷 最近几天OpenAI发布的ChatGPT聊天机器人火出天际了,连着上了各个平台的热搜榜.这个聊天机器人最大的特点是模仿人类说话风格同时回答大量问题. 有人说ChatGPT是真正的人工 ...