网络知识点总结归纳

本文主要对Web基础和前端框架知识进行梳理、回顾,把一些需要记住的概念原理,和容易混淆,晦涩的知识点进行归纳。

Web基础

1. 常用的网络传输协议

  • TCP:传输控制协议,可靠传输,面向连接
  • UDP:用户数据包协议,不可靠传输,面向无连接
  • FTP:文件传输协议,用于上传和下载文件
  • HTTP:超文本传输协议,基于TCP/IP通信协议,面向对象
  • SMTP:邮件传输协议
  • TELNET:Internet远程登录服务的标准协议和主要方式
  • DNS:域名系统,将域名解析为ip地址

2. ICMP协议与IGMP协议

ICMP(Internet Control Message Protocol)Internet控制报文协议,是一种面向无连接的网络层协议,是TCP/IP协议族的一个子协议,用于主机、路由器之间传递控制消息。ICMP协议的主要功能是确认IP包是否成功到达目标地址,通知在发送过程中IP包被丢弃的原因。

IGMP(Internet Group Management Protocol)Internet组管理协议,是TCP/IP协议族中负责IPV4组播成员管理的协议。IGMP协议用来接收主机与其直接相邻的组播路由器之间建立和维护组播成员关系,通过在接受主机和组播路由器之间交互IGMP报文实现组成员管理功能,IGMP报文封装在IP报文中,IGMP共有v1、v2、v3三个版本。

3. ARP协议与RARP协议

ARP(Address Resolution Protocal)地址解析协议,其基本功能为透过目标设备的IP地址,查询目标的MAC地址,以保证通信顺利进行。它是IPv4网络层必不可少的协议,不过在IPv6已经不再适用,并被邻居发现协议(NDP)所替代。

RARP(Reverse Address Resolution Protocol)反向地址转换协议允许局域网的物理机器从网关服务器的ARP表或缓存上请求IP地址。网络管理员在局域网网关路由器里创建一个表以映射物理地址(MAC)和与其对应的IP地址,当设置一台新的机器时,其RARP客户机程序需要想路由器上的RARP服务器请求响应的IP地址。

4. TCP与UDP的区别

  • TCP提供面向连接的传输,通信前要先建立连接(三次握手机制);UDP提供无连接的传输,通信前不需要建立连接。
  • TCP提供可靠的传输(有序、无差错、不丢失、不重复);UDP提供不可靠的传输。
  • TCP面向字节流的传输,因此它能将信息分割成组,并在接收端将其重组;UDP是面向数据报文的传输,没有分组开销。
  • TCP首部开销20字节;UDP的首部开销小,只有8字节。
  • TCP的逻辑通信信道是全双工的可靠信道;UDP是不可靠信道。
  • TCP提供拥塞控制和流量控制机制;UDP不提供拥塞控制和流量控制。

应用场景:

  • 对数据可靠性的要求高的应用需要选择TCP协议,如验证密码;而对数据可靠性要求不那么高的可以选择UDP协议。
  • 对应用实时性要求高的应用可以选择UDP协议,如视频监控等。
  • 在网络状况不好的情况下需选用TCP协议,如广域网;而网络状况很好的情况下就不需要采用TCP协议,建议选择UDP协议来减少网络负荷,如局域网。

5. 常见的状态码

状态码 状态 描述
200 Ok 请求成功
204 No Content 请求被受理但没有资源可以返回
206 Partial Content 客户端只是请求资源的一部分,服务器只对请求的部分资源执行GET方法
301 Moved Permanently 永久性重定向
302 Found 临时重定向
303 See Other 与302状态码有相似功能,只是它希望客户端在请求一个URI的时候,能通过GET方法重定向到另一个URI上
304 Not Modified 发送附带条件的请求时,条件不满足时返回,与重定向无关
307 Temporary Redirect 临时重定向,与302类似,只是强制要求使用POST方法
400 Bad Request 请求语法错误,不能被服务器解析
401 Unauthorized 未经授权,需与www-Authenticate一起用
403 Forbidden 服务器收到请求,但拒绝提供服务
404 Not Found 请求资源不存在
500 Internal Server Error 服务器发生不可预期的错误
503 Server Unavailable 服务器当前不能处理客户端的请求,一段时间后可能恢复正常

总之,HTTP状态码分为5个类别:

  • 1xx:指示信息–表示请求已接收,继续处理
  • 2xx:成功–表示请求已被成功接收、理解、接受
  • 3xx:重定向–要完成请求必须进行更进一步的操作
  • 4xx:客户端错误–请求有语法错误或请求无法实现
  • 5xx:服务器端错误–服务器未能实现合法的请求

参考来源

6. 三次握手

  • 第一次握手:建立连接时,客户端发送SYN包(SYN=1,seq=x)到服务器,并进入同步已发送状态(SYN_SENT),等待服务器确认。状态过程:SYN=1, seq=x
  • 第二次握手:服务器收到SYN包,必须确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(seq=y),即SYN+ACK包(SYN=1,ACK=1),服务器进入同步收到状态(SYN_RECV)。状态过程:SYN=1, ACK=1, seq=y, ack=x+1
  • 第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认ACK包(ack=y+1),此包发送完毕,客户端和服务器进入已建立连接状态(ESTAB_LISHED),完成握手。状态过程:ACK=1, seq=x+1, ack=y+1

