周总结第八周

周总结(8)

软件开发架构

C/S架构

C客户端,S服务端

​ 优点:针对客户端可以高度定制

B/S架构

​ B浏览器,S服务端

​ 优点:不需要下载

通过前者与后者进行数据交互,基于网络

网络编程

  • 基于网络编写代码,能够实现数据的远程交互。

目的: 开发C/S架构的软件

必备条件

必须具备一个物理链接介质。

OSI七层协议

1.应用层

2.表示层

3.会话层

4.传输层

5.网络层

6.数据链路层

7.物理链接层

物理链接层

  • 计算机之间的物理连接介质,接收数据

数据链路层

  • 基于物理链接层收到的信号

1.规定了 信号的分组方式

2.以太网协议

​ MAC地址,由12位16进制数组成,前六位位厂商编号,后六位流水线号。 每台计算机都必须有网卡。

网络层

IP地址  规定了计算机接入网络必须要拥有IP地址
与MAC地址区别: MAC地址是物理地址无法修改
IPV4 点分十进制,现如今地址池已不够用
IPV6 可以表示地球上每一粒沙子,地址池非常大

IP地址分为公网与私网,公网才可以直接被浏览器访问到

传输层

PORT端口协议
	每个应用软件都有属于他的独立端口
端口范围:
0-65535 动态分配
0 -1024 系统默认使用的端口
1024-8000 常用软件使用的端口号
我们以后使用8000往后

同一台计算机上面的端口不能重合。每次关闭应用重新打开便会重新分配一个端口。有些软件也可以设置固定端口

IP地址:用来标识全世界独一无二的一台接入互联网的计算机
 PORT号:用来标识一台计算机上面的某一个应用程序
  
 那么IP + PORT :就是定位全世界独一无二的计算机上面的某一个应用程序
  所以我们在日常中使用的网址其本质就是""" IP地址+端口号,通过域名解析将网址解析IP+PORT 两者之间使用冒号:连接 """

TCP协议

可靠协议,流式协议
"三次握手键连接"
	彼此之间互相开通道传输数据。
   1.客户端向服务端发出开通道需求并附上标识(作为此通道唯一标识)
	2. 服务端允许建立通道(+上此通道标识),但此时通道是单向的,服务端也想向 客户端开通通信通道, 发出请求给客户端建立通信通道。 并附上标识。
   	3. 客户端允许建立 通道。附上标识。
#  上述步骤完成后 就建立了双向通道

"四次挥手断链接"
	1.客户端请求断开单向通道。(客户端通向服务端的通道)
   2. 服务端确认断开单向通道。
	3.服务端请求断开单向通道(服务端通向客户端的通道)
   4.客户端确认断开单向通道
# 不能向 3次握手建链接那样 中间2步合并,因为中间需要确认消息是否发完,要思考。
		称为: "TIME_WAIT 状态  "(时间等待)
	比如说: 你发分手短信给女朋友, 女朋友不会立刻给你回复消息。他需要思考思考。(海后除外)
    
====================================================================================
 "洪水攻击"   "SYN_RCVD 状态"  (同步收到)
    是指在同一时间大量的客户端向服务端申请建立链接。 导致服务端处于 SYN_RCVD状态
 解决方案:
	设定缓冲池: 设定数量同时只接待规定的数量的客户端,超出部分进入缓冲中排队。当接待中有客户端退出,顺位补位  # 类似于咱们玩游戏排队    

UDP协议

丢包协议、不可靠协议
不需要建立双向通道,数据的传输的速度快,但是传输的数据可能会发生丢失
QQ使用的就是UDP协议,以前我们使用QQ得时候经常会发现短信发了对方收不到,所以就存在产生数据的丢失情况。
但如果在UDP协议的基础上做扩展来保证数据的安全就两全其美。

应用层

程序员自己写的应用程序。协议狠多
 常见的  HTTP , HTTPS, FTP 

网络相关专业名词

计算机之间想要实现数据交互 必须 要 '连接'在一起
1.交换机
	能够将所有接在交换机设备上的计算机彼此互联
2.广播
	首次查找在同一台交换机上的其他计算机,需要向交换机发出广播 ,首次"群发"  
3.单播
   广播目标计算机回复消息时称为 "单播"并传输 mac地址
4.广播风暴
	同一台交换机上的计算机同时 发出广播
5.局域网
	单个交换机组成的网络。一个小区域内的网络。
6.广域网
	区域更大的局域网
7.互联网
	所有局域网,广域网连接到一起
8.路由器
	不同局域网之间的计算机无法实现数据交互,需要路由器连接 

socket模块

封装七层协议复杂端口提供了简单快捷端口

套接字间接:

​ AF_UNIX 基于文件类型的套接字家族

​ AF_INET 基于网络类型的套接字家族

socket基本使用

bind()					 绑定地址
listen()				 半连接池
accept()				 等待客户端链接
send()					 发送消息
recv()					 接收消息
connect()				 链接服务端

黏包现象

