03 网络和并发编程

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

28.下面关于网络七层和四层的􏰀述, 哪条是错误的?

'''
A.SNMP工作在四层
B.四层是指网络的传输层, 主要包括IP和端口信息
C.七层是指网络的应用层(协议层), 比如http协议就工作在七层
D.四层主要应用于TCP和UDP的代理, 七层主要应用于HTTP等协议的代理
'''

# 答案:
A

29.tracerroute一般使用的是哪种网络层协议

'''
A.VRRP
B.UDP
C.ARP
D.ICMP
'''

# 答案:
D

30.iptables知识考察, 根据要求写出防火墙规则?

'''
A.屏蔽192.168.1.5访问本机dns服务端口
B.允许10.1..0/2访问本机的udp88889999端口
'''
# 答案:
'''
iptables -A INPUT -p ICMP --icmp-type 8 -m time --timestart 00:00:00 --timestop 23:59:59 --weekdays Mon -j DROP
'''

31.业务服务器192.168.1.2访问192.168.1.3数据接口, 无法正常返回数据, 请根据以上信息写出排查思路。

pass

32.请实现一个简单的socket编程。

'''
要求:
1.实现server端的功能即可
2.遵循基本语言编程规范
'''
# 答案:
# 服务端:
# coding=utf-8

import socket

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('', 8888))
server.listen(5)

#等待客户端连接
while True:
    (client, address) = server.accept()
    data = client.recv(4096)
    print data
    client.send("hello")                                                    
    client.close()


# 客户端
import socket                
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(('127.0.0.1', 8888))
client.send("My name is client")
message = client.recv(4096)                                                      
print message                
client.close()

33.谈一下对于多线程编程的理解, 对于CPU密集型怎样使用多线程, 说说线程池, 线程锁的用法, 有没有用过multiprocessing或concurrent.future?

# 答案:
'''
CPU密集型使用多进程
IO密集型使用多线程
加锁
锁定
释放
'''

34.关于守护线程的说法, 正确的是

'''
A.所有非守护线程终止, 即使存在守护线程, 进程运行终止
B.所有守护线程终止, 即使存在非守护线程, 进程运行终止
C.只要有守护线程或者非守护线程其中之一存在, 进程就不会终止
D.只要所有的守护线程和非守护线程中终止运行之后, 进程才会终止
'''
# 答案:
A

35.TCP协议在每次建立或者拆除连接时, 都要在收发双方之间交换()报文

'''
A.一个
B.两个
C.三个
D.四个
'''
# 答案:
C

36. 􏰀述多进程开发中join与deamon的区别

# 答案:
'''
p.join([timeout]):主线程等待p终止(强调:是主线程处于等的状态,而p是处于运行的状态)。timeout是可选的超时时间,需要强调的是,p.join只能join住start开启的进程,而不能join住run开启的进程 

p.daemon:默认值为False,如果设为True,代表p为后台运行的守护进程,当p的父进程终止时,p也随之终止,并且设定为True后,p不能创建自己的新进程,必须在p.start()之前设置
'''

37.请简述GIL对Python性能的影响

# 答案:
'''
GIL:全局解释器锁。每个线程在执行的过程都需要先获取GIL,保证同一时刻只有一个线程可以执行字节码。
线程释放GIL锁的情况:
在IO操作等可能会引起阻塞的system call之前,可以暂时释放GIL,但在执行完毕后,必须重新获取GIL
Python 3.x使用计时器(执行时间达到阈值后,当前线程释放GIL)或Python 2.x,tickets计数达到100
Python使用多进程是可以利用多核的CPU资源的。
多线程爬取比单线程性能有提升,因为遇到IO阻塞会自动释放GIL锁
'''

38.曾经在哪里使用过: 线程、进程、协程?

# 答案:
'''
1.在写高并发的服务端代码时。
2.在写高性能爬虫的时候。
'''

39.请使用yield实现一个协程?

# 答案:
import time

import queue
def consumer(name):
    print("--->starting eating baozi...")
    while True:
        new_baozi = yield
        print("[%s] is eating baozi %s" % (name,new_baozi))
        #time.sleep(1)
 
def producer():
 
    r = con.__next__()
    r = con2.__next__()
    n = 0
    while n < 5:
        n +=1
        con.send(n)
        con2.send(n)
        print("\033[32;1m[producer]\033[0m is making baozi %s" %n )
 
 
if __name__ == '__main__':
    con = consumer("c1")
    con2 = consumer("c2")
    p = producer()

40.请使用python内置async语法实现一个协程?

# 答案:
from datetime import datetime
import asyncio

