网络编程与并发编程总结

目录

  • 一、软件开发架构
    • 1. C/S
    • 2. B/S
    • 3. 客户端与服务端作用
  • 二、网络编程
    • 1. 互联网协议OSI七层协议
      • 1.1 物理连接层
      • 1.2 数据链路层
      • 1.3 网络层
      • 1.4 传输层
      • 1.5 应用层
    • 2. socket
    • 3. subprocess(补充)
    • 4. 粘包问题
    • 5. struct解决粘包问题
    • 6. socketserver
  • 三、并发编程
    • 1. 多道技术
      • 1.1 单道
      • 1.2 多道
    • 2. 并发与并行
    • 3. 进程
      • 3.1 程序与进程的区别
      • 3.2 进程调度
      • 3.3 进程的三个状态
      • 3.4 同步与异步
      • 3.5 阻塞与非阻塞
      • 3.6 创建进程的两种方式
      • 3.7 回收进程资源的两种条件
    • 4. 僵尸进程与孤儿进程(了解)
    • 5. 守护进程
    • 6. 互斥锁
    • 7. 队列(进程队列)
    • 8. 堆栈
    • 9. IPC进程间通信
    • 10. 生产者与消费者 模型
    • 11. 线程
      • 11.1 什么是线程
      • 11.2 使用线程的好处
      • 11.3 进程与线程优缺点
    • 12. 线程间数据是共享的
    • 13. GIL全局解释器锁
    • 14. 多线程使用的好处
    • 15. 死锁现象(了解)
    • 16. 递归锁(了解)
    • 17. 信号量(了解)
    • 18. 线程队列
    • 19. event事件
    • 20. 进程池与线程池
    • 21. 协程
      • 21.1 单线程下实现并发的好处
      • 21.2 协程的创建
    • 22. gevent
    • 23. IO模型(了解)

一、软件开发架构

1. C/S

Client:客户端

Server:服务端

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

缺点:使用过程需要下载、更新,不能直接使用,且不能同时使用多个软件。

2. B/S

Browser:浏览器端(客户端)

Server:服务端

优点:无需下载更新,可以直接使用,且可以打开多个网页,用时访问多个网站。

缺点:消耗网络资源太大,如果网络不稳定,浏览器会受到很大影响。

3. 客户端与服务端作用

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

客户端:需要使用服务时,可以随时取用。

二、网络编程

1. 互联网协议OSI七层协议

  1. 应用层
  2. 表示层
  3. 会话层
  4. 传输层
  5. 网络层
  6. 数据链路层
  7. 物理连接层

1.1 物理连接层

作用:基于电信号发送二进制数据。

1.2 数据链路层

由来:单纯的二进制信号没有意义,必须规定好二进制的意思,即多少个二进制为一组,每组什么意思。

作用:通过“以太网协议”定义了电信号的分组方式。

以太网协议:统一的给电信号的标准,规定电信号构成的数据包必须包含报头和数据。

报头:包含发送者(原地址),接受者(目标地址),及数据类型。

网卡:从来接收分析二进制信号。

mac地址:每块网卡出厂时都会被烧上世界上唯一的MAC地址,由12位16进制数组成(前六位为厂商编号,后六位为流水线号)。

广播:多对多通信(大吼一声)。但不能跨局域网通信,且容易产生广播风暴。

1.3 网络层

由来:需要一种方法来区分那些计算机属于同一广播域。

功能:引入一套新的地址用来区分不同的广播域和子网。

IP:定位局域网的位置。

arp协议:广播的方式发送数据包,获取mac地址,并解析成IP和port。

1.4 传输层

port:用来定位一台计算机上的唯一一个应用程序。

TCP协议

一种流式协议。

若想在TCP协议下通信,必须建立连接,并建立双向通道。

三次握手件连接:

  1. 客户端往服务端发送请求建立通道;
  2. 服务端确认客户端的请求,并往客户端也发送建立通道请求;
  3. 客户端接收到服务端建立连接的请求,并返回确认。

