网络编程与并发编程总结
- 2019 年 12 月 16 日
- 笔记
软件开发架构:
C/S架构:
Client:客户端
Server:服务端
优点:占用网络资源少,软件的使用更加稳定。
缺点:服务端更新后,客户端也得跟着更新,访问多个服务端需要下载对应的软件,占用客户端计算机的硬件资源大。
B/S架构:
Browser:浏览器
Server:服务端
服务端:24小时不间断提供服务
客户端:访问不同的服务端只需要在浏览器输入不同的网址,占用客户端的硬件资源少,但是占用网络资源大,网速比较慢时会不稳定。
一、网络编程:
1.互联网协议OSI七层协议
- 应用层
- 表示层
- 会话层
- 传输层
- 网络层
- 数据链路层
- 物理连接层 记忆:应表会传网数物
-物理连接层
基于电信号发送二进制数据
-数据链路层
1)规定好电信号的分组方式
2)必须要有一块网卡:
-mac地址:
12位唯一的16进制字符串:前六位为厂商号,后六位为流水号
-以太网协议:
在同一个局域网内通信:
单播:1对1吼
广播:多对多吼(会出现广播风暴)
不能跨局域网通信。
-网络层
ip:定位局域网的位置
port:唯一标识一台计算机上一个应用程序。
arp协议:将mac地址获取并解析成ip和port。
-传输层 TCP,特点:TCP协议称为流式协议,若想要通信,必须建立连接
#### 1.1 TCP协议的三次握手:
客户端往服务端发送建立连接请求,服务端回复收到请求同时发送服务端与客户端建立连接的请求,客户端回复收到建立连接的请求,双向通道建立完成。
#### 12. TCP协议的四次挥手:
服务端向客户端发送断开连接请求,客户端回复收到请求,然后检测自己是否有数据在给客户端发送,如果没有则向客户端发送断开连接请求,客户端回复同意断开连接信息,客户端与服务端断开连接。
双向通道反馈机制:客户端向服务端发送获取数据请求,客户端发送确认收到的消息,如果服务端没有返回消息,客户端则会继续隔段时间发送一次请求,如果时间过长仍没有收到回复则停止发送请求。
1.2 UDP协议
特点:
- 数据不安全
- 不需要建立双向通道
- 客户端发送数据给服务端不需要收到服务端的确认消息
- 传输速度快
- 不会有粘包问题
TCP与UDP的区别:
TCP:如同是在打电话
UDP:如同是在发短信
应用层
ftp
http
http+ssl
2.socket
socket用来写套接字客户端与服务端,内部帮我们封装好了7层协议需要做的事情。
3.手撸socket套接字模板
3.1 服务端
import socket
server = socket.socket()
server.bind(('127.0.0.1',6666))括号内是ip和端口号,且是元组的形式
server.listen(6)半连接池
conn,addr = server.accept()
data = conn.recv(1024)接收数据的二进制位长度
conn.send('发送的消息'.decode('utf-8'))
3.2 客户端
import socket
client = socket.socket()
client.connect(('ip',port))
client.send()
client.recv(1024)
4.subprocess(了解)
功能:通过代码往cmd创建一个管道,并且发送命令和接收cmd返回的结果。
用法:
import subprocess
obj = subprocess.Ponpen('cmd命令',shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
success = obj.stdout
error = obj.stderr.read()
msg = success + error
5.粘包问题
1)不能确定对方发送数据的大小
2)在短时间内,间隔时间短,并且数据量小的情况下,默认将这些数据打包成一个,多次发送的数据一次性发送。
6.struct解决粘包问题
初级版:
将一个数据的长度打包成一个固定长度的报头,struck.pack('i',len(data))
对方获取数据时用
data = struct.unpack('i',headers)[0]
注意:以什么方式打包必须以什么方式解包
升级版:
先将数据存放到字典中,将字典打包发送过去:好处,真实数据长度,文件的描述信息,发送的数据更小。
dic = {
'data_len':10000,
文件的描述信息
}
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(dara)
init_recv += len(data)
8.socketserver(现阶段,了解)
可支持并发
import socketserver
定义类
TCP:必须继承BaseRequestHandler类
class MyTcpServer(socketserver.BaseRequestHandler)
-handle
内部实现了server = socket.socket()
server.bind(('127.0.0.1',6666))
server.listen(5)
while True:
conn,addr = server.accept()
print(addr)
必须重写父类的handle。当客户端连接时会调用该方法
def handle(self):
print(self.client_address)
while True:
request.recv(1024)
self.request.send()
TCP:
SOCK_STREAM
conn.recv
UDP:
SOCK_DGRAM
server.refrom()
8.1 UDP套接字模板:
服务端:
import socket
server= socket.socket(type=socket_SOCK_DGRAM)
server.bind((ip,port))
data,addr = server.refrom(1024)
客户端
import socket
client = socket.socket(type=socket.SOCK_DGRAM)
ip_port = (ip,port)
client.sendto(data,ip_port)
data,_ = client.refrom(1024)
print(data)
二、并发编程
多道技术
多道:切换+保存状态
-空间上的复用:支持多个程序使用
-时间上的复用:遇到IO操作就会切换程序,程序占用CPU时间过长也会被切换。
1.并发与并行:
并发:看起来像同时运行:多道技术 并行:真正意义上的同时运行:多核下 进程: 进程是资源单位,每创建一个进程都会生成一个名称空间,占用内存资源。 程序就是一堆代码 进程就是一堆代码运行的过程
2.进程调度:
时间片轮转法: 10个进程,将固定时间等分成10等份,分配给每个进程
分级别反馈队列 时间短的,执行权限最高放在一级队列,时间长一点的放下面 1级别: 2级别: 3级别:
3.进程的三个状态:
就绪态:创建多个进程,排队准备运行 运行态:进程开始运行,结束、阻塞 阻塞态:遇到IO操作进入,阻塞态之后进入就绪态 占用CPU时间过长会进入就绪态
4.同步与异步:
同步异步是提交任务的方式,同步指同步提交,是串行的,一个任务结束另一个任务才能提交执行;异步指异步提交,多个任务可以并发运行
5.阻塞与非阻塞
阻塞:阻塞态 非阻塞:就绪态、运行态
同步和异步,阻塞和非阻塞是不同的概念不能混为一谈。 等待不一定是阻塞,有可能某个任务占用CPU的时间过长, 所以他们不是同一个概念。
最大化提高CPU的使用率:尽可能减少不必要的IO操作
### 6.创建进程的两种方式 1. p = Process() p.start()向操作系统提交创建进程的任务 p.join() p.daemon = True 必须放在start()前面,否则报错 2. class MyProcess(Process): def run(self):
任务的过程
p = MyProcess()
p.daemon = True 必须放在start()前,否则报错
p.start()
p.join()
7.回收进程的两种条件:
1.调用join让子进程结束后子进程结束 2.主进程正常结束
### 8.僵尸进程与孤儿进程 僵尸进程:凡是子进程结束后pid号还存在的进程 父进程已经结束
孤儿进程:主进程已经结束,子进程还在运行
守护进程:只要主进程结束,所有添加守护进程的子进程都必须结束
9.互斥锁:
保证数据安全 from multiprocessing import Lock mutex = Lock() mutex = acquire() mutex = release()
10.队列
from multiprocessing import Queue q = Queue(5) q.put() q.put_nowait() q.get() q.get_nowait()
堆栈:LIFO
10.1 IPC进程间通信
进程间的数据是隔离的 队列可以让进程间通信 把一个程序放入队列中,另一个程序从队列中获取,实现进程间的数据交互。
10.2 生产者与消费者模型
生产者:生产数据 消费者:使用数据
为了保证平衡:通过队列实现 生产者将数据扔进队列中,消费者从数据中去数据
11.线程
11.1 什么是线程
进程是资源单位 线程是执行单位
创建进程时会自带一个线程(主线程)
11.2进程与线程的优缺点
进程: 优点:多核下计算密集型程序 缺点:开销资源高于线程
线程;优点:占用资源小 IO密集下,提高效率 缺点:无法使用多核优势
线程间数据是共享的
12.全局解释器锁
在CPython中,全局解释器锁(即GIL)是一个互斥锁,可以防止一个进程中的多个线程同时(并行)执行。 锁定是必要的,主要是因为CPython的内存管理不是线程安全的。GIL的存在就是为了保证线程安全。
### 13.死锁现象
所谓死锁:是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程
14.递归锁
解决死锁现象 mutex1,mutex2 = RLock() 只有这把锁的计数为零才会被下一个人使用
15.信号量
也是一把锁,可以让多个人,多个用户一起使用 sm = Semaphore(5)
16.线程队列:保证线程间数据的安全
import queue FIFO:先进先出队列 queue.Queue()
LIFO:后进先出队列 queue.LifoQueue() 优先级队列 queue.PriorityQueue()
17.event事件
可以控制线程的执行,让一些线程控制另一些线程的执行
线程池与进程池 为了控制进程、线程的创建的数量,保证了硬件能正常运行。
from concurrent.futures import ProcessPoolExecutor,TreadPoolExecutor
pool1 = ProssPoolExecutor()默认是CPU的个数
pool2 = ThreadPoolExecutor()默认是CPU个数*5
18.回调函数
pool.submit(函数名,参数).add_done_callback(回调函数名)
注意:回调函数必须接收一个参数,且这个参数是第一个函数的返回值。
res1 = res.result() 获取返回值。
19.协程
协程:单线程下实现并发,不是任何的单位,是程序员YY出来的名字。
协程的优点:节省内存资源,进一步提高CPU的利用率(只有在IO密集型程序中才有优点)
高并发:多进程+多线程+协程
19.1 协程的手动创建:
手动实现切换+保存状态:
yield+next:yield对程序的暂停不会被操作系统识别为IO操作
19.2 使用gevent实现单线程下的并发
from gevent import monkey
monkey,patch_all()监听是否有IO操作
from gevent import spawn,joinall 实现切换+保存状态
s1 = spawn(任务1)
s2 = spawn(任务2)监听是否有IO操作
joinall([s1,s2])等待所有的程序执行完毕结束,执行下面的程序。
20.IO模型(了解)
阻塞IO
非阻塞IO
多路复用IO
异步IO