程序员面试必备的网络编程知识

又到一年的年末了,各个“心怀鬼胎”的程序猿们要开始进行准备下,迎接来年跳槽大会了。而面试呢,都躲不掉问下网络编程的一些知识,在此,收集了较全的基础知识点,希望能帮助到大家。

1.基础知识

1.1.OSI七层模型

OSI七层模型是以下七层:

  • 7应用层(application) : 提供用户服务,具体功能有程序体现 http (用于超文本传输),ftp(文件传输协议)NFS,WAIS pop3(邮件接收) smtp(邮件发送)
    6表示层(Presentation) : 数据的压缩优化和加密
    5会话层 (session): 建立应用连接,选择合适的传输服务
    4传输层 (Transportation): 提供传输服务,进行流量控制
    3网络层 (Network): 路由选择(寻址),网络互连
    2链路层 (Data Link): 进行数据交换,控制具体消息收发,链路连接
    1物理层 (Physical): 提供物理硬件传输,网卡,接口设置,传输介质

七层是国际标准,常用呢把上三层合并成一个为一层,叫应用层;把物理层和链路层合并为链路层。

1.2.TCP协议

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()  #关闭连接
1.3.UPD协议

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()  #关闭连接
1.3.TCP和UPD对比总结

对比下其实tcp和udp的模型很相似。他们间的区别就以下几点:

  1. tcp套接字以字节流方式传输,数据报套接字以数据报形 式传输
  2. tcp传输会有粘包,udp不会(有消息边界)
  3. tcp传输保证传输可靠性,udp则会有部分消息丢失的可能
  4. tcp需要listen accept 保证连接,udp不需要
  5. tcp使用send,recv收发消息,udp使用sendto recvfrom
  • ※这里也顺带补充下套接字的一些属性。
    #创建套接字
    sockfd= socket(socket_family,socket_type,proto=0)
    server.bind((address,port)) #绑定地址
    sockfd.family 获取套接字地址族类型
    sockfd.type 获取套接字类型(SOCK_STREAM 流式,SOCK_DGRAM 数据报)
    sockfd.getsockname() 获取套接字绑定地址
    sockfd.getpeername() 获取连接端的地址信息
    sockfd.fileno() 获取套接字的文件描述符
    sockfd.setsockopt(level,option,value)
    功能 : 设置套接字选项,丰富或者修改套接字属性功能( level 要设置的套接字选项类别 ,option 选择每个类别中具体的选项,value 要设置的值)
1.4.HTTP协议

http协议用途 : 网页的获取,数据的传输
特点 :
1. 应用层协议,传输层采用tcp方式收发消息
2. 简单,灵活,很多语言都有http协议接口
3. 无状态的协议,协议本身不要求记录传输数据
4. http1.1 支持持久连接
网页请求过程:

  1. 客户端(浏览器)通过tcp传输,发送http请求给服务器
  2. 服务器收到http请求后进行解析
  3. 服务端处理具体请求内容,整理需要的数据
  4. 将数据以http响应的格式回发给客户端(浏览器)
  5. 浏览器接收响应,显示内容
    在这里插入图片描述
    其中有两个重点,一个是http请求格式和http相应格式(当然还有json和cookie,做爬虫的老铁们肯定不陌生)
    http请求格式:
    请求行(请求类别 请求内容 协议版本)
    请求头(对请求内容的基本描述)
    空行 !!!很关键
    请求体(请求参数和提交内容)
    http相应格式:
    响应行(协议版本 响应码 附加信息)
    响应头(对响应内容的描述信息)
    空行
    相应体 (回复给客户端的具体内容)

2.IO模型

IO ,什么是IO操作:
和终端交互 : input print
和磁盘交互 : read write
和网络交互 : recv send

2.1.阻塞IO

因为某种条件没有达成造成的函数阻塞的IO操作则是阻塞IO,

2.2.非阻塞IO

当然将原本阻塞的函数通过属性的设置改变阻塞行为,变为非阻塞IO。
常见有两种。
sockfd.setblocking(bool) #设置套接字为非阻塞IO
sockfd.settimeout(sec) #设置套接字超时时间

2.3.IO多路复用

同时监控多个IO事件,选择其中能够执行的IO进行IO 事件处理. 以此形成可以同时操作多个IO的行为模型则是IO多路复用。方法有以下三种

2.3.1.select 方法

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 监控

2.3.2.poll方法

使用方法

  1. 创建套接字 ,poll对象,建立fileno查找字典
    server =socket ()
    p = poll()
    fdmap={server.fileno:server} #server.fileno即为server的IO对象

  2. 设置套接字为关注
    p.register(server.fileno(),event) #event 为要关注的事件

  3. 循环监控IO

    while True:
      events=p.poll()
      for fd,e in events:
       pass

  4. 处理发生的IO

2.3.3.epoll方法

使用方法 : 基本同poll相同

  • 将生成对象函数改为 epoll
  • 将所有关注IO事件类型变为EPOLL类型

epoll特点 :

  • epoll是linux的专属多路复用方法
  • epoll效率比select和poll要高
  • epoll可以监控更多的IO (select 最多1024)
  • epoll支持更多的触发事件类型(EPOLLET边缘触发)