async def add(n):
    print(datetime.now().strftime('%H:%M:%S.%f'))
    count = 0
    for i in range(n):
        count += i
    print(datetime.now().strftime('%H:%M:%S.%f'))
    return count

async def fun(n):
    res = await add(n)
    print(f'res = {res}')

loop = asyncio.get_event_loop()
tasks = [fun(20000000), fun(30000000)]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()

41.简述线程死锁是如何造成的?如何避免?

# 答案:
1.加锁顺序(线程按照一定的顺序加锁)
2.加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)
3.死锁检测

42.asynio是什么?

# 答案:
python高并发模块。

43.gevent模块是什么?

# 答案:
'''
gevent是第三方库,通过greenlet实现协程,其基本思想是:

当一个greenlet遇到IO操作时,比如访问网络,就自动切换到其他的greenlet,等到IO操作完成,再在适当的时候切换回来继续执行。由于IO操作非常耗时,经常使程序处于等待状态,有了gevent为我们自动切换协程,就保证总有greenlet在运行,而不是等待IO。
'''

44.什么是twisted框架?

# 答案:
'''
twisted是异步非阻塞框架。爬虫框架Scrapy依赖twisted。
'''

45.什么是LVS?

# 答案:
'''
LVS :Linux虚拟服务器
作用:LVS主要用于多服务器的负载均衡。
它工作在网络层,可以实现高性能,高可用的服务器集群技术。
它廉价,可把许多低性能的服务器组合在一起形成一个超级服务器。
它易用,配置非常简单,且有多种负载均衡的方法。
它稳定可靠,即使在集群的服务器中某台服务器无法正常工作,也不影响整体效果。另外可扩展性也非常好。
'''

46.什么是Nginx?

# 答案:
'''
Nginx是一款自由的、开源的、高性能的HTTP服务器和反向代理服务器,同时也是一个IMAP、POP3、SMTP代理服务器。可以用作HTTP服务器、方向代理服务器、负载均衡。
'''

47.什么是keepalived?

# 答案:
'''
        Keepalived起初是为LVS设计的,专门用来监控集群系统中各个服务节点的状态,它根据TCP/IP参考模型的第三、第四层、第五层交换机制检测每个服务节点的状态,如果某个服务器节点出现异常,或者工作出现故障,Keepalived将检测到,并将出现的故障的服务器节点从集群系统中剔除,这些工作全部是自动完成的,不需要人工干涉,需要人工完成的只是修复出现故障的服务节点。

        后来Keepalived又加入了VRRP的功能,VRRP(Vritrual Router Redundancy Protocol,虚拟路由冗余协议)出现的目的是解决静态路由出现的单点故障问题,通过VRRP可以实现网络不间断稳定运行,因此Keepalvied 一方面具有服务器状态检测和故障隔离功能,另外一方面也有HA cluster功能,下面介绍一下VRRP协议实现的过程。
'''

48.什么是haproxy?

# 答案:
'''
- TCP 代理:可从监听 socket 接受 TCP 连接,然后自己连接到 server,HAProxy 将这些 sockets attach 到一起,使通信流量可双向流动。

- HTTP 反向代理(在 HTTP 专用术语中,称为 gateway):HAProxy 自身表现得就像一个 server,通过监听 socket 接受 HTTP 请求,然后与后端服务器建立连接,通过连接将请求转发给后端服务器。

- SSL terminator / initiator / offloader: 客户端 -> HAProxy 的连接,以及 HAProxy -> server 端的连接都可以使用 SSL/TLS

- TCP normalizer: 因为连接在本地操作系统处终结,client 和 server 端没有关联,所以不正常的 traffic 如 invalid packets, flag combinations, window advertisements, sequence numbers, incomplete connections(SYN floods) 不会传递给 server 端。这种机制可以保护脆弱的 TCP stacks 免遭协议上的攻击,也使得我们不必修改 server 端的 TCP 协议栈设置就可以优化与 client 的连接参数。


- HTTP normalizer: HAProxy 配置为 HTTP 模式时,只允许有效的完整的请求转发给后端。这样可以使得后端免遭 protocol-based 攻击。一些不规范的定义也被修改,以免在 server 端造成问题(eg: multiple-line headers,会被合并为一行)

- HTTP 修正工具:HAProxy 可以 modify / fix / add / remove / rewrite URL 及任何 request or response header。

- a content-based switch: 可基于内容进行转发。可基于请求中的任何元素转发请求或连接。因此可基于一个端口处理多种协议(http,https, ssh)

- a server load balancer: 可对 TCP 连接 和 HTTP 请求进行负载均衡调度。工作于 TCP 模式时,可对整个连接进行负载均衡调度;工作于 HTTP 模式时,可对 HTTP 请求进行调度。

- a traffic regulator: 可在不同的方面对流量进行限制,保护 server ,使其不超负荷,基于内容调整 traffic 优先级,甚至可以通过 marking packets 将这些信息传递给下层以及网络组件。

- 防御 DDos 攻击及 service abuse: HAProxy 可为每个 IP地址,URL,cookie 等维护大量的统计信息,并对其进行检测,当发生服务滥用的情况,采取一定的措施如:slow down the offenders, block them, send them to outdated contents, etc

- 是 network 的诊断的一个观察节点:根据精确记录细节丰富的日志,对网络诊断很有帮助

- an HTTP compression offloader:可自行对响应进行压缩,而不是让 server 进行压缩,因此对于连接性能较差的 client,或使用高延迟移动网络的 client,可减少页面加载时间。
'''

