又到一年的年末了,各个“心怀鬼胎”的程序猿们要开始进行准备下,迎接来年跳槽大会了。而面试呢,都躲不掉问下网络编程的一些知识,在此,收集了较全的基础知识点,希望能帮助到大家。
OSI七层模型是以下七层:
七层是国际标准,常用呢把上三层合并成一个为一层,叫应用层;把物理层和链路层合并为链路层。
TCP是提供可靠传输的一种协议,可靠两字就来自要连接通信时需要经过三次握手,断开时需要经常四次挥手。具体如图:
基于这个模型应用而生的数据传输工具就叫流事套接字(SOCK_STREAM),一种 套接字类型。tcp套接字以字节流的方式传输消息,没有消息边界,自然会产生粘包问题,需要人工处理下。
处理粘包:
1. 人为添加消息边界
2. 将消息结构化
3. 控制消息发送速度
举例:在c/s模型简单应用如下:
server端:
#创建套接字对象
server= socket(socket_family=AF_INET,socket_type=SOCK_STREAM,proto=0)
server.bind((address,port)) #绑定地址
server.listen(backlog)#监听
client,addr=s.accept() #阻塞,等待接收连接请求
client.recv(buffersize) #接收客户端发来的消息
client.send(message) #向客户端发送消息
client.close()
server.close() #关闭连接
client端
#创建套接字对象
client= socket(socket_family=AF_INET,socket_type=SOCK_STREAM,proto=0)
client.connect(address) #客户端连接服务器
client.send(message) #向服务端发送消息
client.recv(buffersize) #接收服务端发来的消息
client.close() #关闭连接
UDP则提供的是非面向连接的、不可靠的数据传输。以数据报的方式传输。适用于一次只传送少量数据、对可靠性要求不高的应用。
UDP的c/s模型:
server端
#创建套接字对象
server= socket(socket_family=AF_INET,socket_type=SOCK_DGRAM,proto=0)
server.bind((address,port)) #绑定地址
data,addr = server.recvfrom(buffersize) #接收客户端发来的消息
server.sendto(message,addr) #向客户端发送消息
server.close() #关闭连接
client端
#创建套接字对象
client= socket(socket_family=AF_INET,socket_type=SOCK_DGRAM,proto=0)
client.sendto(message,addr) #向服务端发送消息
data,addr = client.recvfrom(buffersize) #接收服务端发来的消息
client.close() #关闭连接
对比下其实tcp和udp的模型很相似。他们间的区别就以下几点:
http协议用途 : 网页的获取,数据的传输
特点 :
1. 应用层协议,传输层采用tcp方式收发消息
2. 简单,灵活,很多语言都有http协议接口
3. 无状态的协议,协议本身不要求记录传输数据
4. http1.1 支持持久连接
网页请求过程:
IO ,什么是IO操作:
和终端交互 : input print
和磁盘交互 : read write
和网络交互 : recv send
因为某种条件没有达成造成的函数阻塞的IO操作则是阻塞IO,
当然将原本阻塞的函数通过属性的设置改变阻塞行为,变为非阻塞IO。
常见有两种。
sockfd.setblocking(bool) #设置套接字为非阻塞IO
sockfd.settimeout(sec) #设置套接字超时时间
同时监控多个IO事件,选择其中能够执行的IO进行IO 事件处理. 以此形成可以同时操作多个IO的行为模型则是IO多路复用。方法有以下三种
rs,ws,xs=select(rlist, wlist, xlist[, timeout]) #创建方法,用于监控IO事件,阻塞等待IO事件发生。
例子有点长,我单独写在一篇里,连接为:https://blog.csdn.net/weixin_43778491/article/details/86557115
这里有几个注意事项。注意 : 1. IO多路复用占用计算机资源较少,效率较高
2. wlist 中如果有IO则select立即返回处理
3. 在IO处理过程中不要出现死循环,影响IO 监控
使用方法
创建套接字 ,poll对象,建立fileno查找字典
server =socket ()
p = poll()
fdmap={server.fileno:server} #server.fileno即为server的IO对象
设置套接字为关注
p.register(server.fileno(),event) #event 为要关注的事件
循环监控IO
while True:
events=p.poll()
for fd,e in events:
pass
处理发生的IO
使用方法 : 基本同poll相同
epoll特点 :
一种思想,充分利用计算机的多核资源,同时运行多个任务,以提高程序的执行效率
进程:程序在计算机中的一次运行过程。进程有两个模型,一个叫进程三态,一个叫五态。如图:
使用模型:
pid = os.fork()
if pid < 0:
print("error")
elif pid == 0:
function1() #子进程做的事情
else:
function2() #父进程做的事情
这里就经常有个叫僵尸进程的面试问题:
僵尸进程 : 子进程先于父进程退出,且父进程没有处理子进 程退出行为,此时子进程就会成为僵尸进程。僵尸进程虽然结束但是会存留部分进程信息在内存中,大量 的僵尸进程会消耗系统资源,因此应该避免僵尸进程产生。
解决方法:通过创建二级子进程去解决,让二级子进程变成孤儿进程
当然也可以设置信号来处理,也是变成孤儿进程: signal.signal(signal.SIGCHLD,signal.SIG_IGN)
使用模型:
def func(*args):
pass
p = Process(target=func,[name=‘process’], args=(),kwargs={})
p.start()
p.join()
这里的join可以处理僵尸进程
这里的进程有些属性说明下:
p.name 进程名称
p.pid 进程号
p.is_alive() 进程状态(查看是否在生命周期)
p.daemon()
默认是False 表示主进程退出,不会影响子进程继续运行,如果设置为True 此时主进程退出,子进程也会结束
要求在start()前设置
进程池:创建一定量的进程作为进程池,用来处理事件. 事件处理完毕后,进程不退出,而是继续等待处理其他事件.直到所有待处理事件处理完毕后统一销毁进程.增加了进程的重复利用,降低资源消耗.
使用模型:
pool = Pool(processes ) #processes为进程数量
pool.apply_async(func,args,kwds) #func 事件函数,args 元组 给函数按位置传参,kwds 字典 给函数按键值传参,这里是异步放入要执行的事件函数
(pool.apply(func,args,kwds)是同步放入要执行的事件函数)
pool.close()
pool.join()
进程空间相对独立,资源无法直接获取.此时在不同的进程间需要进行消息传输,即进程间通信。
管道 | 消息队列 | 共享内存 | |
---|---|---|---|
开辟空间 | 内存 | 内存 | 内存 |
读写方式 | 两端读写 | 先进先出 | 覆盖之前内容 |
效率 | 一般 | 一般 | 较高 |
使用特点 | 多用于父子进程 | 第三方库较多 | 操作需要注意争夺内存资源 |
原理:在内存中开辟管道空间,生成管道操作对象,进程间使用同一组管道对象进行读写实现通信
这里设计到单管道和双管道。
原理:在内存中建立队列模型,进程通过队列对象将消息存入到队列,或者从队列取出消息,完成进程。遵从先进先出原则。
原理: 在内存中开辟一个区域,对多进程可见,进程可以 写入内容或读取内容,但是每次写入的内容都会覆盖之前的。由于覆盖,则会产生争夺内存资源问题。使用时需要配合同步互斥一起使用。
Semaphore 原理 : 给定一个数量,对多个进程可见.多个进程可以通过方法操作这个数量,达到协同工作的目的这个就是控制数量,和内容通信无关。
使用模型
sem = Semaphore(num) #创建信号量
sem.acquire() #将消耗一个信号量,当信号量为0会阻塞
func() #需要做的函数,
sem.release() #当函数事件执行完毕则增加一个信号量资源。
signal 原理 : 通过获取进程号,对其发出系统信号,来控制进程。
使用模型:
def fun3(sig,frame):
os.kill(os.getppid(),SIGUSR1)
def fun4(sig,frame):
os.kill(p.pid,SIGINT)
def spy():
signal(SIGINT,fun3)
while True:
fun2()
p = Process(target=fun1)
p.start()
signal(SIGUSR1,fun4)
th2.join()
线程定义:
线程特征:
线程的同步互斥 !!!很重要
共享资源(临界资源): 多个线程都可以操作的资源称为 共享资源
临界区 : 指一段代码,对临界资源操作的代码段
同步: 同步是一种合作关系,为完成任务,多个进程或者 线程之间形成一种协调调度,按照必要的步骤有 序执行一系列操作
互斥: 互斥是一种制约关系,当一个进程或者线程使用 临界资源时会进行加锁处理,此时另一个进程或 者线程就无法操作,直到解锁后才能操作
和进程的创建差不多。就不重复了。
通信方法 :使用进程空间中的全局变量通信
注意事项 : 共享资源争夺,往往需要同步互斥机制协调
协调有两个办法,要给是Event
通过设置事件对象的设置状态来,协调。即A线程需要等B线程运行结束时,设置事件对象为true时才可以运行,否则阻塞。
e = Event() #创建事件对象
e.set() 将e变为被设置状态
e.wait([timeout]) 果e是未设置的状态则阻塞
e.clear() 清除e的设置
lock 这个原理也差不多。
A,B线程用同一个lock锁, 当A线程运行了,就使用了lock,当运行完再释放lock,B线程运行也要使用lock,当lock被A线程使用了则只能等待A用完才能继续运行。
定义 : 纤程,微线程.是为非抢占式多任务产生子程序的计 算机程序组件.协程允许不同入口点,在不同位置暂停或者开始执行,简单来说,协程就是可以暂停执行的函数。
协程原理: 记录一个函数栈的上下文,进行协程的切换调度. 当一个协程函数暂停执行时会将上下文栈帧保存 起开,在切换回来时恢复到原来的执行位置,从而 继续执行。
协程优点:
1. 协程可以同时处理多个任务
2. 协程本质是单线程,资源消耗少
3. 协程无需切换的开销,无需同步互斥
协程缺点:
1. 无法利用计算机多核
大部分答案在前面都有介绍
1.进程线程的区别有哪些
进程是系统资源分配的最小单位,线程是系统CPU运算的最小单位
一个进程是由1个或者多个线程。
进程的空间独立,数据相互不干扰,所以需要占用较多系统资源,线程是共有享进程的资源,占用的资源较少,线程拥有高并发特性
真正在CPU上运行的是线程
2.在什么情况下使用进程或线程
多进程适合在CPU密集的操作(例如科学计算,位数较多的运算)
多线程适合IO密集的操作(读写较多的操作,爬虫)
3、什么是同步互斥,你在什么情况下使用,如何使用
同步互斥中,存在一个叫共享资源,指多个线程可以操作的资源。同步互斥指的是为了完成任务,多个进程或线程进行协调调度
,单必须按照必要的步骤有序执行一系列操作,当一个进程或线程使用一个共享资源的适合,其他资源不能进行操作,直到解锁才行
4、死锁是什么?如何避免死锁
两个进程或者线程在执行的时候,发生相互抢资源的现象导致,两个互相等待的现象,若无外力作用一直等待的情况。
避免方法:
1,在加多个锁的时候设置超时,若超时,解开自身的锁,其他资源运行后再执行
2,每个锁变序号,加锁的时候按照序号去加,(5个哲学家在圆桌上吃饭的问题,左右手的两个筷子问题)
3,!!!(银行家贷款分配资源问题,并行时每次总会给能满足条件的客户优先进行放款)
从当前状态出发,逐个按安全序列检查各客户谁能完成其工作,然后假定其完成工作且归还全部贷款,再进而检查下一个能完成工作的客户,…。
如果所有客户都能完成工作,则找到一个安全序列,
5、进程间通信方法有哪些?各自有什么特点?
pipe管道 queue 消息队列manager().queue() Value 共享空间 Array
开辟空间 内存 内存 内存
读写效率 一般 一般 较高
特点 父子进程间 第三方库多 需注意资源的抢夺
pipe管道有两种, 半双工管道和双工管道,且只能在有亲缘关系的进程中使用。
信号量:通过计数器的方式,控制多个线程多共享资源的访问,用于多线程间的同步,一般作为锁机制。
消息队列: 消息的链表,存放在内核中,可以承载字节流等
共享内存:映射一段能被其他进程所访问的内存,由一个进程创建,多个进程可以访问。常配合其他通信机制配合
套接字。
6、进程池原理是什么,怎么用
在需要使用大量的进程去完成任务时,此时就需要使用进程池,进程池是由一定量的进程创建的,当进程的任务完成时,不销毁,
继续等待处理其他事件,直到所有待处理事件处理完成后统一销毁,增加了进程的重复利用,降低资源消耗。
7、你是如何处理僵尸进程的
1.创建二级子进程。
2.父进程处理已结束的子进程。