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

注意:
周末作业:
1.将FTP改成并发(拔高作业) + 50分
- 多线程
- 协程

posted @ 2019-10-25 20:26  lucky_陈  阅读(112)  评论(0编辑  收藏  举报