zoukankan      html  css  js  c++  java
  • day32

    目录

    回顾

    软件开发架构:
    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
  • 相关阅读:
    SQList基础+ListView基本使用
    Git本地上传口令
    记住用户名和登录密码+虚拟机没有root权限解决办法
    API+gir上传错误解决办法
    界面跳转+信息传递+AS中如何将ADV转移到其他盘中
    界面跳转
    Android学习——ListView
    开课第一周周总结
    体温上报APP——班级统计
    体温上报APP——打印
  • 原文地址:https://www.cnblogs.com/xwjhyy/p/11887692.html
Copyright © 2011-2022 走看看