7. 四次挥手

  • 第一次挥手:客户端进程发出连接释放报文,并停止发送数据。释放数据报文首部,FIN=1,其序列号为seq=u(实际上等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入终止等待1状态(FIN_WAIT_1)。TCP规定,FIN报文段即使不携带数据也要消耗洗个序号。状态过程:FIN=1, seq=u
  • 第二次挥手:服务器收到连接释放报文,发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号seq=v,此时服务端就进入等待关闭状态(CLOSE-WAIT)。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但服务器若发送数据,客户端依然要接受。客户端收到服务器的确认请求后,此时客户端就进入了终止等待状态2(FIN-WAIT-2),接受服务器发送的最后的数据并等待服务器发送连接释放报文。状态过程:ACK=1, seq=v, ack=u+1
  • 第三次挥手:服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1,ack=u+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时服务器就就进入了最后确认状态(LAST-ACK),等待客户端确认。状态过程:FIN=1, ACK=1, seq=w, ack=u+1
  • 第四次挥手:客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时客户端就进入了时间等待状态(TIME-WAIT)。此时TCP连接还没有释放,必须经过最长报文段寿命(2MSL)的时间后,当客户端撤销相应的TCB后,服务器就接收到客户端发出的确认,才进入CLOSED状态。状态过程:ACK=1, seq=u+1, ack=w+1

8. 为什么连接的时候是三次握手,关闭的时候却是四次握手

因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,”你发的FIN报文我收到了”。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。

9. 为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态

按道理,四个报文都发送完毕,我们可以直接进入CLOSE状态了,但是我们必须假想网络是不可靠的,有可以最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。在Client发送出最后的ACK回复,但该ACK可能丢失。Server如果没有收到ACK,将不断重复发送FIN片段。所以Client不能立即关闭,它必须确认Server接收到了该ACK。Client会在发送出ACK之后进入到TIME_WAIT状态。Client会设置一个计时器,等待2MSL的时间。如果在该时间内再次收到FIN,那么Client会重发ACK并再次等待2MSL。所谓的2MSL是两倍的MSL(Maximum Segment Lifetime)。MSL指一个片段在网络中最大的存活时间,2MSL就是一个发送和一个回复所需的最大时间。如果直到2MSL,Client都没有再次收到FIN,那么Client推断ACK已经被成功接收,则结束TCP连接。

10. 如果已经建立了连接,但是客户端突然出现故障了怎么办

TCP还设有一个保活计时器,显然,客户端如果出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75秒钟发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。

11. session和cookie的区别

Cookie实际上是一小段的文本信息。客户端请求服务器,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie,客户端会把Cookie保存起来。

Session是另一种记录客户状态的机制,不同的是Cookie保存在客户端浏览器中,而Session保存在服务器上。客户端浏览器访问服务器的时候,服务器把客户端信息以某种形式记录,以便再次访问时确认身份。每个用户访问服务器都会建立一个session,用户与服务器建立连接的同时,服务器会自动为其分配一个SessionId。

session和cookie的区别:

  • cookie数据存放在客户的浏览器上,session数据放在服务器上
  • cookie不是很安全,别人可以分析存放在本地的cookie并进行cookie欺骗,考虑到安全应当使用session
  • session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能,考虑到减轻服务器性能方面,应当使用cookie
  • 单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie
  • 可以考虑将登陆信息等重要信息存放为session,其他信息如果需要保留,可以放在cookie中

应用场景:

  • 登录网站,今输入用户名密码登录了,第二天再打开很多情况下就直接打开了。这个时候用到的一个机制就是cookie
  • session一个场景是购物车,添加了商品之后客户端处可以知道添加了哪些商品,而服务器端如何判别呢,所以也需要存储一些信息就用到了session

12. 对称加密和非对称加密

  • 对称加密

即加密密钥和解密密钥相同,如DES、AES等。

  • 非对称加密

将加密密钥分为公钥和私钥,公钥可以公开,私钥是保密的;客户端用公钥加密数据,服务端可以用私钥解密。如RSA、ECC等。

13. HTTP和HTTPS的区别

HTTP协议传输的数据都是未加密的,也就是明文的,因此使用HTTP协议传输隐私信息非常不安全,为了保证这些隐私数据能够加密传输,引入了SSL(Secure Sockets Layer)协议对HTTP协议传输的数据进行加密,因此HTTPS等同于HTTP + SSL。

HTTPS协议简单来说就是HTTP + SSL协议构建的可进行加密传输、身份认证的网络协议,比HTTP协议更加安全。

HTTP与HTTPS的主要区别:

  • HTTP是超文本传输协议,信息是明文传输,是不安全的,而HTTPS则是具有安全性的SSL加密传输协议,是安全的;
  • HTTPS协议需要到CA申请签名颁发的SSL证书,而HTTPS无需证书;
  • HTTP和HTTPS使用的是完全不同的链接方式,用的端口也不一样,HTTPS默认采用80作为通信端口,而HTTPS默认采用443为通信端口;
  • 在OSI网络模型中,HTTP工作于应用层,而HTTPS的安全协议机制工作于传输层。

14. HTTP协议是无状态协议,怎么解决

无状态协议对于事务处理没有记忆能力,无状态记录导致同一客户端HTTP请求完成后,再次发送HTTP请求时,HTTP不知道当前客户端是一个“老客户”。

解决:使用Cookie,Cookie相当于一个身份通行证,客户端第一次访问时给客户端发一个Cookie,当该客户端再次访问时,验证Cookie服务器就可放行。

参考来源

15. 常用HTTP方法有哪些

  • GET: 用于请求访问已经被URI(统一资源标识符)识别的资源,可以通过URL传参给服务器
  • POST: 用于传输信息给服务器,主要功能与GET方法类似,但一般推荐使用POST方式
  • PUT:传输文件,报文主体包含文件内容,保存到对应的URI位置
  • HEAD:获得报文首部,与GET方法类似,只是不返回报文主体,一般用于验证URI是否有效
  • DELETE:删除文件,与PUT方法相反,删除对应的URI位置的文件
  • OPTIONS:查询相应URI支持的HTTP方法

16. HTTP请求报文和响应报文

请求报文包含四个部分:

  • 请求行:请求方法、URI、HTTP版本信息
  • 请求首部字段
  • 请求内容实体
  • 空行

响应报文包含四个部分:

  • 状态行:HTTP版本、状态码、状态码的原因短语
  • 响应首部字段
  • 响应内容实体
  • 空行

17. HTTP协议常见的首部

通用首部字段

  • Date: 创建报文时间
  • Connection: 连接管理
  • Cache-Control:缓存控制
  • Transfer-Encoding:报文主体的传输编码方式

请求首部字段

  • Host: 请求资源所在服务器
  • Accept: 可处理的媒体类型
  • Accept-Charset: 可接受的字符集
  • Accept-Encoding: 可接受的内容编码
  • Accept-Language: 可接受的自然语言

响应首部字段

  • Accept-Ranges: 可接受的字节范围
  • Location:令客户端重定向到的URI
  • Server:HTTP服务器的安装信息

实体首部字段

  • Allow:资源可支持的HTTP方法
  • Content-Type:实体主类的类型
  • Content-Encoding:实体主体适用的编码方式
  • Content-Language:实体主体的自然语言
  • Content-Length:实体主体的字节数
  • Content-Range:实体主体的范围,一般用于发出部分请求时使用

18. http的头部,keep-alive的作用

一般情况下,一旦Web服务器向浏览器发送了请求数据,它就要关闭TCP连接,然后如果浏览器或者服务器在其头信息加入了这行代码:Connection: keep-alive,TCP连接在发送后将仍然保持打开状态,这时浏览器可以继续通过相同的连接发送请求。保持连接节省了为每个请求建立新连接所需的时间,还节约了网络带宽。

19. 一次完整的HTTP请求的过程

HTTP通信机制是在一次完整的HTTP通信过程中,Web浏览器与Web服务器之间经过以下7个步骤:

  • (1)建立TCP连接:TCP/IP协议构建连接网络
  • (2)Web浏览器向Web服务器发送请求命令:HTTP/1.1
  • (3)Web浏览器发送请求头
  • (4)Web服务器应答:HTTP/1.1 200 OK
  • (5)Web服务器发送应答头: 发送服务器自身信息及被请求的文档
  • (6)Web服务器向浏览器发送数据:以Content-Type应答头信息所描述的格式发送用户所请求的实际数据
  • (7)Web服务器关闭TCP连接:弱项保持连接则需要在头信息加入Connection: keep-alive

20. HTTP1.0与HTTP1.1的区别

参考来源

缓存处理:在HTTP1.0中主要使用header里的If-Modified-Since, Expires来做缓存判断的标准,HTTP1.1则引入了更多缓存控制策略,如Entity tag, If-Unmodified-Since, If-Match, If-None-Match等更多可供闲着的缓存头来控制缓存策略。

带宽优化及网络连接的使用:在HTTP1.0中,存在一些浪费带宽的现象,如客户端是需要某个对象的一部分,而服务器却将整个对象发送过来了,并且不支持断点续传功能;在HTTP1.1中,在请求头引入了Range头域,它允许只请求资源的某个个部分,即返回码是206(Partial Content),这使得开发者可以自由选择,以便充分利用带宽和连接。

错误通知管理:在HTTP1.1中新增了24个错误状态响应码,如409表示请求资源与资源当前的状态发生冲突;410便是服务器上的某个资源被永久删除。

Host头处理:在HTTP1.0中认为每台服务器都绑定唯一的IP地址,因此请求消息中的URL并没有传递主机名(hostname)。但随着虚拟主机技术的发展,在一台物理服务器上可以存在多个虚拟主机,并且他们共享一个IP地址;在HTTP1.1中,请求消息和响应消息都支持Host头域,且请求消息中如果没有Host头域会报告400(Bad Request)。

长连接:HTTP1.1支持长连接(Persistent Connection)和请求的流水线处理(Pipelining),在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟,在HTTP1.1中默认开启Connection: keep-alive,一定程度上弥补了HTTP1.0每次请求都要创建连接的缺点。

新增Request方法:HTTP1.1增加了OPTIONS、PUT、DELETE、TRACE、CONNECT这些Request方法。

21. HTTP2.0与HTTP1.x的区别

参考来源

二进制格式(Binary Format):HTTP1.x的解析是基于文本协议格式解析的,存在缺陷不够健壮;而HTTP2.0的协议解析采用的是二进制格式,通用更健壮。

多路复用(MultiPlexing):即连接共享,每个request都是使用连接共享机制,一个request对应一个id,这样一个连接上可以有多个request,每个连接的request可以随机混杂在一起,接收方可以根据request的id将request归属到各自不同的服务端请求里。

header压缩:在HTTP1.x的header中带有大量的信息,而且每次请求都要重复发送,而HTTP2.0中使用encoder来减少需要传输的header的大小,通讯双方各自cache一份header fields表,既避免了重复传输header,又减少了需要传输的大小。

服务端推送(Server Push):同SPDY一样,HTTP2.0也具有Server Push的功能,如网页有个style.css的请求,在客户端接收到style.css数据的同时,服务端会将style.js的文件推送给客户端,但客户端再次尝试获取style.js时就可以直接从缓存中获取,就不用在发请求了。

22. 一次完整的HTTPS请求的过程

图解HTTPS

  • (1)客户端发起HTTPS请求:浏览器输入网址,连接服务器的443端口;
  • (2)服务器收到请求:选择浏览器支持的加密算法和哈希算法;
  • (3)服务器将数值证书返回给客户端:证书序列号、证书过期时间、站点组织名、站点DNS主机名、站点公钥、证书颁发者、证书签名;
  • (4)客户端解析证书:由浏览器内置的TSL完成的,验证公钥是否有效,无误这生成一个随机值R,并用证书对随机值R进行加密;
  • (5)客户端传送加密后的随机值R:服务端获得这个加密的随机值,加密解密的钥匙;
  • (6)服务端解密信息:服务器用自己的私钥解密得到随机值R(即客户端的私钥);
  • (7)传输加密内容信息:服务端使用R对网页内容进行对称加密,并发送给客户端;
  • (8)客户端解密内容信息:客户端用自己生成的私钥对服务端传过来的网页内容进行解密。

23. TCP拥塞控制

TCP拥塞控制

  • 慢开始和拥塞避免

发送方维持一个拥塞窗口cwnd的状态变量,拥塞窗口的大小取决于网络的拥塞程度,并且动态地变化;发送方让自己的发送窗口等于拥塞窗口,控制拥塞窗口的原则是只要网络没有出现拥塞,拥塞窗口就在增大一些,以便把更多的分组发送出去;但只要网络出现拥塞,拥塞窗口就减小一些,以减少注入到网络的分组数。

慢开始算法:慢开始的“慢”并不是指cwnd的增长速率慢,而是指在TCP开始发送报文段时先设置cwnd=1,使得发送方在开始时只发送一个报文段,目的是试探网络的拥塞情况,然后在cwnd加倍增大。为了防止拥塞窗口cwnd增长过大引起网络拥塞,设置一个慢开始门限ssthresh状态变量,当cwnd < ssthresh时,使用慢开始算法;当cwnd > ssthresh 时,停止使用慢开始算法而改用拥塞避免算法;当cwnd = ssthresh 时,既可使用慢开始算法也可以使用拥塞避免算法。

拥塞避免算法:每经过一个往返时间RTT就把发送方的拥塞窗口cwnd+1,按线性规律缓慢增长,而不是加倍增长,所以比慢开始算法的拥塞窗口增长速率缓慢得多。无论在慢开始阶段还是在拥塞避免阶段,只要发送方判断网络出现拥塞,就要把慢开始门限ssthresh设置为出现拥塞时发送方窗口子的一半(且>=2)。然后把拥塞窗口cwnd重新设置为1,执行慢开始算法,如此迭代重复,直到迅速减少主机发送到网络中的分组数,使得积压的分组处理完毕。

  • 快重传和快恢复

快重传算法:首先要求接收方每收到一个失序的报文段后就立即发出重复确认,而不会等到自己发送数据时才进行捎带确认。发送方一连收到三个重复确认就应当立即重传对方未接收到的报文段,而不用继续等待为发送成功报文段设置的重传计时器到期,提高整个网络吞吐量约20%。

快恢复算法:当发送方连续收到三个重复确认,就执行“乘法减小”算法,把慢开始门限ssthresh减半,与慢开始不同之处是现在不执行慢开始算法(即拥塞窗口cwnd现在不设置为1),而是把cwnd值设置为慢开始门限ssthresh减半后的数值,然后开始执行拥塞避免算法,使拥塞窗口缓慢地线性增大。

24. socket编程

socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,对于文件用打开/读写/关闭模式来操作。socket就是该模式的一种实现,socket即是一种特殊的文件,一些socket函数就是对其极性的操作(读/写IO、打开、关闭)基本上,socket是任何一种计算机网络通讯中最基础的内容。file模块是针对某个指定文件进行打开/读写/关闭;而socket模块是针对服务器端和客户端socket进行打开/读写/关闭

例如当你在浏览器地址输入http://www.cnblogs.com/时,你会打开一个套接字,然后连接到http://www.cnblogs.com/并读取响应的页面然后显示出来。而其他一些聊天客户端gtalkskype也是类似,任何网络通讯都是通过socket来完成的。

python之socket编程

socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,socket其实就是一个门面模式,它复杂的TCP/IP协议族隐藏在socket接口后面,对用户来说,一组简单的接口就是全部,让socket去组织数据,以符合指定的协议。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# !/usr/bin/env python
# -*- coding:utf-8 -*-

# 服务端

import socket

ip_port = ('127.0.0.1', 9999)
sk= socket.socket() # 创建流套接字描述符
sk.bind(ip_port) # 命令套接字(协议,本地址,本地端口)
sk.listen(5) # 监听客户端socket请求

while True:
print('server waiting...')
conn, addr = sk.accept()
client_data = conn.recv(1024).decode()
print(client_data)
conn.sendall('不要回答,不要回答,不要回答'.encode())
conn.close()

# 结果
'''
server waiting...
请求成功
'''
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# !/usr/bin/env python
# -*- coding:utf-8 -*-

# 客户端

import socket

ip_port = ('127.0.0.1', 9999)
sk= socket.socket()
sk.connect(ip_port)
sk.sendall('请求成功'.encode())

server_reply = sk.recv(1024).decode()
print(server_reply)

sk.close()

# 结果
'''
不要回答,不要回答,不要回答
'''

socket对象内置方法

属性 描述
\***服务器socket方法*****
s.bind(address) 将地址(主机名、端口号对)绑定到套接字上
s.listen(backlog) 设置并启动TCP监听
s.accept() 被动接受TCP客户端连接,一直等待直到连接到达(阻塞状态)
\***客户端socket方法*****
s.connect(address) 主动发起TCP服务器连接
s.connect_ex(address) connect()的扩展版本,此时会以错误码的形式返回问题,而不是抛出一个异常
\***普通的socket方法*****
s.recv(bufsize[.flag]) 接收TCP消息
s.recv_info() 接收TCP消息到指定的缓冲区
s.send(string[,flag]) 发送TCP消息
s.sendall(string[,flag]) 完整地发送TCP消息
s.recvfrom(bufsize[.flag]) 接收UDP消息
s.recvfrom_info() 接收UDP消息到指定的缓冲区
s.sendto(string[,flag],address) 发送UDP消息
s.getpeername(ipaddr,port) 连接到套接字TCP的远程地址
s.getsockname(ipaddr,port) 当前套接字的地址
s.getsockopt() 返回给定套接字选项的值
s.setsockopt() 设置给定套接字选项的值
s.shutdown() 关闭连接
s.close() 关闭套接字
s.detach() 在未关闭文件描述符的情况下关闭套接字,返回文件描述符
s.ioctl() 控制套接字的模式
\***面向阻塞的套接字方法*****
s.setblocking() 设置套接字的阻塞或非阻塞模式
s.settimeout() 设置套接字的阻塞操作的超时时间
s.gettimeout() 获取套接字的阻塞操作的超时时间
\***面向文件的套接字方法*****
s.fileno() 套接字的文件描述符
s.makefile() 创建与套接字关联的文件对象
\***数据属性*****
s.family 套接字家族
s.type 套接字类型
s.proto 套接字协议
  • 基于TCP的sockte通信

TCP协议被称为端对端协议,当一台计算机需要与另外一台计算机连接时,TCP协议会让他们之间建立一个虚拟链路,用于发送和接受数据。TCP协议负责收集这些数据包,并将其按照适当的顺序传送,接收端收到数据包后将其正确的还原,保证数据包在传送过程中准确无误。TCP协议采用重发机制,当一个通信实体发送一个消息给另外一个通信实体后,需要接收到另外一个通信实体的确认信息,如果没有接收到该确认信息,则会重发信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 服务端
import socket
import threading

# 创建socket对象
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM, proto=0, fileno= None)
# 将socket绑定到本机IP地址和端口
server.bind(('127.0.0.1', 9999))
# 服务端开始监听来自客户端的连接
server.listen(5)

