1、python的底层网络交互模块有哪些?
# 答案:
'''
socket, urllib,urllib3 , requests, grab, pycurl
'''
2、简述OSI七层协议。
# 答案:
'''
应用层:HTTP,FTP,NFS
表示层:Telnet,SNMP
会话层:SMTP,DNS
传输层:TCP,UDP
网络层:IP,ICMP,ARP,
数据链路层:Ethernet,PPP,PDN,SLIP,FDDI
物理层:IEEE 802.1A,IEEE 802.11
'''
3、什么是C/S和B/S架构?
# 答案:
'''
软件系统体系结构:
C/S体系结构:
指的是客户端/服务端 例如;QQ
B(browser)/S体系结构:
指的是浏览器/服务端 例如12306(网站);购物网站
两者区别:
C/S :优点:交互性好,对服务器压力小,安全 ;缺点:服务器更新时需要同步更新客户端
B/S:优点:不需要更新客户端 缺点:交互性差,安全性低
'''
4、简述TCP三次握手、四次挥手的流程。
三次握手过程:
1首先客户端向服务端发送一个带有SYN 标志,以及随机生成的序号100(0字节)的报文
2服务端收到报文后返回一个报文(SYN200(0字节),ACk1001(字节+1))给客户端
3客户端再次发送带有ACk标志201(字节+)序号的报文给服务端
至此三次握手过程结束,客户端开始向服务端发送数据。
1客户端向服务端发起请求:我想给你通信,你准备好了么?
2服务端收到请求后回应客户端:I'ok,你准备好了么
3客户端礼貌的再次回一下客户端:准备就绪,咱们开始通信吧!
整个过程跟打电话的过程一模一样:1喂,你在吗2在,我说的你听得到不3恩,听得到(接下来请
开始你的表演)
补充:SYN:请求询问,ACk:回复,回应。
四次挥手过程:
由于TCP连接是可以双向通信的(全双工),因此每个方向都必须单独进行关闭(这句话才是
精辟,后面四个挥手过程都是其具体实现的语言描述)
四次挥手过程,客户端和服务端都可以先开始断开连接
1客户端发送带有fin标识的报文给服务端,请求通信关闭
2服务端收到信息后,回复ACK答应关闭客户端通信(连接)请求
3服务端发送带有fin标识的报文给客户端,也请求关闭通信
4客户端回应ack给服务端,答应关闭服务端的通信(连接)请求
5、什么是arp协议?
# 答案:
'''
ARP协议,全称“Address Resolution Protocol”,中文名是地址解析协议,使用ARP协议可实现通过IP地址获得对应主机的物理地址(MAC地址)。
'''
6、TCP和UDP的区别?为何基于tcp协议的通信比基于udp协议的通信更可靠?
# 答案:
# TCP和UDP的区别?
'''
1、TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接
2、TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付
3、TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的
UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)
4、每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
5、TCP首部开销20字节;UDP的首部开销小,只有8个字节
6、TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道
'''
# 为何基于tcp协议的通信比基于udp协议的通信更可靠?
'''
tcp:可靠 对方给了确认收到信息,才发下一个,如果没收到确认信息就重发
udp:不可靠 一直发数据,不需要对方回应
'''
7、什么是局域网和广域网?
# 答案:
'''
两者范围不一样:
局域网就是在固定的一个地理区域内由2台以上的电脑用网线和其他网络设备搭建而成的一个封闭的计算机组,范围在几千米以内;
广域网是一种地域跨度非常大的网络集合,范围在几十公里到几千公里。
两者的IP地址设置不一样:
局域网里面,必须在网络上有一个唯一的IP地址,这个IP地址是唯一的,在另外一个局域网,这个IP地址仍然能够使用。
广域网上的每一台电脑(或其他网络设备)都有一个或多个广域网IP地址,而且不能重复。
'''
8.什么是socket?简述基于tcp协议的套接字通信流程。
# 答案:
'''
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部。
服务端:
创建socket对象,绑定ip端口bind(), 设置最大链接数listen(), accept()与客户端的connect()创建双向管道, send(), recv(),close()
客户端:
创建socket对象,connect()与服务端accept()创建双向管道, send(),recv(),close()
'''
9.什么是粘包? socket中造成粘包的原因是什么? 哪些情况会发生粘包现象?
# 答案:
'''
粘包:
数据粘在一起,主要因为:接收方不知道消息之间的界限,不知道一次性提取多少字节的数据造成的数据量比较小,时间间隔比较短,就合并成了一个包,这是底层的一个优化算法(Nagle算法)
'''
10.IO多路复用的作用?
# 答案:
'''
I/O多路复用是用于提升效率,单个进程可以同时监听多个网络连接IO。
举例:
通过一种机制,可以监视多个文件描述符,一旦描述符就绪(读就绪和写就绪),能通知程序进行相应的读写操作,I/O多路复用避免阻塞在io上,原本为多进程或多线程来接收多个连接的消息变为单进程或单线程保存多个socket的状态后轮询处理。
'''
11.什么是防火墙以及作用?
# 答案:
'''
在互联网上防火墙是一种非常有效的网络安全模型,通过它可以隔离风险区域(即Internet或有一定风险的网络)与安全区域(局域网)的连接,同时不会妨碍人们对风险区域的访问。所以它一般连接在核心交换机与外网之间。
1.过滤进出网络的数据
2.2.管理进出访问网络的行为
3.3.封堵某些禁止业务
4.4.记录通过防火墙信息内容和活动
5.5.对网络攻击检测和告警
'''
12.select、poll、epoll模型的区别?
# 答案:
'''
I/O多路复用的本质就是用select/poll/epoll,去监听多个socket对象,如果其中的socket对象有变化,只要有变化,用户进程就知道了。
select是不断轮询去监听的socket,socket个数有限制,一般为1024个;
poll还是采用轮询方式监听,只不过没有个数限制;
epoll并不是采用轮询方式去监听了,而是当socket有变化时通过回调的方式主动告知用户进程。
'''
13.简述进程、线程、协程的区别以及应用场景?
# 答案:
'''
1.进程是操作系统资源分配的最小单位,拥有独立的资源和地址空间
2.线程是CPU调度的单位
3.统一进程中的线程是资源共享的。
4.协程是用户级别的,程序之间的切换由用户自行处理,节省了CPU的调度时间。
'''
14.什么是GIL锁?
# 答案:
'''
全局解释锁,每次只能一个线程获得cpu的使用权:为了线程安全,也就是为了解决多线程之间的数据完整性和状态同步而加的锁,因为我们知道线程之间的数据是共享的。
'''
15.Python中如何使用线程池和进程池?
# 答案:
# 线程池
import threadpool, time
with open(r'../uoko_house_id.txt', 'r', encoding='utf-8') as f: # with open语句表示通用的打开文件的方式,此处用来获取需要爬取参数的列表
roomIdLi = f.readlines()
roomIdList =[x.replace('\n','').replace(' ','') for x in roomIdLi]
print(roomIdList)
li = [[i, item] for i, item in enumerate(roomIdList)] # enumerate()将列表中元素和其下标重新组合输出
def run(roomId):
"""对传入参数进行处理"""
print('传入参数为:', roomId)
time.sleep(1)
def main():
roomList = li # 房间信息
start_time = time.time()
print('启动时间为:', start_time)
pool = threadpool.ThreadPool(10)
requests = threadpool.makeRequests(run, roomList)
[pool.putRequest(req) for req in requests]
pool.wait()
print("共用时:", time.time()-start_time)
if __name__ == '__main__':
main()
# 进程池
from multiprocessing.pool import Pool
from time import sleep
def fun(a):
sleep(5)
print(a)
if __name__ == '__main__':
p = Pool()
for i in range(10):
p.apply_async(fun, args= (i, ))
p.close()
p.join()
print("end")
16.threading.local的作用?
# 答案:
为每个线程创建一个独立的空间,使得线程对自己的空间中的数据进行操作(数据隔离)。
import threading
from threading import local
import time
obj = local()
def task(i):
obj.xxxxx = i
time.sleep(2)
print(obj.xxxxx,i)
for i in range(10): #开启了10个线程
t = threading.Thread(target=task,args=(i,))
t.start()
17.进程之间如何进行通信?
# 答案:
python提供了多种进程通信的方式,主要Queue和Pipe这两种方式,Queue用于多个进程间实现通信,Pipe是两个进程的通信。
# Queue
from multiprocessing import Process, Queue
import os,time,random
#写数据进程执行的代码
def proc_write(q,urls):
print 'Process is write....'
for url in urls:
q.put(url)
print 'put %s to queue... ' %url
time.sleep(random.random())
#读数据进程的代码
def proc_read(q):
print('Process is reading...')
while True:
url = q.get(True)
print('Get %s from queue' %url)
if __name__ == '__main__':
#父进程创建Queue,并传给各个子进程
q = Queue()
proc_write1 = Process(target=proc_write,args=(q,['url_1','url_2','url_3']))
proc_write2 = Process(target=proc_write,args=(q,['url_4','url_5','url_6']))
proc_reader = Process(target=proc_read,args=(q,))
#启动子进程,写入
proc_write1.start()
proc_write2.start()
proc_reader.start()
#等待proc_write1结束
proc_write1.join()
proc_write2.join()
#proc_raader进程是死循环,强制结束
proc_reader.terminate()
# PIPE
import multiprocessing
import os,time,random
#写数据进程执行的代码
def proc_send(pipe,urls):
#print 'Process is write....'
for url in urls:
print 'Process is send :%s' %url
pipe.send(url)
time.sleep(random.random())
#读数据进程的代码
def proc_recv(pipe):
while True:
print('Process rev:%s' %pipe.recv())
time.sleep(random.random())
if __name__ == '__main__':
#父进程创建pipe,并传给各个子进程
pipe = multiprocessing.Pipe()
p1 = multiprocessing.Process(target=proc_send,args=(pipe[0],['url_'+str(i) for i in range(10) ]))
p2 = multiprocessing.Process(target=proc_recv,args=(pipe[1],))
#启动子进程,写入
p1.start()
p2.start()
p1.join()
p2.terminate()
18.什么是并发和并行?
# 答案:
# 并发:同一时刻只能处理一个任务,但可以交替处理多个任务。(一个处理器同时处理多个任务)
# 并行:同一时刻可以处理多个任务。(多个处理器或者是多核的处理器同时处理多个不同的任务)
# 类比:并发是一个人同时吃三个馒头,而并行是三个人同时吃三个馒头。
19.同步和异步,阻塞和非阻塞的区别?
# 答案:
'''
同步:执行一个操作之后,需要主动等待返回结果;
异步:执行一个操作之后,不需要主动等待返回结果,若接收到结果通知,再回来执行刚才没执行完的操作。
同步和异步关心的问题是:要不要主动等待结果。
阻塞:在执行一个操作时,不能做其他操作;
非阻塞:在执行一个操作时,能做其他操作。
阻塞和非阻塞关心的问题是:能不能做其他操作。
'''
20.路由器和交换机的区别?
# 答案:
'''
1:交换机:是负责内网里面的数据传递(arp协议)根据MAC地址寻址。
路由器:在网络层,路由器根据路由表,寻找该ip的网段。
2:路由器可以把一个IP分配给很多个主机使用,这些主机对外只表现出一个IP。
交换机可以把很多主机连起来,这些主机对外各有各的IP。
3:交换机是做端口扩展的,也就是让局域网可以连进来更多的电脑。
路由器是用来做网络连接,也就是连接不同的网络。
'''
21.什么是域名解析?
# 答案:
'''
在互联网上,所有的地址都是ip地址,现阶段主要是IPv4(比如:110.110.110.110)。
但是这些ip地址太难记了,所以就出现了域名(比如http://baidu.com)。
域名解析就是将域名,转换为ip地址的这样一种行为。
'''
22.如何修改本地hosts文件?
# 答案:
'''
Hosts是一个没有扩展名的系统文件,可以用记事本等工具打开,其作用就是将一些常用的网址域名与其对应的IP地址建立一个关联“数据库”,
当用户在浏览器中输入一个需要登录的网址时,系统会首先自动从Hosts文件中寻找对应的IP地址,
一旦找到,系统会立即打开对应网页,如果没有找到,则系统会再将网址提交给DNS域名解析服务器进行IP地址的解析。
文件路径:C:\WINDOWS\system32\drivers\etc。
将127.0.0.1 www.163.com 添加在最下面
修改后用浏览器访问“www.163.com”会被解析到127.0.0.1,导致无法显示该网页。
'''
23.生产者消费者模型应用场景?
# 答案:
'''
生产者与消费者模式是通过一个容器来解决生产者与消费者的强耦合关系,生产者与消费者之间不直接进行通讯,
而是利用阻塞队列来进行通讯,生产者生成数据后直接丢给阻塞队列,消费者需要数据则从阻塞队列获取,
实际应用中,生产者与消费者模式则主要解决生产者与消费者的生产与消费的速率不一致的问题,达到平衡生产者与消费者的处理能力,而阻塞队列则相当于缓冲区。
应用场景:用户提交订单,订单进入引擎的阻塞队列中,由专门的线程从阻塞队列中获取数据并处理。
优势:
1;解耦
假设生产者和消费者分别是两个类。如果让生产者直接调用消费者的某个方法,那么生产者对于消费者就会产生依赖(也就是耦合)。
将来如果消费者的代码发生变化,可能会影响到生产者。而如果两者都依赖于某个缓冲区,两者之间不直接依赖,耦合也就相应降低了。
2:支持并发
生产者直接调用消费者的某个方法,还有另一个弊端。由于函数调用是同步的(或者叫阻塞的),在消费者的方法没有返回之前,生产者只能一直等着
而使用这个模型,生产者把制造出来的数据只需要放在缓冲区即可,不需要等待消费者来取。
3:支持忙闲不均
缓冲区还有另一个好处。如果制造数据的速度时快时慢,缓冲区的好处就体现出来了。
当数据制造快的时候,消费者来不及处理,未处理的数据可以暂时存在缓冲区中。等生产者的制造速度慢下来,消费者再慢慢处理掉。
'''
24.什么是cdn?
# 答案:
'''
目的是使用户可以就近到服务器取得所需内容,解决 Internet网络拥挤的状况,提高用户访问网站的响应速度。
cdn 即内容分发网络
'''
25.程序从FlagA执行到FlagB的时间大致为多少秒
import threading
import time
def _wait():
time.sleep(60)
# FlagA
t = threading.Thead(target=_wait, daemon=False)
t.start()
# FlagB
# 答案:
60s 因为没有设置守护线程,需要等子线程跑完主线程才结束
26.有A.txt和B.txt两个文件, 使用多进程和进程池的方式分别读取这两个文件
# 答案:
# 多进程
"""通过多进程加速读取excel的测试"""
__author__ = "hanyaning@deri.energy"
import os.path
import time
from service import logger
import pandas as pd
from multiprocessing import Process, Manager
startTime = time.time()
logger = logger.MyLogger("multi_process").getLogger()
def getExcelData(path, return_data=None, file_name=""):
global startTime
logger.info("开始读取Excel文件,当前进程pid:" + str(os.getpid()))
if not os.path.exists(path):
raise FileNotFoundError()
if os.path.isfile(path):
return_data[file_name] = pd.read_excel(path, skiprows=1, skipfooter=1)
logger.info("读取Excel文件完毕,当前进程pid:" + str(os.getpid()))
if __name__ == "__main__":
excel_path = os.path.join(os.getcwd(), "../excels")
xls_names = [x for x in os.listdir(excel_path) if x.endswith(".xls")]
first = str(time.time() - startTime)
logger.info("进入程序用时:" + first)
p_list = []
# Manager类似于同步数据管理工具,可在多进程时实现各进程操作同一个数据,比如这里通过它组织返回值
manager = Manager()
# Manager.dict()类似于共享变量,各个进程可以修改它,通过每次添加不同的key值,可以实现方法返回值的获取
return_data = manager.dict()
first = time.time() - startTime
# 手动创建多个进程读取,可能存在创建进程过多导致系统崩溃的情况
for file_name in xls_names:
p = Process(target=getExcelData, args=(os.path.join(excel_path, file_name), return_data, file_name))
p.start()
p_list.append(p)
print(p_list)
"""
经测试,直到这里都还会延迟数秒才执行进程的target方法,尽管前面已经调用了start(),但进程并没有立即执行
寡人认为是系统创建进程需要时间,并且是创建好所有进程后才各进程才开始工作,这里要创建120个进程花费了大多数的时间
后面在采用进程池时,当设置最大进程数为120时,依然花费了大把的时间,而设置为10时,大大缩小了创建进程到执行target方法所要等待的时间
这也证明了寡人的观点,至于正确与否,寡人先跟代码去了,且等下回分解
"""
for p in p_list:
# 如果有子进程没有执行完,需要先阻塞主进程
p.join()
logger.info("各进程执行完毕")
# 获取返回值字典为列表
data_frames = return_data.values()
# 合并列表为一个dataFrame
data = pd.DataFrame()
for da in data_frames:
data = data.append(da)
endTime = time.time()
print(endTime - startTime)
print(len(data))
# 进程池
"""通过多进程加速读取excel的测试"""
__author__ = "hanyaning@deri.energy"
import os.path
import time
from service import logger
import pandas as pd
from multiprocessing import Pool
logger = logger.MyLogger("multi_process").getLogger()
def getExcelData(path):
logger.info("开始读取excel,当前进程pid:" + str(os.getpid()))
data = pd.DataFrame()
if not os.path.exists(path):
raise FileNotFoundError()
if os.path.isfile(path):
logger.info("读取Excel文件完毕,当前进程pid:" + str(os.getpid()))
return data.append(pd.read_excel(path, skiprows=1, skipfooter=1), sort=False)
if __name__ == "__main__":
excel_path = os.path.join(os.getcwd(), "../excels")
xls_names = [x for x in os.listdir(excel_path) if x.endswith(".xls")]
startTime = time.time()
p_list = []
# 使用进程池Pool
pool = Pool(processes=10)
pool_data_list = []
data = pd.DataFrame()
for file_name in xls_names:
# 需要注意不能直接在这里调用get方法获取数据,原因是apply_async后面 get()等待线程运行结束才会下一个,这里多进程会变成阻塞执行
pool_data_list.append(pool.apply_async(getExcelData, (os.path.join(excel_path, file_name),)))
pool.close()
# 需要阻塞以下,等所有子进程执行完毕后主线程才继续执行
pool.join()
for pool_data in pool_data_list:
# 这里再使用get()方法可以获取返回值
data = data.append(pool_data.get())
endTime = time.time()
print(endTime - startTime)
print(len(data))
27.以下那些是常见的TCPFlags?(多选)
'''
A.SYN
B.RST
C.ACK
D.URG
'''
# 答案:
'''
A,B,C,D
'''