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)

最新文章

  1. unity播放视频
  2. HDU 1166 敌兵布阵(分块)
  3. nhibernate 配置nvarchar(max)
  4. Simple JavaScript Inheritance--一个极简JS面向对象-类库
  5. surge for mac出测试版本了
  6. Android 另类方法监听软键盘的弹出收起事件
  7. C#写文本日志帮助类(支持多线程)
  8. form表单控件
  9. linux atime ctime mtime
  10. css-盒模型,浮动,定位之间的关系
  11. java reflect反思总结
  12. (中等) HDU 3265 Posters , 扫描线。
  13. centos7服务的管理
  14. Alpha第九天
  15. 控制公司 Controlling Companies
  16. Mybatis的应用1 Mybatis和logback的应用配置
  17. python-实现3级菜单(作业课)
  18. 记一次easywechat企业付款问题
  19. bootstrap的模拟单选按钮
  20. HTTP 03 HTTP 报文

热门文章

  1. Linux环境下执行脚本重启Weblogic控制台中部署的应用程序
  2. MASA Framework -- EventBus入门与设计
  3. 解决头部使用 position:fixed; 固定定位后遮住下方内容的问题
  4. 2022-11-01 Acwing每日一题
  5. Goland环境中Go module配置
  6. PHPMQTT问题一二三
  7. 12、求Sn = a + aa + aaa + aaaa + ....其中a为一个数字,一共有n项。a和n由用户键盘输入。
  8. 第2-3-7章 个人网盘服务接口开发-文件存储服务系统-nginx/fastDFS/minio/阿里云oss/七牛云oss
  9. 禁用显卡自动更新(解决官办驱动和OEM驱动相冲)
  10. 深度解析KubeEdge EdgeMesh 高可用架构