49.什么是负载均衡?

# 答案:
'''
系统的扩展可分为纵向(垂直)扩展和横向(水平)扩展。纵向扩展,是从单机的角度通过增加硬件处理能力,比如CPU处理能力,内存容量,磁盘等方面,实现服务器处理能力的提升,不能满足大型分布式系统(网站),大流量,高并发,海量数据的问题。因此需要采用横向扩展的方式,通过添加机器来满足大型网站服务的处理能力。比如:一台机器不能满足,则增加两台或者多台机器,共同承担访问压力。这就是典型的集群和负载均衡架构


'''

50.什么是rpc及应用场景?

# 答案:
'''
RPC主要用于公司内部的服务调用,性能消耗低,传输效率高,服务治理方便。HTTP主要用于对外的异构环境,浏览器接口调用,APP接口调用,第三方接口调用等...
'''

51.什么是反向代理和反向代理?

# 答案:
'''
反向代理,这个词相信搞网络的朋友都很熟悉的,但是具体是什么意思呢?说实话,复杂的我也不懂,就我个人理解而言,反向代理有很多用途,比如说保护真实服务器不被外界攻击,加速网络等等。今天我们要介绍的就是加速网络的一种。
'''

52.创建进程:

'''
1.首先要导入multiprocessing中的Process;
2.创建一个Process对象;
3.创建Process对象时,可以传递参数;

1.p = Process(target=XXX, args=(元组,) , kwargs={key:value}) 
2.target = XXX 指定的任务函数,不用加() 
3.args=(元组,) , kwargs={key:value} 给任务函数传递的参数 
4.使用start()启动进程;
5.结束进程。
'''

53.给子进程指定函数传递参数Demo:

import os
from multiprocessing import Process
import time
def pro_func(name, age, **kwargs):
    for i in range(5):
    print("子进程正在运行中,name=%s, age=%d, pid=%d" %(name, age, os.getpid()))
    print(kwargs)
    time.sleep(0.2)
if __name__ == '__main__':
    # 创建Process对象
    p = Process(target=pro_func, args=('小明',18), kwargs={'m': 20})
    # 启动进程
    p.start()

time.sleep(1) # 1秒钟之后,立刻结束子进程 17.
p.terminate()
p.join() 注意:进程间不共享全局变量。

54.进程之间的通信-Queue

在初始化Queue()对象时,(例如q=Queue(),若在括号中没有指定最大可接受的消息数量,或数
量为负值时,那么就代表可接受的消息数量没有上限-直到内存的尽头)

55.进程间通信Demo:

from multiprocessing import Process, Queueimport os, time, random # 写数据进程执行的代码:def write(q):
for value in ['A', 'B', 'C']:
    print('Put %s to queue...' % value)
    q.put(value)
    time.sleep(random.random())

56.读数据进程执行的代码:def read(q):

while True:
if not q.empty():
    value = q.get(True)
    print('Get %s from queue.' % value)
    time.sleep(random.random())
else:
    break
if __name__=='__main__':
# 父进程创建Queue,并传给各个子进程:
    q = Queue()
    pw = Process(target=write, args=(q,))
    pr = Process(target=read, args=(q,))
    # 启动子进程pw,写入:
    pw.start()

    # 等待pw结束:
    pw.join()

    # 启动子进程pr,读取:
    pr.start()
    pr.join()
    # pr进程里是死循环,无法等待其结束,只能强行终止:
    print('')
    print('所有数据都写入并且读完') 进程池Pool
