• day_32


    软件开发架构

    C/S:

    client:客户端

    server:服务端

    优点:占用网络资源少,软件的使用稳定

    缺点:服务端更新后,客户端也要更新,需要使用多个软件,需要下载多个客户端

    B/S:

    Browser:浏览器

    server:服务端

    服务端与客户端作用:

    服务端:24小时不间断提供服务

    客户端:需要体验服务端时,再去连接服务端,并享受服务

    网络编程

    七层协议

    应用层,表示层,会话层,传输层,网络层,数据链路层,物理连接层

    • 物理连接层

      基于电信号发送二进制数据

    • 数据链路层

      1. 规定好电信号的分组方式

      2. 必须要有一块网卡

        mac地址:

        12位唯一的16进制字符串

        • 前6位:厂商号
        • 后6位:流水号

      以太网协议:

      在同一个局域网内通信。

      • 单播

        1对1通信

      • 广播

        多对多通信

        • 广播风暴:

          不能夸局域网通信

    • 网络层

      • ip:定位局域网的位置

      • arp协议:

        将mac地址获取,并解析成ip和port

    • 传输层

      • TCP

        特点:

        tcp协议称之为流式协议

        想要通信,必须建立连接,并建立双向通道

        • 三次握手,四次挥手

          • 三次握手建连接

            • 客户端往服务端发送请求建立通道
            • 服务端要确认客户端的请求,并网客户端也发送请求建立通道
            • 客户端接收到服务端建立连接的请求,并返回确认
            • 建立双向通道
          • 双向通道:

            • 反馈机制

              客户端往服务端发送请求获取数据,服务端务必返回数据,客户端确认收到,否则会反复发送,一直到某个时间段内,会停止发送

          • 四次挥手断连接

            • 客户端往服务端发送断开连接,服务端返回确认收到
            • 服务端需要再次发送断开连接请求
            • 客户端返回确认收到
            • 最终确认断开连接
      • UDP

        1. 数据不安全
        2. 不需要建立双向通道
        3. 传输速度快
        4. 不会有粘包问题
        5. 客户端发送数据,不需要服务端确认收到
      • TCP和UPD的区别:

        TCP:比喻成在打电话

        UDP:比喻成发送短信

    • 应用层

      • ftp

      • http:

        可以携带一堆数据

      • http+ssl

    socket

    socket用来写台阶自客户端与服务端的模块,内部帮我们封装好了7层协议需要做的事情

    socket套接字模板

    - 服务端:
                import socket
                server = socket.socket()
                server.bind(
                    (ip, port)
                )  # 绑定手机号
                server.listen(6)  # 半连接池: 可以接待7个客户端
                # 监听连接
                conn, addr =server.accept()
                # 接收消息
                data = conn.recv(1024)
                # 发送消息
                conn.send('消息内容'.encode('utf-8'))
    
    - 客户端:
                import socket
                client = socket.socket()
                client.connect(
                    (ip, port)
                )
                # 发送消息
                client.send()
                # 接收消息
                client.recv(1024)
    

    粘包问题

    1. 不能确认对方发送数据的大小
    2. 在短时间内,间隔时间短,并且数据量小的情况,默认将这些数据打包成一个多次发送的数据---》一次性发送

    struct解决粘包问题

    初级版:
            i: 4
            可以将一个数据的长度打包成一个固定长度的报头.
            struct.pack('模式i', '源数据长度')
            data = 'gagawagwaga'
            # 打包成报头
            headers = struct.pack('i', len(data))
    
            # 解包获取数据真实长度
            data = struct.unpack('i', headers)[0]
    
            注意: 以什么方式打包,必须以什么方式解包.
    
            升级版:
                先将数据存放到字典中,将字典打包发送过去
                - 字典的好处:
                    - 真实数据长度
                    - 文件的描述信息
                    - 发送的数据,更小
    
                    dic = {
                        'data_len': 1000000000000000000000046546544444444444444444444444444444444444444,
                        文件的描述信息
                    }
    

    上传大文件数据

    # 客户端
            dic = {
                    文件大小,
                    文件名
                   }
    
            with open(文件名, 'rb') as f:
                for line in f:
                    client.send(line)
    
    
            # 服务端
            dic = {
                    文件大小,
                    文件名
                   }
            init_recv = 0
            with open(文件名, 'wb') as f:
                while init_recv < 文件大小:
                    data = conn.recv(1024)
                    f.write(data)
                    init_recv += len(data)
    

    socketserver

    可以支持并发

    import socketserver
    # 定义类
            # TCP: 必须继承BaseRequestHandler类
            class MyTcpServer(socketserver.BaseRequestHandler):
    
                - handle
                    # 内部实现了
                    server = socket.socket()
                    server.bind(
                        ('127.0.0.1', 9527)
                    )
                    server.listen(5)  ---
    
                    while True:
                        conn, addr = server.accept()
                        print(addr)
    
                # 必须重写父类的handle, 当客户端连接时会调用该方法
                def handle(self):
                    print(self.client_address)
                    while True:
                        try:
                            # 1.接收消息
                            # request.recv(1024) == conn.recv(1024)
                            data = self.request.recv(1024).decode('utf-8')
                            send_msg = data.upper()
                            self.request.send(send_msg.encode('utf-8'))
    
                        except Exception as e:
                            print(e)
                            break
    

    并发编程

    多道技术

    • 多道:切换+保存状态

      • 空间上的复用

        支持多个程序使用

      • 时间上的复用

        • 遇到IO操作就会切换程序
        • 程序占用CPU时间过长会切换

    并发与并行

    并发:看起来像同时运行:多道技术

    并行:真正意义上的同时运行:多核下

    进程

    进程是资源单位,每创建一个进程都会生成一个名称空间,占用内存资源

    • 程序与进程

      程序就是一堆代码,进程就是一堆代码运行的过程

    • 进程调度

      • 时间片轮转法

        10个进程,将固定时间,等分成10份时间片,分配给每一个进程

      • 分级反馈队列

        1级最高

    • 进程的三个状态

      • 就绪态

        创建多个进程,必须要排队准备运行

      • 运行态

        进程开始运行,1. 结束 2. 阻塞

      • 阻塞态

        当运行态遇到IO操作,就会进阻塞态

    • 同步与异步

      提交任务的方式

      • 同步:同步提交,串行,一个任务结束后,另一个任务才能提交并执行
      • 异步:异步提交,多个任务可以并发运行
    • 阻塞与非阻塞

      • 阻塞:

        阻塞态

      • 非阻塞:

        就绪态

        运行态

    • 同步和异步、阻塞和非阻塞的区别

      两者是不同的概念,不能混为一谈

    • 创建进程的两种方式

      1. p=Process(target=任务,args=(任务的参数,))

        p.deamon=True #必须放在start()前,否则报错

        p.start() # 向操作系统提交创建进程的任务

        p.join() # 向操作系统发送请求,等所有子进程结束,父进程再结束

      2. class MyProcess(Process):

        ​ def run(self):

        ​ 任务的过程

        p=Process(target=任务,args=(任务的参数,))

        p.deamon=True #必须放在start()前,否则报错

        p.start() # 向操作系统提交创建进程的任务

        p.join() # 向操作系统发送请求,等所有子进程结束,父进程再结束

    • 回收进程资源的两种条件

      • 调用join让子进程结束后,主进程才能结束
      • 主进程正常结束

    僵尸进程与孤儿进程

    僵尸进程:凡是子进程结束后,pid号还在,主进程意外死亡的都会变成僵尸进程

    孤儿进程:凡是子进程没有结束,而主进程已经意外结束的,就是孤儿进程,操作系统的优化机制会回收这些进程

    守护进程

    只要父进程结束,所有的子进程都必须结束

    互斥锁

    将并发编程穿行,牺牲执行效率,保证数据安全

    from multiprocessing import Lock
    mutex=Lock()
    # 加锁
    mutex.acquire()
    # 修改数据
    mutex.reliease()
    

    队列

    • FIFO队列:先进先出

      from multiprocessing import Queue
      q=Queue(5)
      # 添加数据,若队列添加数据满了,则等待
      q.put()
      # 添加数据,若队列添加数据满了,直接报错
      q.put_nowait()
      
      # 获取队列中的数据
      q.get() # 若队列中没有数据,会卡住等待
      q.get_nowait() # 若队列中没有数据,会直接报错
      

    堆栈

    LIFO

    IPC进程间通信

    • 进程间的数据是个例的
    • 队列可以让进程间实现通信
    • 把一个程序放入队列中,另一个程序从队列中获取,实现进程间数据交互

    生产者与消费者 模型

    生产者:生产数据

    消费者:使用数据

    目的是为了保证供需平衡

    通过队列实现,生产者将数据扔进队列中,消费者从队列中获取数据

    可以保证一边生产一边消费

    线程

    • 什么是线程

      • 进程:资源单位

      • 线程:执行单位

        • 创建进程时,会自带一个线程

        一个进程下可以创建多个线程

    • 线程的好处

      节省资源开销

    • 进程与线程的优缺点

      • 进程

      优点:多核下可以并行执行,计算密集型情况下提高效率

      缺点:开销资源远高于线程

      • 线程

        优点:占用资源远小于进程,IO密集型情况下提高效率

        缺点:无法利用多核优势

    线程间数据是共享的

    GIL全局解释锁

    只有Cpython才有自带一个GIL全局解释器锁

    1. GIl本质上是一个互斥锁

    2. GIL是为了阻止同一个进程内多个线程同时执行(并行)

      • 单个进程下的多个线程不发实现并行,但能实现并发
    3. 这把锁主要是因为Cpython的内存管理不是“线程安全”的

      • 内存管理
        • 垃圾回收机制

      注意:多个线程过来执行,一旦遇到IO操作,就会立马释放GIL解释器锁,交给下一个先进来的线程

      总结:GIL的存在就是为了保证线程安全的,保证数据安全

    多线程使用的好处

    • 多线程:

      IO密集型,提高效率

    • 多进程:

      计算密集型,提高效率

    死锁现象

    递归锁

    解决死锁现象

    信号量

    信号量也是一把锁,可以让多个任务一起使用

    互斥锁:

    只能让一个任务使用

    信号量:

    可以让多个任务一起使用

    sm= Semaphore(5)

    线程队列

    使用场景:

    若线程间数据不安全情况下使用,为了保证线程间数据的安全

    import queue

    • FIFO 先进先出队列

      queue.Queue()

    • LIFO 后进后出队列

      queue.LifoQueue()

    • 优先级队列

      • 根据数字大小判断,判断出队优先级

      • 进队数据是无序的

        queue.PriorityQueue()

    event事件

    可以控制线程的执行,让一些线程控制另一些线程的执行

    e=Event()

    • 线程1

      e.set() # 给线程2发送信号,让他执行

    • 线程2

      e.wait() # 等待线程1的信号

    进程池与线程池

    为了控制进程/线程创建的数量,保证了硬件能正常运行

    from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
    pool1=ProcessPoolExecutor() # 默认CPU个数
    pool2=ThreadPoolExecutor() # CPU个数*5
    
    # 将函数地址的执行结果,给回调函数
    pool3.submit(函数地址,参数).add_done_callback(回调函数地址)
    
    • 回调函数(必须接受一个参数res):

      获取值

      res2 =res.result()

    协程

    • 进程:资源单位

    • 线程:执行单位

    • 协程:单线程下实现并发,不是任何的单位,是程序员Yy出来的名字

    • 单线程下实现并发

      好处是节省资源,单线程《多线程《多进程

      • IO密集型下:

        协程有优势

      • 计算密集型下:

        进程有优势

    • 高并发

      • 多进程+多线程+协程(Nginx)

    协程的创建:

    ​ 手动实现切换+保存状态:

    • yield

    • 函数一直调用next()

      会不停的切换

      yield不能监听IO操作的任务

      • gevent可以

    gevent

    from gevent import monkey
    monkey.patch_all() # 设置监听所有IO
    from gevent import spawn,joinall # 实现  切换+保存状态
    
    #实现了单线程下并发
    s1=spawn(任务一)
    s2=spawn(任务二)
    joinall([s1,s2])
    

    IO模型

    • 阻塞IO
    • 非阻塞IO
    • 多路复用IO
    • 异步IO
  • 相关阅读:
    RBAC概念
    Django框架的优缺点
    全文检索whoosh
    软件项目管理|期末复习(九)
    软件项目管理|期末复习(十四)
    HOJX 1003| Mixing Milk
    [转发]ACM刷题网站
    [转发]软件工具|Github上整理的一些工具
    软件项目管理|期末复习(三)
    计算机图形学|两道习题
  • 原文地址:https://www.cnblogs.com/maqiaobin/p/11740559.html
Copyright © 2020-2023  润新知