def handle_sock(conn, addr):
while True:
data = conn.recv(1024)
print(data.decode("utf-8"))
recv_data = input()
conn.send(recv_data.encode("utf-8"))

while True:
# 每当接收客户端请求时,就返回对应的socket和远程地址
conn, addr = server.accept()
# 用线程去处理新接收的连接
client_thread = threading.Thread(target=handle_sock, args=(conn, addr))
client_thread.start()
1
2
3
4
5
6
7
8
9
10
11
12
13
# 客户端
import socket

# 创建socket对象
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 客户端socket调用connect()方法连接远程服务器地址
client.connect(('127.0.0.1', 9999))

while True:
recv_data = input()
client.send(recv_data.encode("utf-8"))
data = client.recv(1024)
print(data.decode("utf-8"))
  • 基于UDP的socket通信

udp无连接,比TCP更简洁,可以同时多个客户端去跟服务端通信

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 服务端
from socket import *

# 创建socket对象
udp_server = socket(AF_INET, SOCK_DGRAM)
udp_server.bind(('127.0.0.1', 8888))
data1, addr1 = udp_server.recvfrom(1024)
data2, addr2 = udp_server.recvfrom(1024)

while True:
data1 = udp_server.recvfrom(1024)
udp_server.sendto(data1[0], addr2)
data2 = udp_server.recvfrom(1024)
udp_server.sendto(data2[0], addr1)
1
2
3
4
5
6
7
8
9
10
11
# 客户端1
from socket import *

client1 = socket(AF_INET, SOCK_DGRAM)
client1.sendto('hello1'.encode("utf-8"), ('127.0.0.1', 8888))

while True:
msg = input('input-1')
client1.sendto(msg.encode("utf-8"), ('127.0.0.1', 8888))
data, addr = client1.recvfrom(1024)
print(data.decode('utf-8'))
1
2
3
4
5
6
7
8
9
10
11
# 客户端2
from socket import *

client2 = socket(AF_INET, SOCK_DGRAM)
client2.sendto('hello2'.encode('utf-8'), ('127.0.0.1', 8888))

while True:
data, addr = client2.recvfrom(1024)
print(data.decode("utf-8"))
msg = input('input-2')
client2.sendto(msg.encode("utf-8"), ('127.0.0.1', 8888))

25. urllib和urllib2

urllib提供urlencode方法用来GET查询字符串的产生,而urllib2没有。这是为何urllib常和urllib2一起使用的原因。

urllib2可以接受一个Request对象来为URL请求设置headers(请求头),urllib仅可以接受URL,不能伪装用户代理User Agent字符串等。

