上周内容回顾
设计模式
简介
设计模式指的是在IT行业很多前辈在针对一些问题经过反复实践设定出来固定的解决套路
其中设计模式总共有23种
-
设计模式的分类
可以将其分为三大类,创建型、结构型、行为型。
设计模式之单列模式
在单列模式下类加括号调用多次只允许产生一个对象
如下:
# 正常情况下,类名加括号是实例化产生对象,执行几次就会产生几个不同的对象
class Myclass:
pass
obj1 = Myclass()
obj2 = Myclass()
obj3 = Myclass()
print(id(obj1), id(obj2), id(obj3)) # 2263190030944 2263190494080 2263189844464
#使用元类干预对象的创建过程,实现单列模式。
class MyMeTaClass(type):
# 记录类是否已经创建了对象
instance = None
def __call__(self, *args, **kwargs):
if self.instance:
return self.instance
# 获取空对象
obj = super().__call__(*args, **kwargs)
# 保存对象
self.instance = obj
# 返回空对象
return obj
class Single(metaclass=MyMeTaClass):
def __init__(self, name):
self.name = name
obj1 = Single('jason')
obj2 = Single('kevin')
obj3 = Single('tony')
print(id(obj1), id(obj2), id(obj3))
print(obj1.name)
print(obj2.name)
print(obj3.name)
pickle模块
简介
pickle模块与json模块很像,都是用来序列化及反序列化的。
pickle模块优点在于支持python所有的数据类型,缺点pickle模块不支持跨语言传输,兼容性不好。
实际应用
为了方便编写选课系统,需要将对象保存到文件中,那么就可以用到pickle模块。
class MyClass:
def __init__(self, name):
self.name = name
def choice_course(self):
print('%s正在选课' % self.name)
obj = MyClass('jason')
# 需求:将对象保存到文件 并且将来取出之后还可以通过句点符随意调用
# 常规的文件操作不行
with open(r'a.txt','w',encoding='utf8') as f:
f.write(obj) # 报错
# json模块也不行
import json
with open(r'a.txt','w',encoding='utf8') as f:
f.write(json.dumps(obj)) # 报错
# pickle模块可以解决,存取都没问题。
import pickle
with open(r'a.txt', 'wb') as f:
f.write(pickle.dumps(obj))
pickle.dumps(obj, f)
with open(r'a.txt', 'rb') as f:
data = pickle.load(f)
print(data)
print(data.name)
data.choice_course()
选课系统
选课系统项目分析
选课系统
角色:学校、学员、课程、讲师
要求:
1. 创建北京、上海 2 所学校
2. 创建linux , python , go 3个课程 , linux\py 在北京开, go 在上海开
3. 课程包含,周期,价格,通过学校创建课程
4. 通过学校创建班级, 班级关联课程、讲师5. 创建学员时,选择学校,关联班级
5. 创建讲师角色时要关联学校,
6. 提供三个角色接口
6.1 学员视图 可以注册 交学费 选择班级,
6.2 讲师视图, 讲师可管理自己的班级 上课时选择班级 查看班级学员列表 修改所管理的学员的成绩
6.3 管理视图,创建讲师, 创建班级,创建课程
7. 上面的操作产生的数据都通过pickle序列化保存到文件里
功能分析
- 管理员功能(核心功能)
- 注册
- 登录
- 创建讲师
- 创建学校
- 创建课程
- 讲师功能
- 登录
- 选择教授课程
- 查看教授课程
- 管理教授课程
- 批改学生作业
- 学生功能
- 注册
- 登录
- 选择学校
- 选择课程
- 查看分数
系统架构设计
功能展示层
src.py
admin_view.py
teacher_view.py
student_view.py
核心逻辑层
admin_interface.py
teacher_interface.py
student_interface.py
数据处理层
db_hanlder.py
model.py
软件开发架构
简介
在编写项目之前需要遵循的代码层面上的规范(即代码允许的流程、环节、步骤)
c/s架构
-
c:client 客户端、s:server 服务端
计算机上下载的各个互联网公司的app软件,本质其实就是客户端。
下载客户端的目的就是为了体验其对应服务端的服务
客户端可以看成是即将消费的客人,服务端可以看成是给你提供服务的店铺
服务端所具备的特性、固定的地址、可以同时服务很多人、24小时不间断提供服务
b/s架构
-
b:broswer 浏览器、s:sever 服务端
b/s架构其实本质上也是c/s架构,这种架构方式其实就是通过浏览器来充当各个服务端的客户端,用于想要体验服务不需要下载指定的客户端。
发展趋势
统一接口原则,如:微信、支付宝等...
其内部有很多小程序,可以直接体验服务,后续就是c/s架构和b/s架构交错使用,便面各自的劣势,方便用户使用 也方便自己
网络编程简介
-
如何理解网络编程
网络编程是基于互联网编写代码,程序可以实现远程数据交互
-
网络编程的意义
网络编程的本质就是为了解决计算机之间数据交互的问题,学习网络编程之后,我们就可以基于网络编程可以编写一个c/s架构的软件
-
网络编程的起源
任何先进的技术一般都是来源于军方,网络编程来源于美国军方开发
在没有网络编程的时候,如果俩太计算机需要数据交互,那么就需要使用拷贝的方式拷贝到硬盘上,然后拿着硬盘传递给需要交互数据的人这样非常的不方便。
-
网络编程的要求
早期的时候是使用有线的电话,不需要链接电话线
后来是那种大屁股电脑,必须要有网线。到现在的笔记本电脑,也必须要有网卡。
计算机之间想要实现远程数据交互传输,首要条件就是要有物理链接介质。
OSI七层协议
OSI七层协议指的是计算机设计到数据远程交互的时候,需要遵循设定的一些规范和交互的方式。
-
osi七层协议共分为七层
应用层、表示层、会话层、传输层、网络层、数据链路层、物理链接层
-
针对上述七层也可以将其合并为五层
应用层、传输层、网络层、数据链路层、物理链接层
-
还可以更加优化为四层
应用层、传输层、网络层、网络接口层
其中顺序很重要,且每一层都有各自的功能和规范
各层的特征
数据发送出去的时候,从应用层到物理链接层
数据接收回来的时候,从物理链接层到应用层
物理连接层
保证物理链接介质的条件,传递电信号(主要研究插网线情况)
数据链路层
-
规定了电信号的分组方式
-
规定了每台计算机必须都要有一块网卡
网卡必须有一串记录:以太网地址\mac地址(相当于电脑的身份证)
以太网\mac地址:由12为16进制数组成的
前6位: 生产商编号 后6位:生产流水编号
mac地址相当于电脑的身份证号,也就意味着可以根据地址查找计算机(可以通过mac地址实现数据交互)
网络相关名词简介
-
交换机
交换机是一种能够让接入交换机的多台计算机实现彼此互联的一种设备
-
以太网通信/mac通信
有了交换机之后,根据服务器的mac地址就可以实现数据交互。
在交互数据的过程中,先是通过广播的方式传递信息传递给需要交互数据的设备
后需要交互的设备通过单播的方式回复信息。
广播:相当于在人群中找人,大喊这个人的名字
单播:相当于在人群中听着有人在叫他顺着就找过来了
缺陷:mac地址通信仅限于局域网,接入交换机的设备过多,如果出现多个设备广播,可能会造广播风暴,类似于所有人同时大喊。
-
局域网、广域网
局域网指的是在某个固定区域组成的网络,广域网可以看成是更大区域的局域网。
-
路由器
路由器是一种将多个局域网链接到一起的一种设备
网络层
-
IP协议
规定了任何接入互联网的计算机都要由一个IP地址(类似于护照)
-
IP地址
IP地址由俩个版本分别是IPV4和IPV6
IPV4:采用点分十进制的方式
最小:0.0.0.0 最大:255.255.255.255
也就意味着同时可提供最大IP地址为4228250625个
但随着社会的发展上网的人数越来越多,上述地址数量远远不够就诞生了IPV6
IPV6: 据说可以给地球上的每一粒沙子分一个IP地址
IP特征:每个IP都自带定位,列如IP代理
传输层
PORT协议
也就是端口协议,规定了一台计算机的每一个正在运行的应用层序都必须有一个端口号,端口号相当于计算机用来管理应用程序的表示或者编号。
-
端口号的特征
端口号范围:0-65535 端口号是动态分配的,同一时间同一台计算机的端口号不能冲突
-
端口号的分配
0-1024:一般是操作系统内部需要使用的
1024-8000:一般是常见软件已经使用了
8000-65535:我们平时写代码可以使用8000以后的端口号
-
IP+PORT协议
IP用于标识全世界任意一台接入互联网的计算机
PORT用于标识一台计算机上的某个应用程序
IP+PORT那么他俩组合也就是用于标识全世界任意一台接入互联网的计算机上的具体应用程序
-
网址简介
网址(URL):统一资源定位符
URL的本质其实就是IP+PORT,也就是(IP:port)
tcp协议与udp协议
规定了数据传输所遵循的规则
数据传输能够遵循的协议有很多种,tcp和udp是较为常见的俩个
-
TCP协议
三次握手
客户端向服务端发起请求建立双向通道,服务端收到请求后响应客户端
服务端向客户端发送请求建立双向通道,客户端收到请求后响应服务端
上述操作被称之为TCP协议中的三次握手,当同时让大量的客户端朝服务端发起请求的时候,可能会造成洪水攻击。
四次挥手
客户端向服务端发起请求断开双向通道,服务端收到请求后响应客户端
中间需要有个检查时间,检查双方是否还存在数据交互。
服务端向客户端发送请求断开双向通道,客户端收到请求后响应服务端
基于TCP传输数据非常的安全,因为有双向通道,数据不容易丢失。
因其有个二次确认机制,每次发送数据都需要返回确认消息否则在一定时间会反复发送,更加确保数据安全。
-
UDP
基于UDP协议发送的数据,没有任何的通道也没有任何的限制UDP发送数据没有TCP安全。
应用层
主要取决于程序员自己采用什么策略和协议
常见协议有:HTTP HTTPS FTP...
socket套接字
基于文件类型的套接字家族 名称: AF_UNIX
基于网络类型的套接字家族 名称: AF_INET
运行程序的时候,肯定是先确保服务端运行之后才是客户端。
# 服务端
import socket
# 1.创建一个socket对象
server = socket.socket() # 括号内什么都不写 默认就是基于网络的TCP套接字
# 2.绑定一个固定的地址(ip\port)
server.bind(('127.0.0.1', 8080)) # 127.0.0.1本地回环地址(只允许自己的机器访问)
# 3.半连接池(暂且忽略)
server.listen(5)
# 4.开业 等待接客
sock, address = server.accept()
print(sock, address) # sock是双向通道 address是客户端地址
# 5.数据交互
sock.send(b'hello big baby~') # 朝客户端发送数据
data = sock.recv(1024) # 接收客户端发送的数据 1024bytes
print(data)
# 6.断开连接
sock.close() # 断链接
server.close() # 关机
# 客户端
import socket
# 1.产生一个socket对象
client = socket.socket()
# 2.连接服务端(拼接服务端的ip和port)
client.connect(('127.0.0.1', 8080))
# 3.数据交互
data = client.recv(1024) # 接收服务端发送的数据
print(data)
client.send(b'hello sweet server') # 朝服务端发送数据
# 4.关闭
client.close()
代码优化
-
send与recv
客户端与服务端不能同时执行同一个
一个先收那么另外一个就必须是发,一个先发那么另外一个就必须是先收,不能同时收或者发。
-
消息自定义
input获取用户数据即可(主要编码解码)
-
循环通信
给数据交互环节添加循环即可
-
服务端能够持续提供服务
不会因为客户端断开链接而报错
使用异常捕获,一旦客户断开链接,服务端结束通信循环,调到链接处等待
-
消息不能为空
增加判断消息是否为空,如果是空则重新输入(主要针对客户端)
-
服务器频繁重启可能会报端口号被占用的错(主要针对mac电脑)
from socket import SOL_SOCKET,SO_REUSEADDR server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加
-
客户端异常退出会发送空消息(针对mac、linux)
针对接收消息加判断处理即可
半连接池
server.listen(5)
括号内填入数字,主要是为了做缓存,便面太多等待无效,一般5位就够了
黏包问题
服务端代码
sock.recv(1024)
sock.recv(1024)
sock.recv(1024)
客户端代码
client.send(b'jason')
client.send(b'kevin')
client.send(b'tony')
1.TCP特性
流式协议:所有的数据类似于水流 连接在一起的
ps:数据量很小 并且时间间隔很多 那么就会自动组织到一起
2.recv
我们不知道即将要接收的数据量多大 如果知道的话不会产生也不会产生黏包
import struct
# info = '下午上课 以后可能是常态!'
# print(len(info)) # 13 数据原本的长度
# res = struct.pack('i', len(info)) # 将数据原本的长度打包
# print(len(res)) # 4 打包之后的长度是4
# ret = struct.unpack('i', res) # 将打包之后固定长度为4的数据拆包
# print(ret[0]) # 13 又得到了原本数据的长度
# info1 = '打起精神啊 下午也需要奋斗 也需要认真听 客服困难 你困我也困!!!'
# print(len(info1)) # 34
# res = struct.pack('i', len(info1)) # 将数据原本的长度打包
# print(len(res)) # 4 打包之后的长度是4
# ret = struct.unpack('i', res)
# print(ret[0]) # 34
"""
struct模块无论数据长度是多少 都可以帮你打包成固定长度
然后基于该固定长度 还可以反向解析出真实长度
思路
1.先将真实数据的长度制作成固定长度 4
2.先发送固定长度的报头
3.再发送真实数据
1.先接收固定长度的报头 4
2.再根据报头解压出真实长度
3.根据真实长度接收即可
"""
struct模块针对数据量特别大的数字没有办法打包!!!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)