双向通道:

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

四次挥手短连接:

  1. 客户端向服务端发送断开连接请求,断开C2S的单向连接;
  2. 服务端返回确认请求,并把未完成传输的数据传输完成;
  3. 服务端向客户端发送断开连接请求;
  4. 客户端返回确认信息,剩余的一条S2C的连接断开。

UDP协议

一种不可靠的传输协议,有以下特点:

  1. 不需要建立双线通道;
  2. 不会有粘包问题;
  3. 传输速度快;
  4. 数据不安全;
  5. 客户端发送数据,不需要服务端确认收到,爱收不收。

TCP协议与UDP协议的区别

TCP:像在打电话,你侬我侬;

UDP:像在发短信,发了就不管了。

1.5 应用层

  • ftp
  • http:可以携带一堆数据
  • https:http + ssl

2. socket

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

3. subprocess(补充)

通过代码往cmd创造一个管道,并且发送命令和接收cmd返回结果。

4. 粘包问题

发生的原因:

  1. 不能确定对方发送数据的大小;
  2. 在短时间内,间隔时间短,并且数据量小的情况,系统会自动将这些数据打包成一个多次发送的数据,然后一次性发送给接收端。

5. struct解决粘包问题

通过struct模块,将数据的长度这个数据压缩成一个固定长度的报头,发送这个报头后,接收方可以通过这个报头解析出真实发送数据的长度,在通过这个长度接受数据,就不会粘包了。

6. socketserver

  • 可以支持并发的一种socket优化模块(没感觉有什么优化)

三、并发编程

1. 多道技术

1.1 单道

程序串行执行,同步执行

1.2 多道

CPU切换程序并保存状态执行多个程序

  • 空间上的复用

    支持多个程序同时运行

  • 时间上的复用

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

2. 并发与并行

并行:看上去像多个程序同时执行,实际上只是每一个时间点都只运行了一个程序。即多道技术。

并行:真正意义的多个程序同时执行,需要多个CPU,每一个时间点有多个程序在运行。多核技术。

3. 进程

表示正在进行的一个过程,是一种资源单位,每创建一个都会生成一个名称空间,

3.1 程序与进程的区别

程序:就是一堆代码

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

3.2 进程调度

目前使用的基本调度方法为:时间片轮转发+分级反馈队列法

  • 时间片轮转法:将固定时间等分成进程数量的时间片,将每一个时间片分配给每一个进程。
  • 分级反馈队列:时间短的进程进入一级队列,优先运行,时间慢一点的进入二级队列,一级运行完了再运行。。。以此类推。

3.3 进程的三个状态

  • 就绪态

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

  • 运行态

    进程开始运行,之后有两种可能:1. 运行结束;2. 遇到IO,进入阻塞态

  • 阻塞态

    但遇到IO操作,进入阻塞态,IO结束,进入就绪态,等待下一次开始运行。

3.4 同步与异步

指提交任务的方式。

  • 同步:同步提交,串行,一个任务结束后,另一个任务才能提交并执行。

  • 异步:异步提交,并发,无需等待上个任务结束就可提交任务开始,并发执行。

3.5 阻塞与非阻塞

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

3.6 创建进程的两种方式

3.6.1 第一种

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

p.daemon = True  #  守护进程,必须放在start()前,否则报错
p.start()  # 向操作系统提交创建进程的任务
p.join()  # 向操作系统发送请求, 等所有子进程结束,父进程再结束

3.6.2 第二种

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

p = MyProcess()
p.daemon = True  # 守护进程,必须放在start()前,否则报错
p.start()  # 向操作系统提交创建进程的任务
p.join()  # 向操作系统发送请求, 等所有子进程结束,父进程再结束

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

  1. 调用join让子进程结束后,主进程才能正常结束
  2. 主进程自然结束死亡

4. 僵尸进程与孤儿进程(了解)

僵尸进程: 凡是子进程结束后,PID号还在, 主进程意外死亡,没法给子进程回收资源.

  • 每个子进程结束后,都会变成,僵尸进程 (PID)

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