26. 常见的web安全问题有哪些

(1)SQL注入

SQL注入是一种常见的Web安全漏洞,攻击者利用这个漏洞,可以访问或修改数据,或者利用潜在的数据库漏洞进行攻击。SQL危害到数据库的信息,管理员的账号密码、用户的敏感信息等;可以获取服务器权限;植入攻击脚本;读取服务器敏感文件。

常见的一个例子,登陆页面输入用户名admin' --,密码随意输入,这样子后台的SQL语句会由原来的SELECT * FROM user WHERE username='admin' AND psw='password';就会变为SELECT * FROM user WHERE username='admin' --' AND psw='xxxx';,这就相当于密码部分被注释了,成了万能不需要密码登陆了。

SQL注入的过程包括:

  • 获取用户请求参数
  • 拼接到代码当中
  • SQL语句按照构造参数的语义执行成功

SQL注入的必备条件:

  • 可以控制输入的数据
  • 服务器要执行的代码拼接了控制的数据

SQL注入防御:

  • 严格限制Web应用的数据库操作权限,如权限仅够满足工作即可
  • 后端代码检查输入的数据是否安全合规,如正则匹配
  • 对入库的特殊字符进行转义处理或者编码转换
  • 所有的查询语句建议是同数据库提供的参数化查询接口

(2)CSRF

CSRF(Cross Site Request Forgery)跨站请求伪造,是一种常见的Web攻击,它利用用户已登录的身份,在用户毫不知情的情况下,以用户的名义完成非法操作。

完成CSRF攻击必须具备的三个条件:用户已经登录了站点A,并在本地记录了cookie;在用户没有登出站点A的情况下(也就是cookie生效的情况下),访问了恶意攻击者提供的引诱危险站点B,B站点发出一个请求站点的一个request请求;站点A没有做任何CSRF防御。

防范CSRF攻击可以遵循的规则:

  • GET请求不对数据进行修改;
  • 不让第三方网站访问用户Cookie;
  • 阻止第三方网站请求接口;
  • 请求附带验证信息,如验证码或者Token;

防御CSRF:

  • Same Site:对Cookie设置Same Site属性,不随着跨域请求发送;
  • Referer Check:通过检查HTTP包头Referer的值是不是这个页面的,来判断是不是CSRF攻击;
  • Anti CSRF Token:比较完善的解决方案,即在发送请求时在HTTP请求总以参数的形式加入一个随机产生的Token,并在服务器建立一个拦截器来验证这个Token,服务器读取浏览器当前域cookie中这个token值,进行校验。
  • 验证码:应用程序和用户交互过程中,特别是账户交易这种核心步骤,强制用户输入验证码,才能完成最终的请求,这样能够很好的遏制CSRF攻击。

(3)XSS

XSS(Cross Site Scripting)跨站脚本攻击,因为缩写与CSS重名,所以叫XSS,是指通过存在安全漏洞的Web网站注册用户的浏览器内运行的HTML标签或JavaSript进行的一种攻击。XSS的原理是恶意攻击者往Web页面里插入恶意可插入恶意可执行网页脚本代码,当用户浏览该页之时,嵌入其中Web里面的脚本代码会被执行,从而可以达到攻击者盗取用户信息或者其他侵犯用户安全隐私的目的。

利用虚假输入表单骗取用户个人信息;利用脚本切取用户的Cookie值,被害者在不知情的情况下,帮助攻击者发送恶意请求;显示伪造的文章或图片。

其中XSS攻击可分为持久型(存储型XSS)和非持久型(反射型XSS),非持久型一般都是通过别人发送带有恶意脚本代码参数的URL,当URL地址被打开时,特有的恶意代码参数被HTML解析、执行;持久型XSS漏洞一般存在于Form表单等交换功能,如文章留言、提交文本信息等。

防御XSS:

  • CSP:本质上就是建立白名单,配置规则告诉浏览器那些资源可以加载和执行,如设置HTTP header中的Content Security Policy: default-src 'self'限制加载本站资源或设置meta标签的方式;
  • 转义字符:用户的输入永远不可信任,对引号、尖括号、斜杠、进行转义;
  • HttpOnly Cookie:预防XSS攻击窃取用户cookie最有效的防御手段,Web应用程序在设置cookie时,将其属性设为HttpOnly,就可以避免该网页的cookie被客户端javaScript窃取,保护用户cookie信息。

(4)点击劫持

点击劫持是一种视觉欺骗的攻击手段。攻击者将需要攻击的网站通过 iframe 嵌套的方式嵌入自己的网页中,并将 iframe 设置为透明,在页面中透出一个按钮诱导用户点击。

点击劫持的原理:用户在登陆A网站的系统后,,被攻击者诱惑打开第三方网站,而第三方网站通过iframe引入了A网站的页面内容,用户在第三方网站中点击某个存在点击劫持的按钮,实际上就是点击了A网站的按钮。

点击劫持的特点:隐蔽性较高,骗取用户操作;“UI-覆盖攻击”;利用iframe或者其它标签的属性。

防御点击劫持:

  • X-FRAME-OPTIONS:DENY表示页面不允许通过iframe的方式展示;SAMEORIGIN表示页面可以在相同域名下通过iframe的方式展示;ALLOW-FROM表示页面可以在指定来源的iframe中展示。
  • JavaScript防御:当通过ifrmae的方式加载页面时,攻击者的网页直接不显示所有内容了。

(5)URL跳转漏洞

URL跳转漏洞是指借助未验证的URL跳转,将应用程序引导到不安全的第三方区域,从而导致的安全问题。

URL跳转漏洞的原理:黑客利用URL跳转漏洞来诱导安全意识低的用户点击,导致用户信息泄露或者资金流失。其原理是黑客构建恶意链接,发送到QQ群、微信群或者贴吧/论坛中,安全意识低的用户点击后,经过服务器或浏览器解析后后,跳转到恶意的网站中,简单来说就是钓鱼网站。

实现方式:Header跳转、JavaScript跳转、meta标签跳转。

防御URL跳转漏洞:

  • Referer的限制
  • 加入有效性验证Token

(6)OS命令攻击注入

OS命令注入和SQL注入差不多,只不过SQL注入是针对数据库的,而OS命令是针对操作系统的。OS命令注入攻击指通过Web应用,执行非法的操作系统命令达到攻击的目的。主要在能调用shell函数的地方就存在被攻击的风险。OS命令注入攻击可以向shell发送命令,让操作系统的命令行启动程序、安装程序等。

rm -rf很可怕!!!

防御OS命令攻击注入:

  • 后端对前端提交的内容进行规则限定,如正则表达式
  • 点调用系统命令前对所有传入参数进行命令行参数转义过滤
  • 不要直接拼接命令语句,最好借助一些工作做拼接、转义处理,如Node.js的shell-escape npm

27. POST和GET的区别

  • GET主要是读取资源,POST主要是改写/新建资源
  • 不可以重复的操作, 比如创建一个条目/修改一条记录, 用POST, 因为POST不能被缓存,所以浏览器不会多次提交。
  • 可以重复的交互,比如取个数据,跳个页面, 用GET。
  • GET请求的URL可以手动输入,请求的URL可以存在书签里,或者历史里,且URL可以被搜索引擎收录。
  • GET是幂等的、只读的、纯粹的操作,而POST是非幂等的操作。

28. python中实现IO多路复用

I/O多路复用指:通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间。

IO多路复用是指内核一旦发现进程指定的一个或者多个IO条件准备读取,它就通知该进程。IO多路复用适用如下场合:

  • (1)当客户处理多个描述字时(一般是交互式输入和网络套接口),必须使用I/O复用;
  • (2)当一个客户同时处理多个套接口时,而这种情况是可能的,但很少出现;
  • (3)如果一个TCP服务器既要处理监听套接口,又要处理已连接套接口,一般也要用到I/O复用;
  • (4)如果一个服务器即要处理TCP,又要处理UDP,一般要使用I/O复用;
  • (5)如果一个服务器要处理多个服务或多个协议,一般要使用I/O复用;

与多进程多线程相比,I/O多路复用技术的最大优势就是系统开销小,系统不必创建进程/线程,也不必维护这些进程/线程,从而大大减少了系统的开销。

29. select,poll和epoll

  • select

通过一个select()系统调用来监视多个文件描述符的数组,当select()返回后,该数组中就绪的文件描述符便会被内核修改标志位,使得进程可以获得这些文件描述符从而进行后续的读写操作。

select优缺点:

  • 优点:目前几乎支持所有平台,优点是良好的跨平台支持;

  • 缺点:

    • 单个进程能够见识的文件描述符存在最大限制,linux一般是1024,不过可以通过修改宏定义甚至重新编译内核的方式提升这一限制;
    • select()说维护的存储大量文件描述符的数据结构,随着文件描述符的数量增大,其复制开销也线性增长;
    • 网络响应时间的延迟使得大量TCP链接处于非活跃状态,但调用select()会对所有socket进行一次线性扫描,所以这也浪费一定的开销。
  • poll