3.多任务编程

一种思想,充分利用计算机的多核资源,同时运行多个任务,以提高程序的执行效率

3.1.进程的创建

进程:程序在计算机中的一次运行过程。进程有两个模型,一个叫进程三态,一个叫五态。如图:
在这里插入图片描述
在这里插入图片描述

3.1.1.fork

使用模型:

pid = os.fork()

if pid < 0:
    print("error")
elif pid == 0:
	function1()      #子进程做的事情
 
else:
    function2()   #父进程做的事情

这里就经常有个叫僵尸进程的面试问题:
僵尸进程 : 子进程先于父进程退出,且父进程没有处理子进 程退出行为,此时子进程就会成为僵尸进程。僵尸进程虽然结束但是会存留部分进程信息在内存中,大量 的僵尸进程会消耗系统资源,因此应该避免僵尸进程产生。
解决方法:通过创建二级子进程去解决,让二级子进程变成孤儿进程
当然也可以设置信号来处理,也是变成孤儿进程: signal.signal(signal.SIGCHLD,signal.SIG_IGN)

3.1.2.Process

使用模型:
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()前设置

3.1.3.Pool

进程池:创建一定量的进程作为进程池,用来处理事件. 事件处理完毕后,进程不退出,而是继续等待处理其他事件.直到所有待处理事件处理完毕后统一销毁进程.增加了进程的重复利用,降低资源消耗.
使用模型:
pool = Pool(processes ) #processes为进程数量
pool.apply_async(func,args,kwds) #func 事件函数,args 元组 给函数按位置传参,kwds 字典 给函数按键值传参,这里是异步放入要执行的事件函数
(pool.apply(func,args,kwds)是同步放入要执行的事件函数)
pool.close()
pool.join()

3.2.进程间的通信

进程空间相对独立,资源无法直接获取.此时在不同的进程间需要进行消息传输,即进程间通信。

管道消息队列共享内存
开辟空间内存内存内存
读写方式两端读写先进先出覆盖之前内容
效率一般一般较高
使用特点多用于父子进程第三方库较多操作需要注意争夺内存资源
3.2.1.管道 (pipe)

原理:在内存中开辟管道空间,生成管道操作对象,进程间使用同一组管道对象进行读写实现通信
这里设计到单管道和双管道。

3.2.2.消息队列 (queue)

原理:在内存中建立队列模型,进程通过队列对象将消息存入到队列,或者从队列取出消息,完成进程。遵从先进先出原则。

3.2.3.共享内存

原理: 在内存中开辟一个区域,对多进程可见,进程可以 写入内容或读取内容,但是每次写入的内容都会覆盖之前的。由于覆盖,则会产生争夺内存资源问题。使用时需要配合同步互斥一起使用。

3.2.4.信号量 Semaphore 和信号 signal

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()
3.3.线程

线程定义:

  1. 线程也是多任务编程方法
  2. 线程也可以使用计算机多核资源
  3. 线程被称为轻量级的进程,也是运行状态的概念
  4. 一个进程中可以包含多个线程,线程是进程的一 部分
  5. 线程是系统分配内核的最小单位

线程特征:

  1. 线程也是运行状态,有生命周期,消耗计算机资 源
  2. 多个线程之间独立运行互不干扰
  3. 一个进程内的线程共享进程资源
  4. 线程的创建删除消耗的系统资源远远小于进程
  5. 线程也有自己独立的资源,栈空间,命令集,ID等

线程的同步互斥 !!!很重要

共享资源(临界资源): 多个线程都可以操作的资源称为 共享资源
临界区 : 指一段代码,对临界资源操作的代码段

同步: 同步是一种合作关系,为完成任务,多个进程或者 线程之间形成一种协调调度,按照必要的步骤有 序执行一系列操作

互斥: 互斥是一种制约关系,当一个进程或者线程使用 临界资源时会进行加锁处理,此时另一个进程或 者线程就无法操作,直到解锁后才能操作

3.3.1线程的创建threading

和进程的创建差不多。就不重复了。

3.3.2线程的通信Event,Lock

通信方法 :使用进程空间中的全局变量通信

注意事项 : 共享资源争夺,往往需要同步互斥机制协调
协调有两个办法,要给是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用完才能继续运行。

3.4协程

定义 : 纤程,微线程.是为非抢占式多任务产生子程序的计 算机程序组件.协程允许不同入口点,在不同位置暂停或者开始执行,简单来说,协程就是可以暂停执行的函数。
协程原理: 记录一个函数栈的上下文,进行协程的切换调度. 当一个协程函数暂停执行时会将上下文栈帧保存 起开,在切换回来时恢复到原来的执行位置,从而 继续执行。

协程优点:
1. 协程可以同时处理多个任务
2. 协程本质是单线程,资源消耗少
3. 协程无需切换的开销,无需同步互斥
协程缺点:
1. 无法利用计算机多核

4.总结(一些常见面试题)

大部分答案在前面都有介绍
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.父进程处理已结束的子进程。

  • 1
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值