from multiprocessing import Poolimport os, time, random
def worker(msg):
    t_start = time.time()
    print("%s开始执行,进程号为%d" % (msg,os.getpid()))
    # random.random()随机生成0~1之间的浮点数 7.    time.sleep(random.random()*2)
    t_stop = time.time()
    print(msg,"执行完毕,耗时%0.2f" % (t_stop-t_start))
    po = Pool(3)  # 定义一个进程池,最大进程数3
    for i in range(0,10): 13# Pool().apply_async(要调用的目标,(传递给目标的参数元祖,))
    # 每次循环将会用空闲出来的子进程去调用目标    po.apply_async(worker,(i,))
    print("----start----")
    po.close()  # 关闭进程池,关闭后po不再接收新的请求 po.join()  # 等待po中所有子进程执行完成,必须放在close 语句之后 print("-----end-----")

57.multiprocessing.Pool 常用函数解析:

apply_async(func[, args[, kwds]]):使用非阻塞方式调用func(并行执行,堵塞方式必须等待上一个进程退出才能执行下一个进程),args为传递给func的参数列表,kwds为传递给func的关键字参数列表;
close():关闭Pool,使其不再接受新的任务;
terminate():不管任务是否完成,立即终止;
join():主进程阻塞,等待子进程的退出, 必须在close或terminate之后使用;
进程池中使用Queue
如果要使用Pool创建进程,就需要使用multiprocessing.Manager()中的 Queue(),而不是
multiprocessing.Queue(),否则会得到一条如下的错误信息:

RuntimeError: Queue objects should only be shared between processes through
inheritance.
from multiprocessing import Manager,Poolimport os,time,random
def reader(q):
    print("reader启动(%s),父进程为(%s)" % (os.getpid(), os.getppid()))
    for i in range(q.qsize()):
    print("reader从Queue获取到消息:%s" % q.get(True))
def writer(q):
    print("writer启动(%s),父进程为(%s)" % (os.getpid(), os.getppid()))
    for i in "itcast":
    q.put(i)
if __name__=="__main__":
    print("(%s) start" % os.getpid())
    q = Manager().Queue()  # 使用Manager中的Queue
    po = Pool()
    po.apply_async(writer, (q,))
    time.sleep(1)

    # 先让上面的任务向Queue存入数据,然后再让下面的任务开始从中取数据 po.apply_async(reader, (q,))
    po.close()
    po.join()
    print("(%s) End" % os.getpid())

58.谈谈你对多进程,多线程,以及协程的理解,项目是否用?

这个问题被问的概率相当之大,其实多线程,多进程,在实际开发中用到的很少,除非是那些对项目性能要求特别高的,有的开发工作几年了,也确实没用过,你可以这么回答,给他扯扯什么是进程,线程(cpython中是伪多线程)的概念就行,实在不行你就说你之前写过下载文件时,用过多线程技术,或者业余时间用过多线程写爬虫,提升效率。

'''
进程:一个运行的程序(代码)就是一个进程,没有运行的代码叫程序,进程是系统资源分配的最
小单位,进程拥有自己独立的内存空间,所以进程间数据不共享,开销大。

线程:  调度执行的最小单位,也叫执行路径,不能独立存在,依赖进程存在一个进程至少有一个线程,叫主线程,而多个线程共享内存(数据共享,共享全局变量),从而极大地提高了程序的运行效率。

协程:是一种用户态的轻量级线程,协程的调度完全由用户控制。协程拥有自己的寄存器上下文和栈。

协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存
器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切
换非常快。
'''

59.解释一下什么是锁,有哪几种锁?

锁(Lock)是 Python 提供的对线程控制的对象。有互斥锁、递归锁。

60.什么是死锁呢?

若干子线程在系统资源竞争时,都在等待对方对某部分资源解除占用状态,结果是谁也不愿先解锁,互相干等着,程序无法执行下去,这就是死锁。
GIL锁(有时候,面试官不问,你自己要主动说,增加b格,尽量别一问一答的尬聊,不然最后等到的一句话就是:你还有什么想问的么?)
GIL锁 全局解释器锁(只在cpython里才有)
作用:限制多线程同时执行,保证同一时间只有一个线程执行,所以cpython里的多线程其实是伪多线程!
所以Python里常常使用协程技术来代替多线程,协程是一种更轻量级的线程,
进程和线程的切换时由系统决定,而协程由我们程序员自己决定,而模块gevent下切换是遇到了耗时操作才会切换。
三者的关系:进程里有线程,线程里有协程。

61.什么是线程安全,什么是互斥锁?

每个对象都对应于一个可称为" 互斥锁"的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。
同一个进程中的多线程之间是共享系统资源的,多个线程同时对一个对象进行操作,一个线程操作尚未结束,
另一个线程已经对其进行操作,导致最终结果出现错误,此时需要对被操作对象添加互斥锁,保证每个线程对该对象的操作都得到正确的结果。