和select在本质上没有多大差别,但是poll没有最大文件描述符数量的限制。

poll的优缺点:

  • 优点:select()和poll()将就绪的文件描述符告诉进程后,不管进程有没有对其进行IO操作,再次调用的时候将会报告这些文件的描述符,就绪信息不会丢失;

  • 缺点:包含大量的文件描述符的数组被整体复制于用户态和内核的地址空间之间,而不论这些文件描述符是否就绪,它的开销随着文件描述符数量的增加而线性增大;

  • epoll

epoll是在2.6内核中提出的,是之前的select和poll的增强版本,同时支持水平触发和边缘触发。相对于select和poll来说,epoll更加灵活,没有描述符限制。epoll使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的copy只需一次。

epoll同样只告知那些就绪的文件描述符,而且当我们调用epoll_wait()获得就绪文件描述符时,返回的不是实际的描述符,而是一个代表就绪描述符数量的值,你只需要去epoll指定的一个数组中依次取得相应数量的文件描述符即可,这里也使用了内存映射(mmap)技术,这样便彻底省掉了这些文件描述符在系统调用时复制的开销。

epoll采用基于事件的就绪通知方式,在select/poll中,进程只有在调用一定的方法后,内核才对所有监视的文件描述符进行扫描,而epoll事先通过epoll_ctl()来注册一个文件描述符,一旦基于某个文件描述符就绪时,内核会采用类似callback的回调机制,迅速激活这个文件描述符,当进程调用epoll_wait()时便得到通知。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# 基于select实现的socket服务端
import socket
import select

# 创建套接字对象
sk = socket.socket()

# 绑定套接字
sk.bind(('127.0.0.1', 9999))

# 将该socket变为服务模式,5表示服务器没有调用accept进行处理的连接个数为最大为5,这个值不能无限大,内核要维护连接队列
sk.listen(5)

# 初始化读取数据的监听列表,最开始从sk这个套接字上读取数据
inputs = [sk, ]
# 初始化写入数据的监听列表,最开始并没有客户端连接进来,所以列表为空
outputs = []
# 保存客户端发送过来的消息记录
messages = {}

while True:
# 调用select监听所有列表中的套接字,将发生变化的套接字加入对应列表
"""
监听可读、可写、异常三类事件
rlist: 被读的socket
wlist: 被写的socket
e: 异常的socket
"""
rlist, wlist, e = select.select(inputs, outputs, [], 2)
print(len(inputs), len(rlist), len(wlist), len(outputs))
for r in rlist:
if r == sk:
conn, addr = r.accept()
conn.sendall(bytes("Hello", encoding="utf-8"))
inputs.append(conn)
messages[conn] = [] # 为客户端创建一个存储接受信息的列表
else:
try:
data = r.recv(1024)
if not data:
raise Exception("断开连接")
else:
outputs.append(r) # 标记谁给我发送过消息
messages[r].append(data)

except Exception as e:
inputs.remove(r) # 移除的客户端,放置下次循环时异常报错
del messages[r]

for w in wlist: # 遍历所有给我发送消息的用户,以此给予回复
msg = messages[w].pop()
resp = msg + bytes('respone', encoding="utf-8")
w.sendall(resp)
outputs.remove(w) # 将回复过信息的用户从列表中移除,放置下次循环重复回复用户
1
2
3
4
5
6
7
8
9
10
11
12
# 基于select实现的socket客户端
import socket

s = socket.socket()
s.connect(('127.0.0.1', 9999))
data = s.recv(1024)
print(data.decode("utf-8"))

while True:
inp = input(">>>")
s.sendall(bytes(inp, encoding="utf-8"))
print(s.recv(1024).decode("utf-8"))

30. python常用的并发网络库

(1)tornado:并发网络库,同时也是一个Web微框架

Tornado 适用于微服务,实现 Restful 接口

  • 底层基于Linux多路复用
  • 可以通过协程或者回调实现异步编程
  • 不过生态不完善,相应的一步框架步入ORM不完善

(2)gevent:绿色线程实现并发,猴子补丁修改内置socket

Gevnet高性能的并发网络库

  • 基于轻量级绿色线程实现并发
  • 需要monkey patch,gevent修改了内置的socket改为非阻塞
  • 配合gunicorn和gevent部署作为wsgi server

(3)asyncio:python3 内置的并发网络库,基于原生的协程

Asyncio基于协程实现的内置并发网络库

  • python3 引用到内置库,协程+事件循坏
  • 生态不够完善,没有大规模的生产环境检验
  • 目前应用不够广泛,基于Aiohttp可以实现一些小的服务

31. 什么是前后端分离,优缺点各是什么

后端只负责提供数据接口,不在渲染模版,前端从接口获取数据并呈现

优点:

  • 实现前后端解耦,接口复用,减少开发量
  • 前后端各司其职,同步开发,提升开发效率
  • 有利于调试、测试与运维部署
  • 有利于快速定位问题,前端问题前端解决,接口数据问题后端解决
  • 大并发的情况下,可以同时水平扩展前后端服务器
  • 减少后端服务器的并发/负载压力,前端http请求在nginx上,后台接口请求调用tomcat
  • 即使后端服务器暂时超时或者宕机了,前端页面也会正常访问,只不过数据显示不出来而已

缺点:

  • 不利于页面SEO优化
  • 前端工作量加大

32. nginx与apache的区别

nginx相对apache的优点:

  • 轻量级,起同样的web服务,比apache占用更少的内存及资源
  • 抗并发,nginx处理请求是异步阻塞的,支持更多的并发连接,而apache则是阻塞型的,在高并发下nginx能保持低资源低消耗但高性能
  • 配置简洁
  • 高度模块化的设计,编写模块相对简单
  • 社区活跃

apache相对nginx的优点:

  • rewrite,比nginx的rewrite更强大
  • 模块超多,支持很多
  • 比nginx的bug少很多
  • 很稳定

33. CGI和WSGI

CGI通用网关接口,是连接web服务器和应用程序的接口,用户通过CGI来获取动态数据或文件等。CGI程序是一个独立的程序,它可以用几乎所有语言来写,包括perl/c/lua/python等。

WSGI(Web Server Gateway interface)是Python应用程序或者框架和Web服务器之间的一种接口,实现Web服务器与Web框架的交互,WSGI的其中一个目的就是让用户可以用统一的语言python编写前后端。

34. RPC

RPC(Remote Procedure Call Protocol)远程过程调用协议,它是一种通过网络远程计算机程序上请求服务,而不需要了解底层网络技术。RPC协议假定某协议传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。

35. SOAP

SOAP(Simple Object Access Protocol)简单对象访问协议,是交换数据的一种协议规范,使用在计算机网络Web服务(web service)中,交换带结构信息。SOAP为了简化网页服务器(Web Server)从XML数据库中提取数据时,节省去格式化页面时间,以及不同应用程序之间按照HTTP通信协议,遵循XML格式执行资料互换,使其抽象于语言实现、平台和硬件。

36. RESTful架构

RESTful架构,就是目前最流行的一种互联网软件架构。它结构清晰、符合标准、易于理解、扩展方便,所以正得到越来越多网站的采用。

REST(Representational State Transfer)表层状态转化,表现层其实指的是资源(Resources)的表现层,资源就是网络上一个实体或者一个具体信息,如一张图片,用特定的统一资源定位符(URI)指定;表现层(Representation)是指把资源呈现出来的形式,如文本可以用txt格式、html格式、xml格式、json格式、二进制格式等来表现;状态转化(State Transfer)客户端通过四个HTTP动词,对服务器端资源进行操作,实现表现层状态转化,如GET获取资源、POST新建更新资源、PUT用来更新资源、DELETE用来删除资源等。

RESTful架构一些典型的设计误区

  • 最常见的一种设计错误就是URI包含动词,资源一种实体,所以应该名词。
  • 另一个设计误区,就是URI中加入版本号,版本号可以再HTTP请求头信息的Accept字段中进行区分。

37. 幂等

HTTP方法的幂等性是指一次和多次请求某一个资源应该具有同样的副作用。

GET http://www.bank.com/account/123456,不会改变资源的状态,不论调用一次还是N次都没有副作用。请注意,这里强调的是一次和N次具有相同的副作用,而不是每次GET的结果相同。GET http://www.news.com/latest-news这个HTTP请求可能会每次请求得到不同的结果,但它本身没有产生任何副作用,因而是满足幂等性的。

DELETE方法用于删除资源,有副作用,但它应该满足幂等性。比如DELETE http://forrum.com/article/4231,调用一次和N次对系统产生的副作用是相同的,即删掉id为4231的帖子;因此,调用者可以多次调用或者刷新页面而不用担心引起错误。

POST所对应的URI并非创建的资源本身,而是资源的接受者。比如POST http://www.forum.com/articles的语义是在http://www.forum.com/articles下创建一篇文章,HTTP响应中包含帖子的创建状态以及帖子的URI。两次相同的POST请求会在服务器端创建两份资源,它们具有不同的URI;所以,POST方法不具备幂等性。

