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
  • 相关阅读:
    HDOJ 1846 Brave Game
    并查集模板
    HDU 2102 A计划
    POJ 1426 Find The Multiple
    POJ 3278 Catch That Cow
    POJ 1321 棋盘问题
    CF 999 C.Alphabetic Removals
    CF 999 B. Reversing Encryption
    string的基础用法
    51nod 1267 4个数和为0
  • 原文地址:https://www.cnblogs.com/xwjhyy/p/11887692.html
Copyright © 2011-2022 走看看