day 32

day 32

# 软件开发架构:
    C/S:
        Client: 客户端
        Server: 服务端

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

        缺点:
            服务端更新后,客户端也得跟着跟新.
            须要使用多个软件,须要下载多个客户端

    B/S:
        Browser: 浏览器(客户端)
        Server: 服务端

    服务端与客户端做用:
        服务端: 24小时不间断提供服务
        客户端: 须要体验服务端时,再去链接服务端,并享受服务

一 网络编程:
    1.互联网协议OSI七层协议
        1)应用层
        2)表示层
        3)会话层
        4)传输层
        5)网络层
        6)数据链路层
        7)物理链接层

        - 物理链接层
            基于电信号发送二进制数据.

        - 数据链路层
            1) 规定好电信号的分组方式
            2) 必需要有一块网卡:
                - mac地址:
                    12位惟一的16进制字符串
                        - 前6位: 厂商号
                        - 后6位: 流水号
            - 以太网协议:
                在同一个局域网内通讯.
                - 单播
                    1对1吼
                - 广播
                    多对多吼
                    - 广播风暴:
                    - 不能跨局域网通讯

        - 网络层
            - ip: 定位局域网的位置
            - port: 惟一标识一台计算机上一个应用程序.
            - arp协议:
                将mac地址获取,并解析成ip和port.

        - 传输层
            - TCP
                特色:
                    TCP协议称之为流式协议.

                若想要通讯,必须创建链接,并创建双向通道.
                - 三次握手,四次挥手
                    - 三次握手建链接
                        - 客户端往服务端发送请求创建通道
                        - 服务端要确认客户端的请求,并往客户端也发送请求创建通道
                        - 客户端接收到服务端创建链接的请求,并返回确认
                        - 创建双向通道

                    - 双向通道:
                        - 反馈机制
                        客户端往服务端发送请求获取数据,服务端务必返回数据,客户端确认收到.
                        反则会反复发送,一直到某个时间段内,会中止发送

                    - 四次挥手断链接
                        - C往S发送断开链接请求,S返回确认收到
                        - S须要再次发送断开链接请求
                        - C返回确认收到
                        - 最终确认断开链接

            - UDP
                1)数据不安全
                2)不须要创建双向通道
                3)传输速度快
                4)不会有粘包问题
                5)客户端发送数据,不须要服务端确认收到,爱收不收

            TCP与UPD的区别:
                TCP: 比喻成在打电话
                UDP: 比喻成发送短信

        - 应用层
            - ftp
            - http:
                能够携带一堆数据

            - http + ssl

    2.socket
        socket用来写套接字客户端与服务端的模块,内部帮咱们封装好了7层协议须要作的事情.

    3.手撸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)

    4.subprocess(了解)
        用来经过代码往cmd建立一个管道,而且发送命令和接收cmd返回的结果.
        import subprocess
        obj = subprocess.Popen(
            'cmd命令',
            shell=True,
            # 接收正确结果
            stdout=subprocess.PIPE,
            # 接收错误结果
            stderr=subprocess.PIPE
        )
        success = obj.stdout.read()
        error = obj.stderr.read()
        msg = success + error


    5.黏包问题
        1.不能肯定对方发送数据的大小
        2.在短期内,间隔时间短,而且数据量小的状况, 默认将这些数据打包成一个
            屡次发送的数据 ---> 一次性发送

    6.struct解决黏包问题
        初级版:
        i: 4
        能够将一个数据的长度打包成一个固定长度的报头.
        struct.pack('模式i', '源数据长度')
        data = 'gagawagwaga'
        # 打包成报头
        headers = struct.pack('i', len(data))

        # 解包获取数据真实长度
        data = struct.unpack('i', headers)[0]

        注意: 以什么方式打包,必须以什么方式解包.

        升级版:
            先将数据存放到字典中,将字典打包发送过去
            - 字典的好处:
                - 真实数据长度
                - 文件的描述信息
                - 发送的数据,更小

                dic = {
                    'data_len': 1000000000000000000000046546544444444444444444444444444444444444444,
                    文件的描述信息
                }

    7.上传大文件数据
        # 客户端
        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)


    10.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
        TCP:
            SOCK_STREAM
            conn.recv()

        UDP模板:
            SOCK_DGRAM
            server.recvfrom()

            - 服务端
                import socket
                server = socket.socket(
                    type=socket.SOCK_DGRAM
                )
                server.bind(
                    (ip, port)
                )
                data, addr = server.recvfrom(1024)
                server.sendto(data, addr)

            - 客户端
                import socket
                client = socket.socket(
                    type=socket.SOCK_DGRAM
                )
                ip_port = (ip, port)

                client.sendto(data, ip_port)

                data, _ = client.recvfrom(1024)
                print(data)