PUT方法所对应的URI是要创建或更新的资源本身。比如PUT http://www.forum/articles/4231的语义是创建或者更新ID为4231的帖子。对同一URI进行多次PUT的副作用和一次PUT是相同的,因此PUT方法具备幂等性。

38. 为什么要有TIME_WAIT这个状态

为什么主动关闭的一端不直接进入closed状态,而是要先进入time_wait并且停留两倍的MSL时长呢?这是因为TCP是建立在不可靠网络上的可靠协议。如果主动关闭的一端收到被动关闭一端的发出的FIN包后,返回ACK包,同时进入TIME_WAIT,但是由于网络的原因,主动关闭一端发送的ACK包可能会延迟,从而触发被动关闭一方重传FIN包,这样一来一回极端情况正好是2MSL。如果主动关闭的一端直接close或者不到两倍MSL时间就关闭,那么被动关闭发出重传FIN包到达,可能出现的问题是:旧的连接不存在,系统只能返回RST包;新的TCP连接已经建立,延迟包可能会干扰新连接。这都可能导致TCP不可靠。(所以需要旧的连接一直保持time_wait 2MSL时间)

Django

1. Django

Django 是一个由 Python 编写的一个开放源代码的 Web 应用框架。使用 Django,只要很少的代码,Python 的程序开发人员就可以轻松地完成一个正式网站所需要的大部分内容,并进一步开发出全功能的 Web 服务 Django 本身基于 MVC 模型,即 Model(模型)+ View(视图)+ Controller(控制器)设计模式,MVC 模式使后续对程序的修改和扩展简化,并且使程序某一部分的重复利用成为可能。

Django的优点:

  • 自带ORM,强大的数据库功能
  • 自带强大的后台功能
  • 优雅的地址匹配
  • 易扩展的、简易且样式分离的模版系统
  • 与memcached等缓存系统联用表现出色,加载速度快
  • MVC模式,低耦合、开发快捷、部署方便、可重用性高

常用的一些命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 创建项目
django-admin startproject xxx

# 项目根目录下运行服务器,通过浏览器访问http://127.0.0.1:8000/查看
python manage.py runserver

# 创建应用程序app,创建完成需要在setting.py文件中INSTALLED_APPS中添加app名称
python manage.py startapp xxx

# 启动数据库
python manage.py makemigrations

# 数据迁移
python manage.py migrate

# 创建超级用户
python manage.py createsuperuser

# 生成对应的ORM表结构
python manage.py inspectdb

2. Django请求生命周期

一般是用户通过浏览器向服务器发送一个请求(request),这个骑牛会去访问视图函数,如果不涉及到数据调用,那么这个时候视图函数返回一个模版也就是一个网页给用户,视图函数调用Model,模型去数据库查找数据,然后逐级返回,视图函数把返回的数据渲染到模版中,最后返回网页给用户。

大致步骤:

  • (1)wsgi请求封装后交给Web框架
  • (2)中间件,对请求进行校验或在请求中添加其他相关数据,如csrf/request.session
  • (3)路由匹配,根据浏览器发送的不同url去匹配不同的视图函数
  • (4)视图函数,在视图函数中进行业务逻辑的处理,orm–>templates–>渲染
  • (5)中间件,对相应的数据进行处理
  • (6)wsgi将响应的内容发送给浏览器

3. MVC模式与MVT模式

  • MVC

MVC(Model-View-Controller)即模型-视图-控制器,它们之间以一种插件式的、松耦合的方式连接在一起。其中,Model负责业务对象与数据库的映射(ORM);View负责与用户页面交互;Controller接受用户输入调用模型和视图完成用户的请求。

  • MVT

MVT(Model-View-Template)即模型-视图-模版,本质上与MVC是一样的,为了各组件之间保持松耦合关系。其中,Model负责业务对象与数据库的映射(ORM);View负责业务逻辑并在适当的时候调用Model和Template;Template负责如何把页面展示给用户(即html)。

4. FBV和CBV

  • FBV(Function Base View):就是在视图里面使用函数处理请求
  • CBV(Class Base View):就是在视图里面使用类处理请求

5. ORM的理解

ORM(Object Relational Mapping)即对象-关系-映射,是MVC或MVT框架中的一个重要部分,用于实现业务对象数据模型与数据表中字段映射,数据模型的设计不需要依赖于特定的数据库,通过简单的配置就可以轻松更换数据库,极大减少了开发人员的工作量。常用的ORM库有sqlalchemy、django ORM。

6. 中间件的作用

中间件是一个用于处理Django的请求和响应的框架级别的钩子,每个中间件都负责做一些特定的功能。

中间件是介于request与response处理之间的一道处理过程,相对比较轻量级,并在全局范围内改变Django的输入和输出。

7. 谈谈restful规范的认识

首先restful是一种软件架构风格或者是一种设计风格,并不是标准,它只是提供了一种设计原则和约束条件,主要用于客户端和服务器交互类软件。就像设计模式一样,并不是一定要遵循这个原则,而是基于这个风格设计的软件可以更加简洁,层次更清晰。

一些常见的规范:

  • restful提倡面向资源编程,在URL接口中尽量要使用名词,不要使用动词
  • 在URL接口中推荐使用HTTPS协议,让网络接口更加安全
  • 在URL中可以体现版本号
  • URL中可以体现是否是API接口
  • URL中可以添加添加条件去筛选匹配
  • 可以根据HTTP不同的method,进行不同的资源操作
  • 响应式应该设置状态码
  • 有返回值值,而且格式为统一的json格式
  • 返回值错误信息
  • 返回结果中要提供帮助链接,即api最好做到Hypermedia

8. rest_framework框架中有哪些组件

  • 序列化组件:serializers 对queryset序列化以及对请求数据格式化校验
  • 路由组件:routers 进行路由分发
  • 视图组件:ModelViewSet 帮助开发者提供一些类和方法
  • 认证组件:写一个类并注册到认证类(authentication_classes),在类的authticate方法中编写认证逻辑
  • 权限组件:写一个类并注册到权限类(permission_classes),在类的has_permission方法中编写权限逻辑
  • 频率限制组件:写一个类并注册到频率类(throttle_classes),在类的allow_request/wait方法中编写频率限制逻辑
  • 解释器:选择多数据解析的类,在解析器类中注册(parser_classes)
  • 渲染器:定义数据如何渲染到页面上,在渲染器上注册(renderer_classes)
  • 分页:对获取的数据进行分页处理,pagination_class
  • 版本:版本控制用来在不同的客户端使用不同的行为

9. rest_framework框架认证流程

Django rest framework源码分析(1)—-认证

基于CBV使用rest_framework框架,基于反射实现根据请求方式不同,执行不同的方法。原理:url–>view方法–>dispatch方法(反射执行其他方法:GET/POST/PUT/DELETE等)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# 1、在settings的app中添加rest_framework
INSTALLED_APPS = {
'rest_framework',
}

# 配置url
from django.contrib import admin
from django.urls import path
fromm API.views import AuthView

urlpatterns = [
path('admin/', admin.site.urls),
path('api/v1/auth/', AuthView.as_view()),
]

# models,一个保存用户的信息,一个保存用户登录成功后的token
from django.db import models

class UserInfo(models.Model):
USER_TYPE= (
(1, '普通用户'),
(2, 'VIP'),
(3, 'SVIP')
)

user_type = models.IntegerField(choices=USER_TYPE)
username = models.CharField(max_length=32)
password = models.CharField(max_length=64)

class UserToken(models.Model):
user = models.OneToOneField(UserInfo, on_delete=models.CASCADE)
token = models.CharField(max_length=64)

# 用户登录的views
from django.shortcuts import render
from django.http import JsonResponse
from rest_framework.views import APIView # rest_framework框架,继承View
from API import models
import hashlib
import time


def md5(user):
# 当前时间,相当于生成一个随机的字符串
ctime = str(time.time())
m = hashlib.md5(bytes(user, encoding="utf-8"))
m.update(bytes(ctime, encoding="utf-8"))
return m.hexdigest()

class AuthView(object):
# 用于用户登录验证
def post(self, request, *args, **kwargs):
ret = ('code': 1000, 'msg': None)
try:
user = request._request.POST.get('username')
pwd = request._request.POST.get('password')
obj = models.UserInfo.objects.filter(username=user, password=pwd).first()
if not obj:
ret['code'] = 1001
ret['msg'] = '用户名或密码错误'
# 为用户创建token
token = md5(user)
# 存在就更新,不存在就创建
models.UserToken.objects.update_or_create(user=obj, default={'token': token})
ret['token'] = token

except Exception as e:
ret['code'] = 1002
ret['msg'] = '请求异常'
return JsonResponse(ret)

# 添加一个认证类
from rest_framework.request import Request
from rest_framework import exceptions # 验证失败触发异常
from rest_framework.authentication import BasicAuthentication

ORDER_DICT = {
1:{
'name': 'apple',
'price': 15
},
2:{
'name': 'dog',
'price': 100
}
}