我们在使用socket 模块进行数据交互时,服务端连续执行了3次recv, 客户端连续执行了 3次send 
此时如果我们的recv 填写的1024字节 如果send 传输的字节不超过1024,那么我们只第一次recv就会得到3条信息合并成一条。  
如果我们填写的字节大写跟我们传输的一样,那么就会一次性连续得到3条消息  

                                   此现象就称为黏包现象。
"黏包现象产生的原因"
	1.不知道每次的数据到底多大
	2.TCP也称为流式协议:数据像水流一样绵绵不绝没有间隔(TCP会针对数据量较小且发送间隔较短的多条数据一次性合并打包发送)
"解决方法:"
	明确的得知即将获取的数据具体有多大。使用 "struct模块"

黏包终极解决思路

# 发送方
1.先构造一个字典,里面放真实数据的信息
eg: 数据大小、名称、简介、、、
2、对字典做打包处理pack
3、发送固定长度的报头 (字典的长度)
4、发送真实的字典数据
5、发送真实的真正数据

# 接收方
1、先接收打包好固定长度的字典
2、解析出字典的真实长度unpack
3、接收字典数据
4、从字典数据中解析出各种信息
5、接收真实的数据

服务端:

import socket
import struct
import json
server = socket.socket()
server.bind(('127.0.0.1', 14334))
server.listen(5)

sock, addr = server.accept()
# 接收固定长度的报头
date_dict_head = sock.recv(4)
# 根据报头解析出字典数据的长度
data_dict_len = struct.unpack('i',date_dict_head)[0]
# 接收字典数据
data_dict_bytes = sock.recv(data_dict_len)
# 利用jason 模块 解码并反序列化
data_dict = json.loads(data_dict_bytes)
# 获取真实字典数据
print(data_dict)
total_size = data_dict.get('file_size')
recv_size = 0
with open(data_dict.get('file_name'),'wb') as f:
    while recv_size < total_size:
        data = sock.recv(1024)
        f.write(data)
        recv_size += len(data)
        print(recv_size)
        
第二种获取真实数据的方法:
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 struct
import json