5. 守护进程

一种规则,可以让子进程在父进程结束后二必须结束。

6. 互斥锁

将并发的程序变成并行,保护了数据的安全,不会混乱,但牺牲了执行效率。

from multiprocessing import Lock

mutex = Lock()
# 加锁

mutex.acquire()
修改数据
mutex.release()

7. 队列(进程队列)

# FIFO队列: 先进先出

from multiprocessing import Queue
q = Queue(5)  # 括号内的数字表示队列内可以添加的最大数据量

# 添加数据,若队列添加数据满了,则等待
q.put()
# 添加数据,若队列添加数据满了,直接报错
q.put_nowait()

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

8. 堆栈

LIFO队列。

9. IPC进程间通信

  • 进程间数据是隔离的。
  • 队列可以让进程间进行通信

10. 生产者与消费者 模型

生产者:生产数据

消费者:使用数据

为了保证供需平衡。

11. 线程

11.1 什么是线程

线程:执行单位

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

进程:资源单位

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

11.2 使用线程的好处

节省资源的开销

11.3 进程与线程优缺点

  • 进程:
    • 优点:
      1. 多核下可以并行执行
      2. 计算密集型下可以提高效率
    • 缺点:
      1. 开销资源远高于线程
  • 线程:
    • 优点:
      1. 占用资源远比进程小
      2. IO密集型下提高效率
    • 缺点:
      1. 无法利用多核优势

12. 线程间数据是共享的

同一个进程下可以创建多个子线程,这些子线程可以共用该进程的内存空间,因此,线程间的数据是共享的。

13. GIL全局解释器锁

声明:只有CPython才自带一个GIL全局解释器锁。

  1. GIL的本质是一个互斥锁。
  2. GIL的目的是为了阻止同一个进程内多个线程同时执行(并行)。
    • 单个进程下的多个线程无法实现并行,但能实现并发。
  3. 这把锁主要是因为cpython的内存管理不是“线程安全”的。
    • 内存管理:垃圾回收机制。。

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

14. 多线程使用的好处

  • 多线程:提高IO密集型程序的执行效率
  • 多进程:提高计算密集型程序的执行效率

15. 死锁现象(了解)

a进程用了a锁,b进程用了b锁,这时候a进程还想用b锁,b进程还想用a锁,就会导致死锁。

16. 递归锁(了解)

解决死锁现象

mutex = Lock()  # 只能引用1次
mutex1, mutex2 = RLock()  # 可以引用多次
+1, 只要这把锁计数为0释放该锁, 让下一个人使用, 就不会出现死锁现象.

17. 信号量(了解)

信号量可以理解为一个锁池,可以让池里的这个锁给多个任务一起使用。

sm = Semaphore(5)  可以让5个任务使用

18. 线程队列

使用场景:若想在线程间数据不安全情况下使用线程队列,可以保证线程间数据的安全

import queue

- FIFO: 先进先出队列
    queue.Queue()
- LIFO: 后进先出队列
    queue.LifoQueue()
- 优先级队列:
    - 根据数字大小判断,判断出队优先级.
    - 进队数据是无序的
    queue.PriorityQueue()

19. event事件

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

e = Event()

  • 线程1

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

  • 线程2

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

20. 进程池与线程池

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

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()

21. 协程

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

进程:资源单位

线程:执行单位

21.1 单线程下实现并发的好处

节省资源,单线程<多线程<多进程

  • IO密集型下:协程有优势
  • 计算密集型下:进程有优势
  • 高并发:多进程+多线程+协程 (Nginx)

21.2 协程的创建

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

  • yield
  • 函数一直在调用next()
    • 会不停的切换

yield不能监听IO操作的任务,可以使用gevent来实现监听IO操作。

22. 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])

23. IO模型(了解)

  1. 阻塞IO
  2. 非阻塞IO
  3. 多路复用IO
  4. 异步IO

你可能感兴趣的:(网络编程与并发编程总结)