class Authentication(APIView):
# 认证
def authenticate(self, request):
token = request._request.GET.get('token')
token_obj = models.UserToken.objects.filter(token=token).first()
if not token_obj:
raise exceptions.AuthenticationFailed('用户认证失败')
# 在rest_framework内部将会这两个字段赋值给request,以供后续操作使用
return (token_obj.user, token_obj)

def suthenticate_header(self, request):
pass

class OrderView(APIView):
# 订单相关业务
authentication_classes = [Authentication,] # 添加认证
def get(self, request, *args, **kwargs):
ret = {'code': 1000, 'msg': None, 'data': None}
try:
ret['data'] = ORDER_DICT
except Exception as e:
pass
return JsonRespone(ret)

def post(self, request, *args, **kwargs):
return HttpResponse("post")

def put(self, request, *args, **kwargs):
return HttpResponse("put")

def delete(self, request, *args, **kwargs):
return HttpResponse("delete")

drf的认证可研究下源码,BaseAuthentication源码。

自己写认证类方法:

  • 创建认证类:继承BaseAuthentication类,重写authenticate方法,authenticate_header方法直接写pass即可。
  • authenticate()返回值:None表示当前认证不管等下一个认证执行;raise exceptions.AuthenticationFailed('用户认证失败');返回值是元组形式。
  • 局部使用:authentication_classes=[BaseAuthentication,]
  • 全局使用:REST_FRAMEWORK = {"DEFAULT_AUTHENTICATION_CLASSES": ['API.utils.auth.Authentication',]}

10. rest_framework实现用户访问频率控制

from rest_framework.throttling import SimpleRateThrottle

这里使用字节流类是继承了SimpleRateThrottle类,而这个类利用了django内置的缓存来存储访问记录。通过全局节流设置,所以的视图默认是使用UserThrottle类进行节流,如果不想使用默认的类就自定义给throttle_classes属性变量赋值,如throttle_classes=[VisitThrottle]

11. 路由匹配的原则

URLconf配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from django.conf.urls import url

urlpatterns = [
url(正则表达式, views视图, 参数, 别名),
]

"""
正则表达式:一个正则表达式字符串
views视图函数:一个可调用对象,通常为一个视图函数或者一个指定视图函数路径的字符串
参数:可循啊的要传递给视图函数的默认参数(字典形式)
别名:一个可选的name参数
"""

# Django2.0 路由系统的写法
from django.urls import path

urlpatterns = [
path('articles/2003/', views.special_case_2003),
path('articles/<int:year>/', views.year_archive),
path('articles/<int:year>/<int:month>/', views.month_archive),
path('articles/<int:year>/<int:month>/<slug:slug>/', views.article_detail),
]

关于正则匹配优先级

  • 在url匹配列表中,如果第一条和第二条同时满足匹配规则,则优先匹配第一条
  • 在url匹配列表中,如果第一条为正则模糊匹配,第二条为精确匹配,则优先匹配第一条
1
2
3
4
5
6
7
8
9
10
from django.conf.urls import url
from django.contrib import admin
from app01 import views

urlpatterns = [
url(r'^blog/2017/12/', views.blog_list),
url(r'^blog/(\d{4})/([0-5]{2})/', views.blog_list),
url(r'^blog/(\d{4})/[7-9]/$', views.blog_list),
url(r'^admin/', admin.site.urls),
]

12. 路由系统中include的作用

include作用是路由转发,通常在每个app里,各自创建一个urls.py的路由模块,然后从根路由触发,将app所属的url请求,全部转发到相应的urls.py模块中。

13. Django默认自动在网址结尾添加’/‘

Django settings.py配置文件中,APPEND_SLASH = True表示开启URL地址后面自动添加’/‘。

14. urlpatterns中的name与namespace的作用

  • name:别名,给路由取一个别名
  • namespace:名称空间,防止多个应用之间的路由重复

15. 如何根据urlpatterns中的name方向生成url,这样反向生成url的方式有几种

(1)在url 中配置 redirect_to 或者 RedirectView

1
2
3
4
5
6
7
8
9
10
11
12
# 配置 redirect_to
from django.views.generic.simple import redirect_to

urlpatterns = patterns('',
(r'^one/$', redirect_to, {'url': '/another/'}),
)

# 配置 RedirectView
from django.views.generic import RedirectView
urlpatterns = patterns('',
(r'^one/$', RedirectView.as_view(url='/another/')),
)

(2)在view 中 通过 HttpResponseRedirect 实现 redirect,状态码是301、302

1
2
3
4
5
from django.http import HttpResponseRedirect

def myview(request):
...
return HttpResponseRedirect("/path/")

(3)利用django的 redirect app实现,主要是RedirectFallbackMiddleware来完成,如果django发现404错误,就会去django_redirect去匹配URL,若匹配成功且存在新的URL,则自动转向URL;若新URL为空,返回410;若匹配不成功,返回原来的错误。

  • 在settings.py中增加django.contrib.redirects到你的INSTALLED_APPS 设置。
  • MIDDLEWARE_CLASSES中添加django.contrib.middleware.RedirectFallbackMiddleware
  • 运行manage.py syncdb 创建django_redirect这个表,包含了site_idold_pathnew_path字段。

16. WSGI/uwsgi/uWSGI

WSGI(Web Server Gateway interface)Web服务器网关接口,不是服务器也不是API之类的,只是一种描述web服务器(如nginx、uWSGI服务器)如何与web应用程序通信的规范。

uwsgi与WSGI一样是uWSGI服务器的独占通信协议,用于定义传输信息的类型,每个uwsgi packet 前 4 bytes为传输信息类型的描述,与WSGI协议完全是两种东西。

uWSGI是一个全功能的HTTP服务器,实现了WSGI协议、uwsgi协议、HTTP协议等,目的就是把HTTP协议转化为语言支持的网络协议,如把HTTP协议转化为WSGI协议,让python直接使用。

17. 简述Django对HTTP请求的执行流程

Django自带的开发者服务器或者uWSGI服务器,WSGI网关协议的服务器监听端口等待外界HTTP请求,服务器根据WSGI协议从HTTP中提取出必要的参数组成一个字段传入Handler中进行处理分析,加载中间件、路由分配、调用路由匹配的视图、完成数据请求或处理,最后返回一个可以被浏览器解析的符合HTTP协议的HTTPResponse。

18. Django中的跨域问题如何处理

django解决跨域请求的问题

跨域是指一个域下面的文档或脚本试图去请求另一个与下的资源。当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同时,就会出现跨域。

解决跨域问题:

  • (1)安装第三方扩展:pip install django-cors-headers
  • (2)添加应用
1
2
3
INSTALL_APPS = (
'corsheaders',
)
  • (3)添加中间件,第一条
1
2
3
MIDDLEWARE = [
'corsheaders.middleware.CorMiddleware',
]
  • (4)配置访问规则或者白名单
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 允许所有域名跨域(优先选择)
CORS_ORIGIN_ALLOW_ALL = True

# 配置白名单
CORS_ORIGIN_WHITELIST = (
'*'
# '127.0.0.1:8000', # ip地址:端口
# 'localhost:8000',
# 'www.baidu.com:80',
)

# 配置方法
CORS_ALLOW_METHODS = (
'GET',
'POST',
'DELETE',
'PUT',
'OPTIONS',
'PATCH',
'VIEW',
)

# 配置headers
CORS_ALLOW_HEADERS = (
'XMLHttpRequest',
'X_FILENAME',
'accept-encoding',
'authorization',
'content-type',
'dnt',
'origin',
'user-agent',
'x-csrftoken',
'x-request-with',
'Pragma',
)
  • settings.py设置允许携带cookie:CORS_ALLOW_CREDENTIALS = True,如果配置了nginx,则
1
2
3
4
5
6
7
# 进入nginx.conf
vim /usr/local/nginx/conf/nginx.conf

# 修改配置
location / {
proxy_pass 127.0.0.1:8000/;
}

19. Django中CSRF的实现机制

  • (1)django第一次响应来自某个客户端的请求时,后端随机产生一个tocken值,把这个token保存在session状态中;同时,后端把这个token放到cookie中交给前端页面。
  • (2)下次前端需要发起请求的时候,把这个token值加入到请求数据或者头信息中,一起穿个后端Cookies: {csrftoken:xxxxx}
  • (3)后端校验前端请求带过来的token和session里的token是否一致。

20. Django的Model的继承有几种形式

抽象基类继承(Abstract base classes): 用父model保存那些重复录入的信息,父类是不使用的也就是不生成单独的数据表。

多重表继承(Multi-table inheritance):从现有的Model继承并让每个Model都有自己的数据表

代理model(Proxy models):只想model中修改python-level级的行为,而不涉及字段改变。

21. Django中如何加载初始化数据

Django在创建对象时,在调用save()方法后,ORM框架会把对象的属性转为写入到数据库中,实现对数据库的初始化;通过操作对象,查询数据库,将查询集返回给视图函数,通过模版展现在前端页面。

22. Django如何批量创建数据