client = socket.socket()
client.connect(('127.0.0.1',14334))
# 获取真实数据大小
file_size = os.path.getsize(r'D:\pythonProject\11.16\1.txt')
# 制作真实数据的字典数据
data_dict = {
    'file_name': '我爱你.txt',
    '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))
# 发送字典报头  (i模式报头固定长度是4)
client.send(data_dict_len)
# 发送字典
client.send(data_dict_bytes)
# 发送真实数据
with open(r'D:\pythonProject\11.16\1.txt','rb') as f:
    for line in f:
        client.send(line)

并发编程理论

发展史:
1.穿孔卡片,一次只能给一个人使用电脑.cpu利用率极低。
2.联机批处理  使用磁盘一次性录入多个程序,缩短了cpu的等待时间,提高了cpu的利用率
3.脱机批处理 类似于现在的计算机核心部件雏形,极大提升了cpu的利用率。

单道技术

  • 所有程序排队执行,执行完一个才会去执行下一个,过程中不能重合。

    ​ 缺点: 浪费了资源。耗时长

多道技术

  • 计算机利用等待空闲 提前准备其他数据。最大化压榨CPU性能提高性能

    两种状态:

    ​ 1.切换

    运行中遇到IO操作,cpu会利用空档去执行别的程序

    ​ 2.保存

    每次切换都会保存程序运行的当前状态。

进程理论

​ 1.程序

程序员编写的代码还没有运行。

​ 2.进程

系统当前正在运行的程序

进程的调度算法

1. FCFS 先来先服务
顾名思义谁先来先服务谁
2. 短作业优先调度
优先处理代码短的程序
3. 时间片轮转法+多级反馈队列
将时间平均分给每个程序,如果没执行完便会根据进程时间长短进行分级 分配更多的时间
等级越靠下表示耗时越长(代码很多),每次分级分到的时间就会越多优先级越低。   如果有新代码来了优先执行新代码

并行与并发

并行:
 多个程序同时执行,需要多个CPU进行工作
并发:
 一个CPU工作看起来运行了多个程序

进程的三状态

就绪态
	所有程序再被CPU执行之后都必须先进入就绪态等待
运行态
	CPU正在执行
阻塞态
	进程运行过程中出现了IO操作 阻塞态无法直接进入运行态 需要先进入就绪态。

同步与异步

表达任务的提交方式

同步:
	提交完任务之后会在原地等待任务的返回结果,在等待的过程不会做任何事。
异步:
	提交完任务之后 不原地等待,去干别的事情,有结果自动通知。

阻塞与非阻塞

任务的执行状态
阻塞态
	进程的三状态中的阻塞态。任务有IO操作时就会进入
非阻塞态
   就绪态和运行态
    
 """
    如果想要提高程序被执行效率
       就要程序一直处于就绪态和运行态
  """

同步异步和阻塞费阻塞结合使用

同步异步:用来描述任务的提交方式
阻塞非阻塞: 用来描述任务的执行状态

同步+阻塞:
  银行排队办理业务,期间不做任何事,就等着
同步+非阻塞:
  银行排队办理业务,期间可以去做一些其他的事,但是人还在办理业务的队列中
    
异步阻塞:
   在椅子上坐着,不做任何事
异步非阻塞:
   在椅子上面坐着,在期间喝水、吃东西、工作、玩手机、、(这个过程就是把程序运行到了极致) 

创建进程的多种方式

我们常用的就是直接双击软件图标。
但是python代码也给我们提供了创建进程的方式
第一种方法:

def task(name):
    print('子程序开始', name)
    time.sleep(3)
    print('子程序结束', name)


if __name__ == '__main__':
    p1 = Process(target=task, args=('tank',))  
    p1.start()	# 异步     
    task()  # 同步
    print('这是主程序')
===================================================================================

第二种方法:
class MyProcess(Process):
    import time
from multiprocessing import Process


class MyProcess(Process):
    def __init__(self,name,age):
        super().__init__()
        self.name = name
        self.age  = age

    def run(self):
        print('子程序开始',self.name)
        time.sleep(3)
        print('子程序结束',self.age)

if __name__ == '__main__':
    obj = MyProcess('tank',28)
    obj.start()
    print('主程序')
    

进程间的数据隔离

一台计算机上的多个进程 数据在默认情况下是无法访问的。也称为物理隔离,但是我们也可以通过 global 来修改 外部数据实现
"""
在不同的操作系统中创建进程底层原理不一样
    windows
        以导入模块的形式创建进程
    linux/mac
        以拷贝代码的形式创建进程
"""
在子程序中复制了同样的代码形成了一个新的内存空间,在此内存空间的task函数类利用global修改了外部name的值

进程的join方法

"join方法就是让主进程等待子进程代码运行完毕之后 再执行后续的代码"
想要p1.start()之后的代码, 等待子进程全部运行结束之后再打印
   1.直接sleep,但是这个方法肯定不可行,因为子进程运行时间不可控
    2.join方法
      针对多个子进程的等待

IPC机制与消息队列

IPC : 进程间相互通信
消息队列:存储数据的地方,谁都能存,谁都能取
Queue 模块
先进先出,使用put方法增加数据

进程对象的多种方法

1.查看进程号:
 	current_process()    # 查看父进程号
 	current_process().pid  # 查看子进程号
os查看进程号:
    import os
 	os.getpid()         # 查看子进程号
  	os.getppid()        # 查看父进程号
代码:
def start():
    print(f'我是子进程1我的进程号:', os.getpid())
    print(f'我是子进程1我的父进程号:', os.getppid())
def end():
    print(f'我是子进程2我的进程号:', os.getpid())
    print(f'我是子进程2我的父进程号:', os.getppid())
if __name__ == '__main__':
    p1 = Process(target=start)
    p2 = Process(target=end)
    p1.start()
    p2.start()
----------------------------------------
我是子进程1我的进程号: 45084
我是子进程2我的进程号: 22744
我是子进程1我的父进程号: 28576
我是子进程2我的父进程号: 28576
----------------------------------------
===================================================================================
2.中止进程:
	p1.terminate()  
    
代码:
def start():
    print(f'我是子进程1')
def end():
    print(f'我是子进程2')
if __name__ == '__main__':
    p1 = Process(target=start)
    p2 = Process(target=end)
    p1.start()
    p1.terminate()
    print('我p1被杀死了')
    p2.start()
----------------------------------------
我p1被杀死了
我是子进程2
----------------------------------------
===================================================================================
3.判断进程是否存活
p1.is_alive() # 判断过程中会经历主进程回收,如果中间不加 sleep  还是 为True 还没死透,也称为假死
4.start()   # 创建子进程
5.join()	# 等待子进程代码执行完毕后在执行主进程代码

守护进程

守护进程会随着守护的   进程结束(主进程)而立刻结束
使用 .daemon= True来设置 守护进程

僵尸进程和孤儿进程

僵尸进程
   所有的子进程在运行结束之后都会变成僵尸进程(死了没死透)
   因为还保留着pid和一些运行过程的中的记录便于主进程查看(只是短时间保存)
    等这些信息被主进程回收,就彻底死了
    1.主进程正常结束
    2.调用join方法   等待子进程 死透
    # 僵尸进程是无害的
孤儿进程
  # 子进程存在,但是父进程毙了
  子进程会被操作系统自动接管   (类似于福利院收养了)

多进程数据错乱与互斥锁

"""
  在多个进程操作同一个数据的时候会造成数据的错乱, 所以我们需要增加一个加锁处理(互斥锁)
  将并发变成串行, 效率低了,但安全性高了
  互斥锁并不能轻易使用, 容易造成死锁现象
  互斥锁旨在处理数据的部分加锁, 不能什么地方都加

"""
"""
  行锁: 针对行数加锁, 同一时间只能一个人操作
  表锁: 针对表数据加锁, 同一时间只能一个人操作
"""
posted @ 2022-11-20 16:02  李阿鸡  阅读(20)  评论(0编辑  收藏  举报
Title