62.说说下面几个概念:同步,异步,阻塞,非阻塞?

同步:多个任务之间有先后顺序执行,一个执行完下个才能执行。
异步:多个任务之间没有先后顺序,可以同时执行有时候一个任务可能要在必要的时候获取另一个同时执行的任务的结果,这个就叫回调!
阻塞:如果卡住了调用者,调用者不能继续往下执行,就是说调用者阻塞了。
非阻塞:如果不会卡住,可以继续执行,就是说非阻塞的。
同步异步相对于多任务而言,阻塞非阻塞相对于代码执行而言。

63.什么是僵尸进程和孤儿进程?怎么避免僵尸进程?

孤儿进程:父进程退出,子进程还在运行的这些子进程都是孤儿进程,孤儿进程将被 init 进程(进程号为 1)所收养,并由init进程对它们完成状态收集工作。

僵尸进程:进程使用fork创建子进程,如果子进程退出,而父进程并没有调用 waitwaitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中的这些进程是僵尸进程。

避免僵尸进程的方法:
-fork两次用孙子进程去完成子进程的任务;
-用wait()函数使父进程阻塞;
-使用信号量,在 signal handler 中调用waitpid,这样父进程不用阻塞。

64.Python中的进程与线程的使用场景?

多进程适合在 CPU 密集型操作(cpu 操作指令比较多,如位数多的浮点运算)。
多线程适合在 IO 密集型操作(读写数据操作较多的,比如爬虫)。

65.线程是并发还是并行,进程是并发还是并行?

线程是并发,进程是并行;
进程之间相互独立,是系统分配资源的最小单位,同一个线程中的所有线程共享资源。

66.并行(parallel)和并发(concurrency)?

并行:同一时刻多个任务同时在运行。
并发:在同一时间间隔内多个任务都在运行,但是并不会在同一时刻同时运行,存在交替执行的情况。
实现并行的库有:multiprocessing
实现并发的库有:threading
程序需要执行较多的读写、请求和回复任务的需要大量的 IO 操作,IO密集型操作使用并发更好。

CPU运算量大的程序程序,使用并行会更好。
IO密集型和CPU密集型区别?
IO密集型:系统运作,大部分的状况是CPU在等 I/O (硬盘/内存)的读/写。
CPU密集型:大部份时间用来做计算、逻辑判断等 CPU动作的程序称之CPU密集型。

67.使用udp发送/接收数据步骤:

1.创建客户端套接字
2.发送/接收数据
3.关闭套接字

import socket
def main():
    # 1、创建udp套接字
    # socket.AF_INET  表示IPv4协议  AF_INET6 表示IPv6协议
    # socket.SOCK_DGRAM  数据报套接字,只要用于udp协议
    udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    # 2、准备接收方的地址
    # 元组类型  ip是字符串类型   端口号是整型
    dest_addr = ('192.168.113.111', 8888)
    # 要发送的数据
    send_data = "我是要发送的数据"
    # 3、发送数据
    udp_socket.sendto(send_data.encode("utf-8"), dest_addr)
    # 4、等待接收方发送的数据  如果没有收到数据则会阻塞等待,直到收到数据
    # 接收到的数据是一个元组   (接收到的数据, 发送方的ip和端口)
    # 1024  表示本次接收的最大字节数
    recv_data, addr = udp_socket.recvfrom(1024)
    # 5、关闭套接字
    udp_socket.close()
if __name__ == '__main__': 22.    main() 编码的转换
    str -->bytes: encode编码
    bytes--> str: decode()解码

68.UDP绑定端口号:

1.创建socket套接字
2.绑定端口号
3.接收/发送数据
4.关闭套接字

import socket
def main():
    # 1、创建udp套接字
    # socket.AF_INET  表示IPv4协议  AF_INET6 表示IPv6协议
    # socket.SOCK_DGRAM  数据报套接字,只要用于udp协议
    udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    # 2、绑定端口
    # 元组类型  ip一般不写 表示本机的任何的一个ip
    local_addr = ('', 7777)
    udp_socket.bind(local_addr)
    # 3、准备接收方的地址
    # 元组类型  ip是字符串类型   端口号是整型
    dest_addr = ('192.168.113.111', 8888)
    # 要发送的数据
    send_data = "我是要发送的数据"
    # 4、发送数据
    udp_socket.sendto(send_data.encode("utf-8"), dest_addr)
    # 5、等待接收方发送的数据  如果没有收到数据则会阻塞等待,直到收到数据
    # 接收到的数据是一个元组   (接收到的数据, 发送方的ip和端口)
    # 1024  表示本次接收的最大字节数
    recv_data, addr = udp_socket.recvfrom(1024)
    # 6、关闭套接字
    udp_socket.close()