使用django.db.models.query.QuertSet.bulk_create()批量创建对象,为了减少SQL查询次数,改进如下:

1
2
3
4
querysetlist = []
for i in resultlist:
querysetlist.append(Account(name=i))
Account.objects.bulk_creat(querysetlist)

23. ORM取消级联

Django取消练级删除,只有在null=True时,才可以使用。
user = models.ForeignKey(User, blank=True, null=True, on_delete=models.SET_NULL

24. QueryDict与dict的区别

QueryDict类似字典的自定义类,用来处理单键对应多值的情况。在HttpResponse对象中,GET和POST属性是django.http.QueryDict类的实例。

而python原生的dict是不允许出现一键多值,只能保留一个。但在HTML表单中,通常会发生一键多值的情况,如多选框就是常见情况。

25. 查询集的两大特性

  • 惰性执行:创建查询集不会访问数据库,直到调用数据时才访问数据库
  • 缓存:

26. 查询集返回的列表过滤器有哪些

  • all():返回所有数据
  • filter():返回满足条件的数据
  • exclude():返回满足条件之外的数据,等同于sql中的where...not
  • order_by():排序

27. django中写原生sql

  • 使用extra
1
Book.objects.filter(pub_name='人民邮电出版社').extra(where=['price>50'])
  • 使用raw
1
2
3
books=Book.objects.raw('select * from e_book')
for book in books:
print(book)
  • 自定义sql
1
2
3
4
5
6
7
8
9
from django.db import connection

cursor = connection.cursor()
cursor.excute("insert into e_book(name)VALUES('山海经')")
cursor.excute("update e_book set name='流畅的python' WHERE name='山海经'")
cursor.excute("delete from e_book where name='山海经'")
cursor.excute("select * from e_book")
cursor.fetchone()
cursor.fetchall()

28. Django中查询queryset时,什么情况下用F和Q

F:对数据本身的不同字段进行操作,如比较、更新,对数据进行加减操作等。

Q:用于构造复杂的查询条件,如’&’、’|’等操作。

29. DateTimeField类型中auto_now与auto_now_add的区别

  • DateTimeField.auto_now

这个参数的默认值为false,设置为true时,能够在保存该字段时,将其值设置为当前时间,并且每次修改model,都会自动更新。这个参数在需要存储“最后修改时间”的场景下,设置该参数为true时,并不简单地意味着字段的默认值为当前时间,而是指字段会被“强制”更新到当前时间,你无法程序中手动为字段赋值,在admin中字段也会成为只读的。

  • DateTimeField.auto_now_add

这个参数的默认值也为False,设置为True时,会在model对象第一次被创建时,将字段的值设置为创建时的时间,以后修改对象时,字段的值不会再更新。该属性通常被用在存储“创建时间”的场景下,一旦被设置为True,就无法在程序中手动为字段赋值,在admin中字段也会成为只读的。

30. values()与values_list()的区别

  • values():取字典的queryset
  • values_list():取元组的queryset

31. selected_related与prefetch_related的区别

  • select_related:适用于外键和多对一的关系查询
  • prefetch_related:适用于一对多或者多对多查询

32. class Meta中的元素信息字段有哪些

  • app_label: 模型类不在默认的应用程序包下的models.py文件中,需要指定应用程序。
  • db_table:用于指定自定义数据库表名的,若不提供,默认会用app_label + '_' + module_name作为表名。
  • db_tablespace:指定这个模型对应的数据库表放在哪个数据库表空间。
  • get_lastest_by:根据哪个字段获取到最新的对象。
  • managed:Django会自动根据模型类生成映射的数据库表,managed=False则不会对数据表进行migrate/migrations、删除等操作。
  • order_with_respect_to:一般用于多对多的关系中,指向一个关联对象并排序。
  • ordering:对象返回的记录结果集是按照哪个字段排序的,ordering=['-order_date', 'author'],其中-表示降序,?表示随机,多个元素按从左至右排序。
  • permissions:Admin管理模块下,设置指定方法权限。
  • unique_together:通过两个字段保持唯一性时使用,unique_together = (("first_name", "last_name"),)
  • verbose_name:取别名,verbose_name = "hello"
  • verbose_name_plural:指定模型的复数形式,verose_name_plural="stories

33. django中多对多关系如何手动生成中间关联表

1
tags = models.ManyToManyField(to="Tag", through="Article2Tag", through_fields=('article', 'tag'),)

34. Cookie与Session

  • Cookie:会话跟踪技术,是由服务器创建的,然后通过响应发送给客户端的一个键值对。具体一个浏览器对一个服务器存储的键值对key-value。
1
2
3
4
5
# 获取cookie
request.COOKIES.get("is_login")

# 设置cookie
response.set_cookie("is_login", True)
  • Session:是服务端技术,服务器在运行时可以为每一个用户的浏览器创建一个独享的session对象,用户在访问服务器的web资源时,可以把各自的数据放在各自的session中,当用户再次访问该服务器的其他web资源师,其他web资源再从用户各自的session中去除数据为用户服务。
1
2
3
4
5
6
7
8
# 获取session
request.session["is_login"] = True

# 设置session
is_login = request.session.get("is_login")

# 清空session
request.session.flush()

35. django中如何实现单元测试

django的单元测试使用python的unittest模块,这个模块使用基于类的方法来定义测试。

36. django的缓存机制

Django根据设置的缓存反射式,浏览器第一次请求时,cache会缓存单个变量或者珍格格网页等内容到硬盘或者内存中,同时设置response头部,当浏览器再次发起请求时,附带f-Modified-Since请求时间到Django,django发现f-Modified-Since会先去参数之后,会与缓存中的过期时间相比较,如果缓存时间比较新,则会重庆请求数据,并缓存起来,然后返回response给客户端,如果珲春没有过期,则直接从缓存中提取数据,返回response给客户端。

37. 缓存系统类型

  • 全站缓存
1
2
3
4
5
MIDDLEWARE_CLASSES = (
'django.middleware.cache.UpdateCacheMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleaware.cache.FetchFromCacheMiddleware',
)
  • 视图缓存
1
2
3
4
5
6
7
from django.views.decorators.cache import cache_page
import time

@cache_page(15) # 超时时间为15秒
def index(request):
t = time.time() # 获取当前时间
return render(request, 'index.html', locals())
  • 模版缓存
1
2
3
4
5
6
{% load cache %}
<h3 style="color: green">不缓存:-----{{ t }}</h3>

{% cache 2 'name' %} # 缓存的key
<h3>缓存:-----:{{ t }}<h3>
{% endcache %}

38. Django中的runserver不用用来部署

runserver方法是调试Django时经常用到的运行方式,它使用Django自带的WSGI Server运行,主要在测试和开发中使用,并且runserver开启的方式也是单进程。

uWSGI是一个Web服务器,实现了WSGI协议、uwsgi、http等协议,具有超快的性能、低内存占用和多app管理等优点,并且搭配着Nginx就是一个生产环境,能够将用户访问请求与应用app隔离开,实现真正的部署。相比来讲,支持并发量更高,方便管理多进程,发挥多核的优势,提升性能。

Flask

Flask作为Web框架,它的主要作用是为了开发Web应用程序,Web应用程序诞生最初的目的是为了利用互联网交流工作文档。

微框架, 插件机制, 比较灵活, 过于灵活就导致代码结构太自由, 不利于工程维护。

一切从客户端发起请求开始:

  • 所有Flask程序都必须创建一个程序实例
  • 当客户端想要获取资源时,一般会通过浏览器发起HTTP请求
  • 此时Web服务器适应WSGI协议,把来自于客户端的请求都交给程序实例
  • Flask使用Werkzeug来做路由分发(URL请求和视图函数之间的对应关系)。根据每个URL请求,找到具体的视图函数
  • 在Flask程序中路由一般通过程序实例的装饰器实现,通过电泳视图函数,获取数据后,把数据传入HTML模版文件中,模板引擎负责渲染HTTP响应数据,然后由Flash返回响应数据给浏览器,最后浏览器显示返回的结果。

Flask框架的核心就是Werkzeug和jinja2。

Tornado

Tornado源意为龙卷风,这里是一款可扩展的,非阻塞的Web服务器,应用开发框架,以及异步联网库,但生态还不够完善。Tornado是高度灵活,专门设计为开发人员和第三方工具厂商提供了一个开放环境。已有部分应用程序接口可以利用并附带参考书目,内容从开发环境接口到连接实现。另外,它还配备了一个WSGI服务器,其他WSGI Python应用程序(和框架)也可以使用。

Twisted

Twisted是一款事件驱动的网络编程框架,支持许多常见的传输及应用层协议,如TCP、UDP、SSL/TLS、HTTP、IMAP、SSH、IRC以及FTP。还支持Unix domain sockets,在MIT许可下应用。

Twisted附带了一个可以web服务器上运行的WSGI,它能够为其他Python web应用程序允许开发人员使用的服务器定制HTTP服务。

jQuery

Vue

AngularJS

React

  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.
  • Copyrights © 2019-2020 holysll
  • Visitors: | Views: