网络编程与并发编程总结

  • 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