if __name__ == '__main__':
    main() 注意点:绑定端口要在发送数据之前进行绑定。

69.TCP客户端的创建流程:

1.创建TCP的socket套接字
2.连接服务器
3.发送数据给服务器端
4.接收服务器端发送来的消息
5.关闭套接字

import socket
def main():
    # 1、创建客户端的socket
    # socket.AF_INET  表示IPv4协议  AF_INET6 表示IPv6协议
    # socket.SOCK_STREAM  流式套接字,只要用于TCP 协议
    client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 2、构建目标地址
    server_ip = input("请输入服务器端的IP地址:")
    server_port = int(input("请输入服务器端的端口号:"))
    # 3、连接服务器
    # 参数:元组类型   ip 是字符串类型   端口号是整型
    client_socket.connect((server_ip, server_port))
    # 要发送给服务器端的数据
    send_data = "我是要发送给服务器端的数据"
    # 4、发送数据
    client_socket.send(send_data.encode("gbk"))
    # 5、接收服务器端恢复的消息, 没有消息会阻塞
    # 1024表示接收的最大字节数
    recv_date= client_socket.recv(1024)
    print("接收到的数据是:", recv_date.decode('gbk'))
    # 6、关闭套接字
    client_socket.close()
if __name__ == '__main__':
    main()

70.TCP服务器端的创建流程

1.创建TCP服务端的socket
2.bing绑定ip地址和端口号
3.listen使套接字变为被动套接字
4.accept取出一个客户端连接,用于服务
5.recv/send接收和发送消息
6.关闭套接字

import socket
def main():
    # 1、创建tcp服务端的socket
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 2、绑定
    server_socket.bind(('', 8888))
    # 3、listen使套接字变为被动套接字
    server_socket.listen(128)
    # 4、如果有新的客户端来链接服务器,那么就产生一个新的套接字专门为这个客户端服务
    # client_socket用来为这个客户端服务
    # tcp_server_socket就可以省下来专门等待其他新客户端的链接
    client_socket, client_addr = server_socket.accept()
    # 5、接收客户端发来的消息
    recv_data = client_socket.recv(1024)
    print("接收到客户端%s的数据:%s" % (str(client_addr), recv_data.decode('gbk')))
    # 6、回复数据给客户端
    client_socket.send("收到消息".encode('gbk'))
    # 7、关闭套接字
    client_socket.close()
    server_socket.close()
if __name__ == '__main__':
    main()

71.TCP是面向连接的通讯协议,通过三次握手建立连接,通讯完成时四次挥手

'''
优点:TCP在数据传递时,有确认、窗口、重传、阻塞等控制机制,能保证数据正确性,较为可靠。
缺点:TCP相对于UDP速度慢一点,要求系统资源较多。
'''

72.简述浏览器通过WSGI请求动态资源的过程?

'''
1.发送http请求动态资源给web服务器
2.web服务器收到请求后通过WSGI调用一个属性给应用程序框架
3.应用程序框架通过引用WSGI调用web服务器的方法,设置返回的状态和头信息。
4.调用后返回,此时web服务器保存了刚刚设置的信息
5.应用程序框架查询数据库,生成动态页面的body的信息
6.把生成的body信息返回给web服务器
7.web服务器吧数据返回给浏览器
'''

73.描述用浏览器访问www.baidu.com的过程

'''
先要解析出baidu.com对应的ip地址
- 要先使用arp获取默认网关的mac地址
- 组织数据发送给默认网关(ip还是dns服务器的ip,但是mac地址是默认网关的mac地址)
- 默认网关拥有转发数据的能力,把数据转发给路由器
- 路由器根据自己的路由协议,来选择一个合适的较快的路径转发数据给目的网关
- 目的网关(dns服务器所在的网关),把数据转发给dns服务器
- dns服务器查询解析出baidu.com对应的ip地址,并原路返回请求这个域名的client
得到了baidu.com对应的ip地址之后,会发送tcp的3次握手,进行连接
使用http协议发送请求数据给web服务器
- web服务器收到数据请求之后,通过查询自己的服务器得到相应的结果,原路返回给浏览器。
- 浏览器接收到数据之后通过浏览器自己的渲染功能来显示这个网页。
- 浏览器关闭tcp连接,即4次挥手结束,完成整个访问过程
'''

