网络编程
架构:程序员开发的一种模式
c/s架构 客户端/服务器模式
b/s架构 Browser浏览器/服务器模式
B/S 架构的优势:统一了应用的接口
mac地址:物理地址
mac地址是12位十六进制组成
ip地址:四位点分十进制组成(ipv4)
端口:操作系统 为每一个应用程序分配一个端口号
ip+端口号 唯一确定某一个电脑上的某一程序
arp协议:通过目标ip地址,获取目标mac地址
确定在局域网内:ip地址&子网掩码
路由器:
有一个路由表,记录了归他管的所有网段
协议:由多人制定的一种规则
TCP:安全可靠的通信方式,面向连接,数据流形式传输
UDP:不安全,不可靠,但是速度快,数据包形式传输
tcp和udp的区别:
TCP---传输控制协议,提供的是面向连接、可靠的字节流服务。当客户和服务器彼此交换数据前,必须先在双方之间建立一个TCP连接,之后才能传输数据。TCP提供超时重发,丢弃重复数据,检验数据,流量控制等功能,保证数据能从一端传到另一端。
UDP---用户数据报协议,是一个简单的面向数据报的运输层协议。UDP不提供可靠性,它只是把应用程序传给IP层的数据报发送出去,但是并不能保证它们能到达目的地。由于UDP在传输数据报前不用在客户和服务器之间建立一个连接,且没有超时重发等机制,故而传输速度很快
tcp三次握手和四次挥手:
三次握手:建立了一个全双工的通信链接
(面试回答)
首先,必须先由客户端发起连接的请求
接下来,服务器接收到请求之后,回复给客户端两个标识,一个syn表示
服务器接收到请求,一个ack表示服务器在做准备工作,两个标识一起
回复给客户端
最后,客户端接收到服务器的回复,客户端准备连接的所有资源,开始进行连接
发送给服务器一个ack表示客户端的连接准备工作已经完成
(此时表示客户端和服务器可以相互连接了)
如果面试官问你,哪句代码体现了三次握手?
回答: 服务器端的accept,客户端connect
四次挥手:
(面试回答)
(1)首先由连接双方任意一方发起断开连接的请求,发起方发送的请求表示
是我没有数据要继续发送了,可以断开连接了,但是你如果还有数据可以继续向我发送数据.
(2)接收方回复给发起方,表示接到了发起放的断开请求,开始着手准备断开事宜
(3)接收方准备完成后,给发起方发送一个标识,表示接受方没有数据继续发送了
可以断开连接了
(4)发起方接收到消息后,准备断开连接,回收资源
如果面试官问你,哪句代码体现了四次挥手?
回答: close()
ois七层模型:
应用层:py代码,应用
表示层
会话层
传输层:tcp和udp协议 四层路由器
网络层:ip协议 路由器 三层交换机
数据链路层:arp协议 网卡 交换机
物理层:网线,集线器
send 和 sendall 的区别:
对于程序员来说,用起来是没有什么区别的
实际上,在socket底层对于两个方法的封装有却别:
send(num) 此方法会尝试先发送n个数据(n<num),接下来再尝试发送num-n
sendall(num) 此方法会尝试一次性将num个数据发送出去
基于文件类型的套接字: AF_UNIX
基于网络类型的套接字: AF_INET
黏包:就是因为接收端不知道如何接收数据,造成接收数据的混乱的问题
只发生在tcp协议上. 因为tcp协议的特点是面向数据流形式的传输
粘包的发生主要是因为tcp协议有两个机制: 合包机制(nagle算法),拆包机制
解决黏包的方案:使用struct模块来确定传输了多少字节
tcp的聊天
import socket udp_sk = socket.socket(type=socket.SOCK_DGRAM) #创建一个服务器的套接字 udp_sk.bind(('127.0.0.1',9000)) #绑定服务器套接字 msg,addr = udp_sk.recvfrom(1024) print(msg) udp_sk.sendto(b'hi',addr) # 对话(接收与发送) udp_sk.close() # 关闭服务器套接字 srever
client端 import socket ip_port=('127.0.0.1',9000) udp_sk=socket.socket(type=socket.SOCK_DGRAM) udp_sk.sendto(b'hello',ip_port) back_msg,addr=udp_sk.recvfrom(1024) print(back_msg.decode('utf-8'),addr)
socketserver
# 正常情况下,tcp协议的socket server,同一时刻只能处理一个请求
# 使用socketserver.ThreadingTCPServer就可以让你的tcp协议的server端同时接受多个客户端的请求
# 基于socket实现的
# serverforever方法
# 启动了一个socket的server端
# socketserver启动之后就会一直对外提供服务
# 一个client服务结束了之后,socketserver会帮助你conn.close
# 但是sk对象永远不会停止服务
并发编程:
操作系统的三种基本类型:多道批处理系统(时空复用,空间隔离)、分时系统、实时系统。
操作系统是一个协调管理和控制计算机硬件资源和软件资源的控制程序.
进程:是系统进行资源分配和调度的基本单位,是操作系统结构的基础,进程是正在运行的程序的实例(an instance of a computer program that is being executed)。广义定义:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。
并发:
并发是指资源有限的情况下,两者交替轮流使用资源,比如一段路(单核CPU资源)同时只能过一个人,A走一段后,让给B,B用完继续给A ,交替使用,目的是提高效率。
进程的状态:就绪,运行,阻塞
同步:所谓同步就是一个任务的完成需要依赖另外一个任务时,只有等待被依赖的任务完成后,依赖的任务才能算完成,这是一种可靠的任务序列。要么成功都成功,失败都失败,两个任务的状态可以保持一致。
异步:所谓异步是不需要等待被依赖的任务完成,只是通知被依赖的任务要完成什么工作,依赖的任务也立即执行,只要自己完成了整个任务就算完成了。至于被依赖的任务最终是否真正完成,依赖它的任务无法确定,所以它是不可靠的任务序列
。
异步非阻塞:
效率更高,
因为打电话是你(等待者)的事情,而通知你则是柜台(消息触发机制)的事情,程序没有在两种不同的操作中来回切换
。
比如说,这个人突然发觉自己烟瘾犯了,需要出去抽根烟,于是他告诉大堂经理说,排到我这个号码的时候麻烦到外面通知我一下,那么他就没有被阻塞在这个等待的操作上面,自然这个就是异步+非阻塞的方式了。
进程的创建:系统初始化,一个进程运行过程中开启了子进程,用户交互式请求,一个批处理作业初识化
进程的结束:正常退出,出错退出,严重退出,被其他进程杀死
进程中:创建进程,锁,队列,进程池
数据隔离:进程与进程之间内存中的数据是互相隔离的
主进程创建守护进程(报活)
其一:守护进程会在主进程代码执行结束后就终止
其二:守护进程内无法再开启子进程,否则抛出异常:AssertionError: daemonic processes are not allowed to have children
信号量的原理:锁+计数器
锁的分类:
在多个进程\线程同时访问一个数据的时候就会产生数据不安全的现象
# 多进程 访问文件
# 多线程
# 同时去访问一个数据
# 尽量不要设置全局变量
# 只要在多线程/进程之间用到全局变量 就加上锁
互斥锁:
递归锁:
全局解释器锁:
死锁出现的原因:多把锁同时应用在多个线程中
互斥锁和递归锁哪个好
# 递归锁 快速恢复服务
# 死锁问题的出现 是程序的设计或者逻辑的问题
# 还应该进一步的排除和重构逻辑来保证使用互斥锁也不会发生死锁
# 互斥锁和递归锁的区别
# 互斥锁 就是在一个线程中不能连续多次ACQUIRE
# 递归锁 可以在同一个线程中acquire任意次,注意acquire多少次就需要release多少次
事件 : 通过一个标记来控制对多个进程进行同步控制
IPC通信
# 队列 管道+锁
# 管道 是队列的底层
管道 pipe
# IPC通信的一种机制,队列就是基于管道来完成通信的,但是管道是原生的通信方式,
# 在进程之间会产生数据不安全的情况,需要自己手动加锁来处理.
# 管道在数据传输过程中,还涉及到一个端口管理,这个需要我们在代码中做处理才能使代码更完善.
(了解)# 数据共享 _ 进程就是数据隔离的
# Manager模块
# 数据类型 都能够进行数据共享
# 一部分都是不加锁 不支持数据进程安全
# 不安全的解决办法 加锁
# 进程池
# 进程不能无限开 会给操作系统调度增加负担
# 且真正能被同时执行的进程最多也就和CPU个数相同等
# 进程的开启和销毁都要消耗资源和时间
进程池
# 什么时候用进程池
# 面向高计算型的场景 采用多进程
# 如果开启的进程数超过5个
# 有几个CPU就能够同时运行几个进程
线程:线程
# 线程是CPU调度的最小单位
# 每个进程中至少有一个线程
# 实际上执行代码的是线程
线程属于进程
# 进程负责获取操作系统分配给我的资源
# 线程负责执行代码
从代码的角度上来看
# 多进程
# 开启和结束 时间开销大
# 切换的效率低
# 内存隔离
# 多线程
# 开启和结束 时间开销非常小
# 切换效率高
# 内存不隔离
# 线程进程之间的对比
# 线程不能独立存在,必须在一个进程里
# 线程的开启 关闭以及切换的开销要远远小于进程
# 同一个进程之间的多个线程之间数据共享
# 什么时候才会有到CPU
# 程序计算的时候
# IO阻塞
# 是不会用到CPU的
池 和 信号量
# 池 效率高
# 池子里有几个一共就起几个
# 不管多少任务 池子的个数是固定的
# 开启进程和关闭进程这些事都是需要固定的开销
# 就不产生额外的时间开销
# 且进程程池中的进程数控制的好,那么操作系统的压力也小
# 信号量
# 有多少个任务就起多少进程/线程
# 可以帮助你减少操作系统切换的负担
# 但是并不能帮助你减少进/线程开启和关闭的时间
协程:
能在一条线程的基础上,在多个任务之间互相切换
# 节省了线程开启的消耗
# 是从python代码的级别调度的
# 正常的线程是CPU调度的最小单位
# 协程的调度并不是由操作系统来完成的
需要安装第三方模块:greenlet,gevent
# 5个进程
# 20个线程
# 500个协程
# 不存在数据不安全
# 也不存在线程切换\创造的时间开销
# 切换是用户级别的,程序不会因为协程中某一个任务进入阻塞状态而使整条线程阻塞
# 线程的切换
# 时间片到了 降低CPU的效率
# IO会切 提高CPU效率
# 阻塞IO
# 正常的socket服务都是阻塞IO
# 只要是遇到阻塞,整个进程/线程都会进入阻塞状态
# 降低了CPU的使用率,
# tcp协议起socket server的时候有一个请求进入阻塞,其他的对象都不能接收请求
# 解决方案 :
# socketserver/协程 # 正确的
# 多线程/多进程 有多少个client就要开多少线程/进程 # 不好
# 池 : 能够同时处理的client是有限的,和池的个数相等 # 不好
# 非阻塞IO
# sk.setblocking(False)
# 在遇到IO的时候不阻塞,程序能够利用不阻塞的时间做其他事情
# 内部是使用while True在做其他任务的询问,导致CPU的使用率过大
# IO多路复用
# io多路复用是操作系统提供的一种 监听 网络IO操作的机制
# 监听三个列表
# 当某一个列表有对应的事件发生的时候
# 操作系统通知应用程序
# 操作系统根据返回的内容做具体的操作
# 对于只有一个对象需要监听的情况 IO多路复用并无法发挥作用
# 对于并发接收网络请求的应用场景 IO多路复用可以帮助你在节省CPU利用率和操作系统调用的基础上完成并发需求
# 操作系统提供的,监听网络IO操作的机制,它是一个代理,可以同时监听多个连接对象
# select 操作系统在内部帮助你轮询所有被监听的对象,受监听对象的数量的影响
# poll 能够监听的网络对象数量多,优化了底层的数据结构,受监听对象的数量的影响
# epoll 内部使用了回调函数的方式,减少操作系统的轮询消耗资源
# 异步IO
# 数据copy阶段做了优化 这是上面所有的IO模型都没有做到的