每日分享
What the caterpillar calls the end, the rest of the world calls a butterfly
毛毛虫终结之时,就是破茧成蝶之日。
小闫语录:
黎明前的夜最黑,你所绝望之时,有可能是下一个机遇。不要放弃,坚持不了的时候再坚持一下。
python技术面试题(五)
1.HTTP/HTTPS协议
HTTP协议是超文本传输协议,是web联网的基础,也是手机联网常用的协议之一,HTTP协议是建立在TCP协议之上的一种应用。它属于应用层。HTTP连接最显著的特点就是客户端发送的每次请求都需要服务器回送响应,在请求结束后,会主动释放连接。从建立连接到关闭连接的过程称为“一次连接”。HTTP是用来在网络上传输HTML文本的协议,用于浏览器和服务器的通信。有一点需要注意,它是明文传输,为了安全可以使用HTTPS协议。
HTTPS协议其实就是HTTP套了一层SSL/TLS外壳。这也是它最大的特点,安全。HTTP协议是应用层直接将数据给到TCP进行传输,HTTPS则是改成应用层将数据给到SSL/TLS,将数据加密之后再给TCP进行传输。
HTTP的端口是80;HTTPS的端口是443。SSL是一个加密套件,证书,TLS是SSL的升级版。
HTTP请求报文格式
请求行:请求方法、资源路径、HTTP协议版本
GET / HTTP/1.1\r\n
请求头:有好多,而且不一,只说一下格式:
头名称:头对应的值\r\n
空行和请求体
HTTP响应报文格式
状态行:协议的版本、状态码、状态说明
HTTP/1.1 200 OK\r\n
响应头: 1. 头名称:头对应的值\r\n
空行和响应体
2.TCP/IP协议
TCP称作传输控制协议,是一种面向连接的、可靠的、基于字节流的传输层通信协议。通过三次握手建立连接,通讯完成时四次挥手终止通讯。它的优点就是在数据传递时,有发送应答机制、超时重传机制、错误校验机制、阻塞管理机制等,能保证数据的正确性,可靠。缺点就是相对于UDP来说速度慢一点,要求系统资源也多一点。
TCP/IP协议是传输层协议,主要解决数据如何自网络中传输。HTTP是应用层协议,主要解决如何包装数据。IP协议处于网络层。
2.1 三次握手
文邹邹晦涩版答案:客户端将标志位SYN置为1,随机产生一个值seq(假装是J吧),然后将该数据包发送给服务器。服务器收到数据包之后将标志位SYN和ACK都置为1,ack=J+1。随机产生一个值seq(假装是K吧),然后将该数据包发送给客户端,确认连接请求。客户端收到确认后,看一下ack是不是J+1,看看ACK是不是1,确认无误之后就将标志位ACK置为1,ack=K+1,然后将数据包发送给服务器。服务器检查一下ack和ACK的值是不是K+1和1,正确了就连接成功。
SYN: 表示连接请求 ACK: 表示确认 FIN: 表示关闭连接 seq:表示报文序号 ack: 表示确认序号
生动举例版:A:“喂,老哥,在不?”B:“在呢,咋地啦?”A:“嗯,和你说件事...”巴拉巴拉的开始唠嗑.....
2.2 四次挥手
文邹邹晦涩版答案:客户端和服务器都可以主动关闭,我们以客户端主动关闭连接为例。
第一次挥手:Client发送一个FIN,用来关闭Client到Server的数据传送。
第二次挥手:Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1。
第三次挥手:Server发送一个FIN,用来关闭Server到Client的数据传送。
第四次挥手:Client收到FIN后,接着发送一个ACK给Server,确认序号为收到序号+1。
生动举例版:因为连接时双向通信的,必须双方都同意关闭才算关闭。就好像谈恋爱,俩人都同意分手,才分得彻底,要不然还是藕断丝连。
2.3说一下什么是tcp的2MSL?
主动发送 fin 关闭的一方,在 4 次挥手最后一次要等待一段时间我们称这段时间为 2MSL
。
2.4为什么客户端在 TIME-WAIT 状态必须等待 2MSL 的时间?
就好像分手一样,客户端主动关闭的,你得为这件事负责吧?客户端为了确保服务器收到最后一次挥手的报文。如果最后一次丢包了,服务器没有收到第四次挥手的报文,还以为客户端不想分手,就会再重发一次第三次挥手的报文,看看客户端是不是后悔了,不想分手了。这个等待时间就是为了接收超时重传的报文。
假如客户端发完就断开了链接,然后服务器一直等不到回应,重传了报文还是没有得到回应,服务器不死心啊,服务器就关闭不了链接。客户端这时就是典型的渣男角色,分手你别让别人还抱有幻想是不是?说多了,回归正题。
tcp最大的特点就是数据不会丢失啊,客户端渣渣的建立了新连接,然后发现有一个旧的数据包,然后让客户端的新连接也不好了,这就叫自食其果。所以有了这个等待时间,既保证了双方都正常关闭,又保证了所有报文段消失,不会在新连接中出现旧的请求报文段。
3.WSGI
先回忆一下后端服务器,它其实分为了web服务器和web框架。web服务器负责解析请求报文,调用框架程序处理请求;组织响应报文,返回内容给客户端。web框架则是负责路由分发(根据url找到对应的处理函数);处理函数中进行业务的处理。
WSGI其实就是为python语言定义的web服务器和web框架之间的一个接口。用来描述web服务器如何与web框架通信的规范。
WSGI协议中,重中之重就是一个接口函数:
def application(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) return [b'<h1>Hello, web!</h1>']
第一个参数是字典类型的请求地址、请求方式等。第二个参数是一个回调函数,用来传递响应状态结果。返回值是响应体。
实现的过程很是巧妙,帮大家简单的回忆一下:
1.在服务器中调用application函数。
2.在服务器中定义用来储存返回的响应头信息的回调函数,函数有两个参数,一个是状态,一个是其它信息,以字典形式传入。
3.在服务器中以字典传入请求地址名,传入回调的函数名。
4.在框架中定义application函数,当处理完数据后,调用传入的函数并返回数据。
5.服务器收到返回的信息后进行响应信息的拼接处理。
通过WSGI接口,可以实现服务器和框架的功能分离。便于服务器的迁移和维护。
4.多任务相关知识
1.并行:基于多个CPU同一时间点执行的多任务方式。
2.并发:基于时间片轮转的方式执行多任务的方式。
3.谈谈你对多进程,多线程,以及协程的理解,项目是否用?
答:一个运行的程序就是一个进程,没有运行的代码叫程序,进程是系统资源分配的最小单位,进程拥有自己独立的内存空间,所有进程间的数据不共享,开销大。进程之间通信使用Queue。线程是调度执行的最小单位,也叫执行路径,不能独立存在,依赖进程存在。一个进程至少有一个线程,叫主线程,而多个线程共享内存(数据共享,共享全局变量),从而极大的提高了程序的运行效率。但是cpython中是伪多线程。由于GIL的存在,python程序中同一时刻有且只有一个线程会执行,无法有效利用多核CPU。协程,又称微线程,纤程,也称为用户级线程,在不开辟线程的基础上完成多任务,也就是在单线程的情况下完成多任务,多个任务按照一定顺序交替执行 通俗理解只要在def里面只看到一个yield关键字表示就是协程。
4.协程Gevent实现多任务,为了让Gevent框架识别耗时操作时自动切换执行对应的任务,可以使用猴子补丁:
from gevent import monkey monkey.patch_all()
5.生成器和迭代器
1.使用自定义迭代器输出斐波那契数列的前10项。
class Fibonacci(object): def __init__(self,num): self.num = num self.a = 0 self.b = 1 self.current_index = 0 def __next__(self): if self.current_index < self.num: result = self.a self.a,self.b = self.b,self.a+self.b self.current_index += 1 return result else: raise StopIteration def __iter__(self): return self if __name__ == '__main__': fib = Fibonacci(10) for value in fib: print(value,end=' ')
2.使用生成器输出斐波那契数列前10项。
def fibonacci(num): a = 0 b = 1 current_index = 0 while current_index < num: result = a a,b = b,a+b current_index += 1 yield result if __name__ == '__main__': fibonacci = fibonacci(10) for value in fibonacci: print(value,end=' ')
6.QQ第三方登录开发流程
第一步:浏览器向服务器请求获取QQ登录网址。
第二步:服务器向客户端返回QQ登录网址和参数。
给客户端返回的数据:
{ "login_url": "https://graph.qq.com/oauth2.0/show?which=Login&display=pc&response_type=code&client_id=101474184&redirect_uri=http%3A%2F%2Fwww.ethan.site%3A8080%2Foauth_callback.html&state=%2F&scope=get_user_info" }
第三步:客户端根据上面返回的QQ登录网址向QQ服务器发起请求。
第四步:QQ服务器向客户端返回QQ授权登录的页面。
第五步:用户开始在授权页面进行操作,登录QQ。
第六步:授权成功之后,QQ服务器让浏览器重定向访问回调网址,,并在网址后面携带code和原始的state参数,此处的参数是去QQ服务器提供的。
参数 | 说明 |
code | QQ返回的授权凭证,根据code可以获取access_token |
status | client端的状态值。用于第三方应用防止CSRF攻击,成功授权后回调时会原样带回。请务必严格按照流程检查用户与state参数状态的绑定。重定向到我们指定的页面,如果用户没有进行绑定,会跳转绑定的页面,这个时候在查询字符串中有两个参数,一个是code,一个是status |
第七步:客户端访问回调网址,携带QQ提供的code参数给服务器。然后获取QQ登录用户的openid并处理。
参数 | 说明 |
openid | OpenID是此网站上或应用中唯一对应用户身份的标识,网站或应用可将此ID进行存储,便于用户下次登录时辨识其身份,或将其与用户在网站上或应用中的原有账号进行绑定。 |
第八步:服务器根据code请求QQ服务器获取 access_token
。
参数 | 说明 |
access_token | 用户是第一次使用QQ登录时返回,其中包含openid,用于绑定身份使用,注意这个是我们自己生成的。 |
第九步:QQ服务器向服务器返回所需要的 access_token
。
第十步:服务器凭借 access_token
请求QQ服务器获取openid。
第十一步:QQ服务器返回openid给服务器。
第十二步:服务器接下来根据openid判断是否绑定过本网站用户。后端接口根据openid到数据库查询 tb_oatu_qq
表(该表中记录了openid和User_id的绑定情况)
第十三步:如果绑定过,直接签发jwt token并返回给客户端,让客户端保存这个token。
第十四步:如果没有绑定过,将openid加密并返回给客户端。进行下面的步骤:
第十五步:前端也会做相应的判断,如果绑定过,直接就返回到首页登录网址,如果没有绑定过,则会在浏览器显示绑定页面,要求用户填写表单进行绑定。
第十六步:用户在填写完上面的表单,点击保存按钮的时候,客户端向服务器发起请求绑定QQ登录用户,服务器将表单信息保存到数据库中。
第十七步:服务器签发jwt token并返回给客户端。