74.Post和Get请求的区别?

GET请求:

'''
请求的数据会附加在URL之后,以?分割URL和传输数据,多个参数用&连接。URL的
编码格式采用的是ASCII编码,而不是uniclde,即是说所有的非ASCII字符都要编码之后再传输。
'''

POST请求:

'''
POST请求会把请求的数据放置在HTTP请求包的包体中。上面的item=bandsaw就
是实际的传输数据。
因此,GET请求的数据会暴露在地址栏中,而POST请求则不会。
传输数据的大小:
- 在HTTP规范中,没有对URL的长度和传输的数据大小进行限制。但是在实际开发过程中,对
于GET,特定的浏览器和服务器对URL的长度有限制。因此,在使用GET请求时,传输数据会
受到URL长度的限制。
- 对于POST,由于不是URL传值,理论上是不会受限制的,但是实际上各个服务器会规定对POST
提交数据大小进行限制,Apache、IIS都有各自的配置。
安全性:
- POST的安全性比GET的高。这里的安全是指真正的安全,而不同于上面GET提到的安全方法
中的安全,上面提到的安全仅仅是不修改服务器的数据。比如,在进行登录操作,通过GET请求,
用户名和密码都会暴露再URL上,因为登录页面有可能被浏览器缓存以及其他人查看浏览器的
历史记录的原因,此时的用户名和密码就很容易被他人拿到了。除此之外,GET请求提交的数据
还可能会造成Cross-site request frogery攻击。
'''

效率:GET比POST效率高。

'''
POST请求的过程:
  1.浏览器请求tcp连接(第一次握手)
  2.服务器答应进行tcp连接(第二次握手)
  3.浏览器确认,并发送post请求头(第三次握手,这个报文比较小,所以http会在此时进行
第一次数据发送)
  4.服务器返回100 continue响应
  5.浏览器开始发送数据
  6.服务器返回200 ok响应
GET请求的过程:
    1.浏览器请求tcp连接(第一次握手)
    2.服务器答应进行tcp连接(第二次握手)
    3.浏览器确认,并发送get请求头和数据(第三次握手,这个报文比较小,所以http会在此时
进行第一次数据发送)
    4.服务器返回200 OK响应
'''
'''
1、cookie数据存放在客户的浏览器上,session数据放在服务器上。
2、cookie不是很安全,别人可以分析存放在本地的cookie并进行cookie欺骗考虑到安全应当使
用session。
3、session会在一定时间内保存在服务器上。当访问增多,会比较占用服务器的性能考虑到减轻服
务器性能方面,应当使用cookie。
4、单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。
5、建议: 将登陆信息等重要信息存放为SESSION 其他信息如果需要保留,可以放在cookie中
'''

76.HTTP协议状态码有什么用,列出你知道的 HTTP 协议的状态码,然后讲出他们都 表示什么意思?

'''
通过状态码告诉客户端服务器的执行状态,以判断下一步该执行什么操作。
常见的状态机器码有:
100-199:表示服务器成功接收部分请求,要求客户端继续提交其余请求才能完成整个处理过程。
200-299:表示服务器成功接收请求并已完成处理过程,常用200(OK请求成功)。
300-399:为完成请求,客户需要进一步细化请求。302(所有请求页面已经临时转移到新的url)。
 304、307(使用缓存资源)。
400-499:客户端请求有错误,常用404(服务器无法找到被请求页面),403(服务器拒绝访问,
权限不够)。
500-599:服务器端出现错误,常用500(请求未完成,服务器遇到不可预知的情况)。
'''

77.说一下什么是tcp的2MSL?

'''
主动发送fin关闭的一方,在4次挥手最后一次要等待一段时间我们称这段时间为2MSL
TIME_WAIT状态的存在有两个理由:
1.让4次挥手关闭流程更加可靠
2.防止丢包后对后续新建的正常连接的传输造成破坏
'''

78.为什么客户端在TIME-WAIT状态必须等待2MSL的时间?

'''
1、为了保证客户端发送的最后一个ACK报文段能够达到服务器。 这个ACK报文段可能丢失,
因而使处在LAST-ACK状态的服务器收不到确认。服务器会超时重传FIN+ACK报文段,客户端就
能在2MSL时间内收到这个重传的FIN+ACK报文段,接着客户端重传一次确认,重启计时器。最
好,客户端和服务器都正常进入到CLOSED状态。如果客户端在TIME-WAIT状态不等待一段时间,
而是再发送完ACK报文后立即释放连接,那么就无法收到服务器重传的FIN+ACK报文段,因而也
不会再发送一次确认报文。这样,服务器就无法按照正常步骤进入CLOSED状态。
2、防止已失效的连接请求报文段出现在本连接中。客户端在发送完最后一个ACK确认报文段
后,再经过时间2MSL,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失。这样
就可以使下一个新的连接中不会出现这种旧的连接请求报文段。
'''

