网络编程与并发变编程复习

软件开发架构

c/s:
Client:客户端

​ Server:服务端

​ 优点:

​ 占用网路资源少,软件的使用的稳定

​ 缺点:

​ 服务端更新后,客户端也得跟着更新

​ 需要使用多个软件,需要下载多个客户端

B/S:

​ Browser:浏览器(客户端)

​ Server:服务器

服务端与客户端作用:

​ 服务端:24小时不间断提够服务

​ 客户端:需要体验服务端时,再去连接服务端,并享受服务

一网络编程

1 互联网协议OSI七层协议

​ 1)应用层

​ 2)表示层

​ 3)会话层

​ 4)传输层

​ 5)网络层

​ 6)数据链路层

​ 7)物理连接层

​ —物理连接层:基于电信号发送二进制数据

​ —数据链路层:规定好电信号的分组方式,必须有网卡

​ Mac地址:12位唯一的16进制字符串

​ 前6位:厂商号

​ 后6位:流水号

​ —以太网协议:在同一局域网内通信

​ 单播:1对1对话

​ 广播:多对多对话

​ —广播风暴

​ —不能跨局域网通信

​ —网络层

​ — ip:定位局域网的位置

​ —port:唯一标识一台计算机上一个应用程序

​ —ARP协议:将Mac地址获取,并解析程ip和port

​ — 传输层

​ —TCP特点:TCP协议称之为流失协议

​ 若想要通信,必须建立连接,并建立双向通道

1572006920181

​ —三次握手,四次挥手

​ — 三次握手建连接

​ —客户端往服务端发送请求建立连接

​ —服务端要确认客户端的请求,并往客户端也发送请求建立连接

​ —客户端接收到服务端建立连接的请求,并返回确认

​ —建立双向通道

​ —双向通道:

​ —反馈机制

​ 客户端往服务端发送请求获取数据,服务端务必返回数据,客户端确认收到

​ 反则会反复发送,一直到某个时间段内,会停止发送

​ —四次挥手断连接

​ —客户端往服务端发送断开连接请求,服务端返回确认收到

​ —服务端再次发送断开连接请求

​ —客户端确认收到

​ —最终确认断开连接

1572006966624

​ —UDP

​ 1)数据不安全

​ 2)不需要建立双向通道

​ 3)传输速度快

​ 4)不会有粘包问题

​ 5)客户端发送数据,不需要服务端确认收到,爱收不收

TCP与UDP的区别:

​ TCP:比喻成再打电话

​ UDP:比喻成发送短信

​ —应用层

​ —FTP

​ —HTTP:可以携带一堆数据

​ —HTTP + SSL

2 socket

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

3 手撸socket套接字模板

服务端

import socket

server = socket.socket()

server.bind((ip,port))

server.listen(5) # 半连接池:可以接待6个客户端

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 = 'gsjfdfytaytsf'

headers = struct.pack('i' ,len(data)) # 打包成报头

data = struct.unpack('i' ,headers)[0] # 解包获取数据的真实长度

注意:以什么方式打包,必须以什么方式解包

升级版:
先将数据存放到字典中,将字典打包发送发送过去

​ —字典的好处:
​ —真实数据长度

​ —文件的描述信息

​ —发送的数据更小

​ dic = {'data_len':10000000000000000000000000444444444444444444,

​ 文件的描述信息}

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,8080'))

​ server.listen(5) --------

​ while True:
​ conn,addr = server.accept()

​ print(addr)

必须重写父类的handle,当客户端连接时回调用该方法

def handle(self):

​ print(self.client_adddress)

​ while True:

​ try:

​ 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 sa 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份时间片,分配给每一个进程

1572007058606

​ —分级反馈队列

​ 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 iport Lock

​ mutex = Loock()

​ mutex.acquire() # 加锁

​ mutux.release() # 修改数据

18 队列

1572007120267

​ 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全局解释器锁

1572007172487

​ —只有cpython才有自带一个GIL全局解释器锁

​ 1 GIL本质上就是一个互斥锁

​ 2 GIL为了阻止同一个进程内多个线程同时执行(并行)

​ —单个进程下的多线程无法实现并行,但能实现并发

​ 3 这把锁主要是因为cpython的内存管理不是“线程安全”的

​ —内存管理

​ —垃圾回收机制

​ 注意:多个线程过来执行,一旦遇到io操作,机会立马释放GIL解释器锁,交给下一个先进来的线程

​ 总结:GIL存在的目的就是为了保证线程阿安全的,保证数据安全

25 多线程使用的好处

​ —多线程:
​ io密集型,提高效率

​ —多进程

​ 计算密集型,提高效率

26 死锁现象(了解)

1572007200046

画图

27 递归锁(了解)

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

28 信号量

​ 信号量也是一把锁,可以让多个任务一起使用

​ 互斥锁:

​ 只能让一个任务使用

​ 信号量:

​ 可以让多个任务一起使用

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

29 线程队列

​ 使用场景:
​ 若线程间数据不安全情况下使用线程队列,为了保证线程间数据的安全

​ import queue

​ —FIFO:先进先出队列

​ queue.Queue()

​ —LIFO:后进先出队列

​ —根据数字大小判断,判断出队友的优先级

​ —队列数据是无序的

​ 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			

posted @ 2019-10-25 22:15  ^啷个哩个啷$  阅读(96)  评论(0编辑  收藏  举报