二 并发编程
    12.多道技术
        - 单道

        - 多道: 切换 + 保存状态
            - 空间上的复用
                支持多个程序使用

            - 时间上的复用
                - 遇到IO操做就会切换程序
                - 程序占用CPU时间过长切换

    13.并发与并行
        并发: 看起来像同时运行: 多道技术
        并行: 真正意义上的同时运行: 多核下


    14.进程
        进程是资源单位,没建立一个进程都会生成一个名称空间,占用内存资源.

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

      - 进程调度
            - 时间片轮转法
                10个进程, 将固定时间,等分红10份时间片,分配给每个进程.

            - 分级反馈队列
                1级别:
                2级别:
                3级别:


      - 进程的三个状态
            - 就绪态:
                建立多个进程, 必需要排队准备运行

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

            - 阻塞态:
                当运行态遇到IO操做,就会进阻塞态.

      - 同步与异步
            提交任务的方式
            - 同步: 同步提交, 串行,一个任务结束后,另外一个任务才能提交并执行.
            - 异步: 异步提交, 多个任务能够并发运行

      - 阻塞与非阻塞
        - 阻塞:
            阻塞态
        - 非阻塞:
            就绪态
            运行态

      - 同步和异步、阻塞和非阻塞的区别。
            二者是不一样的概念,不能混为一谈.

      - 建立进程的两种方式
            一:
                p = Process(target=任务, args=(任务的参数, ))
                p.daemon = True  # 必须放在start()前,不然报错
                p.start()  # 向操做系统提交建立进程的任务
                p.join()  # 向操做系统发送请求, 等全部子进程结束,父进程再结束

            二:
                class MyProcess(Process):
                    def run(self):  # self == p
                        任务的过程

                p = MyProcess()
                p.daemon = True  # 必须放在start()前,不然报错
                p.start()  # 向操做系统提交建立进程的任务
                p.join()  # 向操做系统发送请求, 等全部子进程结束,父进程再结束


      - 回收进程资源的两种条件
            - 调用join让子结束后,主进程才能结束.
            - 主进程正常结束

    15.僵尸进程与孤儿进程(了解)
        僵尸进程: 凡是子进程结束后,PID号还在, 主进程意外死亡,无法给子进程回收资源.
            - 每一个子进程结束后,都会变成,僵尸进程 (PID)

        孤儿进程: 凡是子进程没有结束,可是主进程意外死亡.操做系统优化机制(孤儿院),
        会将没有主,而且存活的进程,在该进程结束后回收资源.

    16.守护进程
        只要父进程结束,全部的子进程都必须结束.

    17.互斥锁
        将并发变成串行,牺牲执行效率,保证数据安全.

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

    18.队列
        - FIFO队列: 先进先出
        from multiprocessing import Queue
        q = Queue(5)
        # 添加数据,若队列添加数据满了,则等待
        q.put()
        # 添加数据,若队列添加数据满了,直接报错
        q.put_nowait()

        # 获取队列中的数据
        q.get()  # 若队列中没数据,会卡住等待
        q.get_nowait()  # 若队列中没数据,会直接报错

    19.堆栈
        LIFO

    20.IPC进程间通讯
        - 进程间的数据是隔离的
        - 队列可让进程间通讯
        - 把一个程序放入队列中,另外一个程序从队列中获取,实现进程间数据交互


    21.生产者与消费者 模型
        生产者: 生产数据
        消费者: 使用数据
        为了保证 供需平衡.

        经过队列实现, 生产者将数据扔进队列中,消费者从队列中获取数据.
            能够保证一边生产一边消费.



    22.线程
        - 什么是线程
            - 进程: 资源单位
            - 线程: 执行单位
                - 建立进程时,会自带一个线程

            一个进程下能够建立多个线程.

        - 使用线程的好处
            节省资源的开销

        - 进程与线程优缺点:
            - 进程:
                优势:
                    - 多核下能够并行执行
                    - 计算密集型下提升效率

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

            - 线程:
                优势:
                    - 占用资源远比进程小
                    - IO密集型下提升效率

                缺点:
                    - 没法利用多核优点


    23.线程间数据是共享的
        - 画图

    24.GIL全局解释器锁

        - 只有Cpython才有自带一个GIL全局解释器锁
        1.GIL本质上是一个互斥锁.
        2.GIL的为了阻止同一个进程内多个线程同时执行(并行)
            - 单个进程下的多个线程没法实现并行,但能实现并发

        3.这把锁主要是由于CPython的内存管理不是 "线程安全" 的.
            - 内存管理
                - 垃圾回收机制

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

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

    25.多线程使用的好处
        - 多线程:
            IO密集型,提升效率

        - 多进程
            计算密集型,提升效率

    26.死锁现象(了解)

    27.递归锁(了解,之后不用)
        解决死锁现象
        mutex = Lock()  # 只能引用1次
        mutex1, mutex2 = RLock()  # 能够引用屡次
        +1, 只要这把锁计数为0释放该锁, 让下一我的使用, 就不会出现死锁现象.

    28.信号量(绝对了解)
        信号量也是一把锁, 可让多个任务一块儿使用.
        互斥锁:
            只能让一个任务使用
        信号量:
            可让多个任务一块儿使用.
            sm = Semaphore(5)  可让5个任务使用

    29.线程队列
        使用场景:
            若线程间数据不安全状况下使用线程队列, 为了保证线程间数据的安全.
        import queue
        - FIFO: 先进先出队列
            queue.Queue()
        - LIFO: 后进先出队列
            queue.LifoQueue()
        - 优先级队列:
            - 根据数字大小判断,判断出队优先级.
            - 进队数据是无序的
            queue.PriorityQueue()

    30.event事件
        能够控制线程的执行,让一些线程控制另外一些线程的执行.
        e = Event()

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

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

    31.进程池与线程池
        为了控制进程/线程建立的数量,保证了硬件能正常运行.
        from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor

        pool1 = ProcessPoolExecutor()  # 默认CPU个数
        pool2 = ThreadPoolExecutor()  # CPU个数 * 5
        pool3 = ProcessPoolExecutor(100)  # 100个
        pool4 = ThreadPoolExecutor(200)  # 200个

        # 将函数地址的执行结果,给回调函数
        pool4.submit(函数地址, 参数).add_done_callback(回调函数地址)

        - 回调函数(必须接收一个参数res):
            # 获取值
            res2 = res.result()

    32.协程
        - 进程: 资源单位
        - 线程: 执行单位
        - 协程: 单线程下实现并发, 不是任何的单位,是程序员YY出来的名字.

        - 单线程下实现并发
            好处是节省资源, 单线程 < 多线程 < 多进程

            - IO密集型下:
                协程有优点

            - 计算密集型下:
                进程有优点

        - 高并发:
            - 多进程 + 多线程 + 协程   (Nginx)

        协程的建立:
            手动实现切换 + 保存状态:
                - yield

                - 函数一直在调用next()
                    会不停地切换

                yield不能监听IO操做的任务
                - gevent来实现监听IO操做

    33.gevent
        pip3 install gevent
        from gevent import monkey
        monkey.patch_all()  # 设置监听全部IO
        from gevent import spawn, joinall  # 实现 切换 + 保存状态

        - 实现了单线程下实现并发
        s1 = spawn(任务1)
        s2 = spawn(任务2)
        joinall([s1, s2])

    34.IO模型(了解)
        - 阻塞IO
        - 非阻塞IO
        - 多路复用IO
        - 异步IO
相关文章
相关标签/搜索