79.说说HTTP和HTTPS区别?

'''
HTTP协议传输的数据都是未加密的,也就是明文的,因此使用HTTP协议传输隐私信息非常不安
全,为了保证这些隐私数据能加密传输,于是网景公司设计了SSL(Secure Sockets Layer)协议用于
对HTTP协议传输的数据进行加密,从而就诞生了HTTPS。简单来说,HTTPS协议是由SSL+HTTP协
议构建的可进行加密传输、身份认证的网络协议,要比http协议安全。
HTTPS和HTTP的区别主要如下:
1、https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
2、http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
3、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
4、http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、
身份认证的网络协议,比http协议安全。
'''

80.谈一下HTTP协议以及协议头部中表示数据类型的字段?

'''
HTTP 协议是 Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于从万维网
(WWW:World Wide Web)服务器传输超文本到本地浏览器的传送协议。
HTTP 是一个基于 TCP/IP 通信协议来传递数据(HTML 文件, 图片文件, 查询结果等)。

HTTP 是一个属于应用层的面向对象的协议,由于其简捷、快速的方式,适用于分布式超媒体
信息系统。它于 1990 年提出,经过几年的使用与发展,得到不断地完善和扩展。目前在 WWW 中
使用的是 HTTP/1.0 的第六版,HTTP/1.1 的规范化工作正在进行之中,而且 HTTP-NG(Next
Generation of HTTP)的建议已经提出。
HTTP 协议工作于客户端-服务端架构为上。浏览器作为 HTTP 客户端通过URL 向 HTTP 服
务端即 WEB 服务器发送所有请求。Web 服务器根据接收到的请求后,向客户端发送响应信息。
表示数据类型字段: Content-Type
'''

81. HTTP请求方法都有什么?

'''
根据HTTP标准,HTTP请求可以使用多种请求方法。
HTTP1.0定义了三种请求方法: GET, POST 和 HEAD方法。
HTTP1.1新增了五种请求方法:OPTIONS, PUT, DELETE, TRACE 和 CONNECT 方法。
1、 GET 请求指定的页面信息,并返回实体主体。
2、HEAD 类似于get请求,只不过返回的响应中没有具体的内容,用于获取报头
3、POST 向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在
请求体中。POST请求可能会导致新的资源的建立和/或已有资源的修改。
4、PUT 从客户端向服务器传送的数据取代指定的文档的内容。
5、DELETE 请求服务器删除指定的页面。
6、CONNECT HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。
7、OPTIONS 允许客户端查看服务器的性能。
8、TRACE 回显服务器收到的请求,主要用于测试或诊断。
'''

82. 使用Socket套接字需要传入哪些参数 ?

'''
Address Family 和 Type,分别表示套接字应用场景和类型。
family的值可以是AF_UNIX(Unix域,用于同一台机器上的进程间通讯),也可以是AF_INET
(对于IPV4协议的TCP和 UDP),至于type参数,SOCK_STREAM(流套接字)或者
SOCK_DGRAM(数据报文套接字),SOCK_RAW(raw套接字)。
'''

83. HTTP常见请求头?

'''
1. Host (主机和端口号)
2. Connection (链接类型)
3. Upgrade-Insecure-Requests (升级为 HTTPS 请求)
4. User-Agent (浏览器名称)
5. Accept (传输文件类型)
6. Referer (页面跳转处)
7. Accept-Encoding(文件编解码格式)
8. Cookie (Cookie)
9. x-requested-with :XMLHttpRequest  (是 Ajax 异步请求)
'''

84. 七层模型? IP ,TCP/UDP ,HTTP ,RTSP ,FTP 分别在哪层?

'''
IP: 网络层 TCP/UDP: 传输层 HTTP、RTSP、FTP: 应用层协议
'''

85. url的形式?

'''
形式: scheme://host[:port#]/path/…/[?query-string][#anchor]
scheme:协议(例如:http, https, ftp)
host:服务器的IP地址或者域名
port:服务器的端口(如果是走协议默认端口,80 or 443)
path:访问资源的路径
query-string:参数,发送给http服务器的数据
anchor:锚(跳转到网页的指定锚点位置)
http://localhost:8000/file/part01/1.2.html
'''
posted @ 2019-10-17 00:40  suwanbin_thought  阅读(102)  评论(0编辑  收藏  举报