python面试大全-网络编程

1,TCP/IP协议(族)

TCP/IP是一类协议系统,它是用于网络通信的一套协议集合# 为什么要分层: (图解HTTP中第24页) “把 TCP/IP 层次化是有好处的。比如,如果互联网只由一个协议统筹,某个地方需要改变设计时,就必须把所有部分整体替换掉。而分层之后只需把变动的层替换掉即可。把各层之间的接口部分规划好之后,每个层次内部的设计就能够自由改动了。 值得一提的是,层次化之后,设计也变得相对简单了。处于应用层上的应用可以只考虑分派给自己的任务,而不需要弄清对方在地球上哪个地方、对方的传输路线是怎样的、是否能确保传输送达等问题。”# TCP/IP 主要分4层 最上面开始 1) 应用层:HTTP(超文本传输协议) FIP(文件传输协议) SMTP(邮件) DNS TELNET(远程登录) DNS(名字转换) HTTP协议是TCP/IP协议里面包含的应用层的协议而已,没有这个协议只是不能浏览网页而已,2) 传输层: 端到端控制 TCP(传输控制协议) UDP(用户数据报协议)UGP SPX3) 网络层(又称网络互连层): 寻址 IP ICMP IGMP “网络层用来处理在网络上流动的数据包。数据包是网络传输的最小数据单位。该层规定了通过怎样的路径(所谓的传输路线)到达对方计算机,并把数据包传送给对方。与对方计算机之间通过多台计算机或网络设备进行传输时,网络层所起的作用就是在众多的选项内选择一条传输路线。”4) 链路层(又称网络接口层): ARP RARP; 负责接收IP数据包并通过网络发送之,或者从网络上接收物理帧,抽出IP数据报,交给IP层。

2,TCP/IP 跟 HTTP的区别
  • TCP/IP是网络上数据通信的一种协议,如果没有这个协议,你就在TCP/IP 网络上 什么也做不了
  • HTTP协议是TCP/IP 协议里面包含的 应用层的协议而已,没有这个协议 只是 不能浏览网页而已,其他的服务是正常的 比如 邮件SMTP,POP3 协议,FTP等协议,这些协议都是 应用层协议
3,UDP / TCP 区别和联系(传输层)
# UDP:用户数据报协议 把数据打包数据大小有限制(64K) 无连接 速度快,但是可靠性低(丢包) 中文名是用户数据报协议,是一个简单的面向数据报的运输层协议,在网络中用于处理数据包,是一种无连接的协议优点:UDP速度快、操作简单、需要求系统资源较少,由于通讯不需要连接,可以实现广播发送。# 缺点:不可靠,不稳 UDP传送数据前并不与对方建立连接,对接收到的数据也不发送确认信号,发送端不知道数据是否会正确接收,也不用重发,不可靠。 # 应用场景 QQ语音 QQ视频# TCP: 传输控制协议 1.建立连接通道,数据大小无限制, 速度慢 但是可靠性高 2.TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。 3.TCP是面向连接的通信协议,通过三次握手建立连接,通讯完成时要四次挥手断开链接(创建链接 数据传输 关闭链接)优点:TCP在数据传递时,有确认、窗口、重传、拥塞控制机制,能保证数据正确性,较为可靠。 TCP采用发送应答机制/ 超时重传/错误效验/流量控制和阻塞管理# 缺点:慢,效率低,占用系统资源高,易被攻击 TCP在传递数据之前,要先建连接,这会消耗时间,而且在数据传递时,确认机制、重传机制、拥塞控制机制等都会消耗大量的时间,而且要在每台设备上维护所有的传输连接,事实上,每个连接都会占用系统的CPU、内存等硬件资源。 而且,因为TCP有确认机制、三次握手机制,这些也导致TCP容易被人利用,实现DOS、DDOS、CC等攻击。 # 4. 应用场景 浏览器,用的HTTP FlashFXP,用的FTP Outlook,用的POP、SMTP Putty,用的Telnet、SSH QQ文件传输

4,项目是TCP还是UDP写的 参考链接: https://blog.csdn.net/yjxsdzx/article/details/71937886
5,应用层===> HTTP(端口80) HTTPS(端口443)
# 运行在TCP协议上的协议: HTTP(Hypertext Transfer Protocol,超文本传输协议),主要用于普通浏览。 HTTPS(Hypertext Transfer Protocol over Secure Socket Layer, or HTTP over SSL,安全超文本传输协议),HTTP协议的安全版本。 FTP(File Transfer Protocol,文件传输协议),由名知义,用于文件传输。 POP3(Post Office Protocol, version 3,邮局协议),收邮件用。 SMTP(Simple Mail Transfer Protocol,简单邮件传输协议),用来发送电子邮件。 TELNET(Teletype over the Network,网络电传),通过一个终端(terminal)登陆到网络。 SSH(Secure Shell,用于替代安全性差的TELNET),用于加密安全登陆用。# 运行在UDP协议上的协议: BOOTP(Boot Protocol,启动协议),应用于无盘设备。 NTP(Network Time Protocol,网络时间协议),用于网络同步。 DHCP(Dynamic Host Configuration Protocol,动态主机配置协议),动态配置IP地址。

6,请求报文 响应报文
# 什么是HTTP协议? HTTP 是 hypertext transfer protocol(超文本传输协议)的简写, HTTP是一个属于应用层的面向对象的协议 它是 TCP/IP 协议的一个应用层 协议,用于定义 WEB 浏览器与 WEB 服务器之间交换数据的过程。客户端连上 web 服务器后,若想获 得 web 服务器中的某个 web 资源,需遵守一定的通讯格式,HTTP 协议用于定义客户端与 web 服务器 通迅的格式。# HTTP协议的主要特点 1.内容格式: 消息头和消息体 2.通信流程: http协议每次响应完成后,会断开与客户端的连接 / 无状态 3.灵活:HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记 4.无连接 5.无状态- 超文本传输协议 - HTTP协议通常承载于TCP协议之上,有时也承载于TLS或SSL协议层之上,这个时候,就成了我们常说的HTTPS - HTTP 1.1 跟HTTP 1.0版本 - HTTP 在TCP的基础上封装 - HTTP 协议是一种无状态协议 - 客户端打开网页 再关闭 再打开, 服务器不知道客户关闭了浏览器 - 无状态是指协议对于事务处理没有记忆能力,服务器不知道客户端是什么状态 - 无状态不代表HTTP不能保持TCP链接,更不能代表HTTP使用的是UDP协议(无连接)- HTML 是一种用来定义网页的文本,会HTML就可以编写网页 - HTTP 是用来在网络上传输HTML文本的协议,用于浏览器和服务器间的通信# <请求报文分析> 1.请求行: 请求方法(GET POST) 请求路径URL HTTP协议版本; 例如:POST /form/entryHTTP/1.1 \r\n 2.请求头: 3.空行: 必须是空行, 通知服务器以下不再有请求头例如: \r\n 4.请求包体: 只在POST方法中使用/get请求没有请求体# 常见的请求头 1.Accept,浏览器端能够处理的内容类型; 接收的文件类型 2.Accept-Encoding, 浏览器能够处理的的压缩编码。通常指定压缩方法 3.Accept-Language, 浏览器当前设置的语言 4.Accept_Charset::浏览器能够显示的字符集 5.Connection: keep-aliveTcp链接 长链接(HTTP/1.1使用Keep-Alive为默认值) 6.Host,发送请求的页面的域名主机和端口号 7.Referer,告诉服务器我是从哪个页面链接过来的; 用于做防盗链 8.User-Agent, 浏览器的用户代理字符串; 浏览器名称(重点) 告诉服务器我是谁,我是哪个客户端访问你的 9.Cookie,用来存储一些用户信息以便让服务器辨别用户身份的; 我是哪个用户 10.Cache-Control,指明当前资源的有效期 11.Cache-Control对缓存进行控制 12.Connection-Length: 16 13.X-Requested-With: XMLHttpRequest 是Ajax异步请求 14.Content-Type 用来表名request的内容类型# <响应报文分析> 响应包体就是网页显示的内容 1.状态行: HTTP协议版本字段, 状态码, 状态码描述 2.响应头: Location Server Vary Connection 3.空行: 通知服务器以下不再有响应头部 4.响应包体: 服务器返回给客户端的文本信息# 常见的响应头 1.Cache-Control ,通知从服务器到客户端内的所有缓存机制,表示它们是否可以缓存这个对象及缓存有效时间。其单位为秒 2.Content-Type, 当前内容的MIME类型 3.Set-Cookie设置HTTP cookie; 服务器设置cookie到客户端 4.Server服务器的名称 5.Content-Encoding 文档的编码方法 6.Content-Length表示内容长度 7.Date:表示消息发送的时间,时间的描述格式由 rfc822 定义 8.last-modified: 最后修改时间,增量爬虫( 这个数据更新了,我也对这个数据更新) = 了解

7,http1.1版本新特性
1.持久连接是HTTP1.1的默认连接方式 请求头部 connection: keep-alive 只要任意一端没有明确提出断开连接,则保持TCP连接状态。2.块编码 确保浏览器和服务器接收到完整的消息 使用一个特别的头部transfer-encoding来表示有多少以块形式的字节流将会被发送3.状态码100 客户端应当继续发送请求。这个临时响应是用来通知客户端它的部分请求已经被服务器接收,且仍未被拒绝。客户端应当继续发送请求的剩余部分,或者如果请求已经完成,忽略这个响应。服务器必须在请求完成后向客户端发送一个最终响应。4.管线化 客户端可以同时发送多个HTTP请求,而不用一个个等待响应 这样就能够做到同时并行发送多个请求,而不需要一个接一个地等待响应了。通俗地讲,请求打包一次传输过去,响应打包一次传递回来。管线化的前提是在持久连接下5.断点续传 其原理是:客户端记录下当前的下载进度,并在需要续传时通知服务器本次需要下载的内容片断 Range和Content-Range6.身份认证,状态管理,cache缓存

8,HTTP长连接和短连接
- HTTP的长连接和短连接本质上是TCP长连接和短连接 - 连接的建立通过三次握手,释放则需要四次握手---> 每个链接的建立都是需要资源消耗 和 时间消耗<短连接> 建立连接——数据传输——关闭连接 在HTTP/1.0中,默认使用的是短连接。也就是说,浏览器和服务器每进行一次HTTP操作,就建立一次连接,但任务结束就中断连接<长连接> 建立连接——数据传输...(保持连接)...数据传输——关闭连接 在HTTP/1.1 起 默认使用长连接 , 会在响应头加入Connection:keep-alive<长连接和短连接的优缺点> -长连接的优点: 1、减少CPU及内存的使用,因为不需要经常的建立及关闭连接,当然高峰并发时CPU及内存也是比较多的 2、减少网络的堵塞,因为减少了TCP请求; 根据RFC 2616 (page 46)的标准定义,单个客户端不允许开启2个以上的长连接,这个标准的目的是减少HTTP响应的时候,减少网络堵塞 3、减少后续请求的响应时间,因为此时不需要建立TCP,也不需要TCP握手等过程; 4、当发生错误时,可以在不关闭连接的情况下进行提示; -长连接的缺点:可能会损害服务器的整体性能,如apache的长连接时间的长短,直接影响到服务器的并发数短连接对于服务器来说管理较为简单,存在的连接都是有用的连接,不需要额外的控制手段。 但如果客户请求频繁,将在TCP的建立和关闭操作上浪费时间和带宽。

9,TCP怎么实现长链接
长连接会存在断开的情况: 1.长链接所在的进程被杀死 2.NAT超时 3.网络状态发生变化 4.其它不可抗拒因素(网络状态差、DHCP的租期等等 )高效维持长链接方案: 1.进程保活:提高进程优先级,降低进程被杀死概率,进程被杀死后,进行拉活,重建 2.心跳保活机制 3.断线重连机制:检测网络状态变化 & 判断连接的有效性# 心跳包 通常是客户端每隔一小段时间向服务器发送一个数据包,通知服务器自己仍然在线,并传输一些可能必要的数据。使用心跳包的典型协议是IM,比如QQ/MSN/飞信等协议。 - 在TCP的机制里面,本身是存在有心跳包的机制的,也就是TCP的选项:SO_KEEPALIVE。系统默认是设置的2小时的心跳频率

11,三次握手
# 三次握手 目的:1).为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误 2).三次握手的最主要目的是保证连接是双工的,可靠更多的是通过重传机制来保证的 全双工:手机 / 单工: 遥控器/ 半双工:对讲机”。1) Client首先发送一个连接试探,ACK=0 表示确认号无效,SYN = 1 表示这是一个连接请求或连接接受报文,同时表示这个数据报不能携带数据,seq = x 表示Client自己的初始序号(seq = 0 就代表这是第0号帧),这时候Client进入syn_send状态,表示客户端等待服务器的回复 2) Server监听到连接请求报文后,如同意建立连接,则向Client发送确认。TCP报文首部中的SYN 和 ACK都置1 ,ack = x + 1表示期望收到对方下一个报文段的第一个数据字节序号是x+1,同时表明x为止的所有数据都已正确收到(ack=1其实是ack=0+1,也就是期望客户端的第1个帧),seq = y 表示Server 自己的初始序号(seq=0就代表这是服务器这边发出的第0号帧)。这时服务器进入syn_rcvd,表示服务器已经收到Client的连接请求,等待client的确认。 3) Client收到确认后还需再次发送确认,同时携带要发送给Server的数据。ACK 置1 表示确认号ack= y + 1 有效(代表期望收到服务器的第1个帧),Client自己的序号seq= x + 1(表示这就是我的第1个帧,相对于第0个帧来说的),一旦收到Client的确认之后,这个TCP连接就进入Established状态,就可以发起http请求了。注意: 1. 三次握手,并没有传递数据,建立连接后才进入到数据传输的状态。 2. SYN(synchronous)是TCP/IP建立连接时使用的握手信号。在客户机和服务器之间建立正常的TCP网络连接时,客户机首先发出一个SYN消息,服务器使用SYN+ACK应答表示接收到了这个消息,最后客户机再以ACK消息响应。这样在客户机和服务器之间才能建立起可靠的TCP连接,数据才可以在客户机和服务器之间传递。

12,TCP三次握手状态转换
1).CLOSED:起始点,在超时或者连接关闭时候进入此状态,这并不是一个真正的状态,而是这个状态图的假想起点和终点。2).LISTEN:服务器端等待连接的状态。服务器经过 socket,bind,listen 函数之后进入此状态,开始监听客户端发过来的连接请求。此称为应用程序被动打开(等到客户端连接请求)。3).SYN_SENT:第一次握手发生阶段,客户端发起连接。客户端调用 connect,发送 SYN 给服务器端,然后进入 SYN_SENT 状态,等待服务器端确认(三次握手中的第二个报文)。如果服务器端不能连接,则直接进入CLOSED状态。4).SYN_RCVD:第二次握手发生阶段,跟 3 对应,这里是服务器端接收到了客户端的 SYN,此时服务器由 LISTEN 进入 SYN_RCVD状态,同时服务器端回应一个 ACK,然后再发送一个 SYN 即 SYN+ACK 给客户端。状态图中还描绘了这样一种情况,当客户端在发送 SYN 的同时也收到服务器端的 SYN请求,即两个同时发起连接请求,那么客户端就会从 SYN_SENT 转换到 SYN_REVD 状态。5).ESTABLISHED:第三次握手发生阶段,客户端接收到服务器端的 ACK 包(ACK,SYN)之后,也会发送一个 ACK 确认包,客户端进入 ESTABLISHED 状态,表明客户端这边已经准备好,但TCP 需要两端都准备好才可以进行数据传输。服务器端收到客户端的 ACK 之后会从 SYN_RCVD 状态转移到 ESTABLISHED 状态,表明服务器端也准备好进行数据传输了。这样客户端和服务器端都是 ESTABLISHED 状态,就可以进行后面的数据传输了。所以 ESTABLISHED 也可以说是一个数据传送状态

13,四次挥手
# 四次挥手 由于 TCP 连接是全双工的,因此每个方向都必须单独进行关闭。这好比,我们打电话(全双工),正常的情况下(出于礼貌),通话的双方都要说再见后才能挂电话,保证通信双方都把话说完了才挂电话。 client至少等2个包的时间, 2mls,大概1到2分钟注意: 中断连接端可以是客户端,也可以是服务器端. 下面仅以客户端断开连接举例, 反之亦然.1.客户端发送一个数据分段, 其中的 FIN 标记设置为1. 客户端进入 FIN-WAIT 状态. 该状态下客户端只接收数据, 不再发送数据. 2.服务器接收到带有 FIN = 1 的数据分段, 发送带有 ACK = 1 的剩余数据分段, 确认收到客户端发来的 FIN 信息. 3.服务器等到所有数据传输结束, 向客户端发送一个带有 FIN = 1 的数据分段, 并进入 CLOSE-WAIT 状态, 等待客户端发来带有 ACK = 1 的确认报文. 4.客户端收到服务器发来带有 FIN = 1 的报文, 返回 ACK = 1 的报文确认, 为了防止服务器端未收到需要重发, 进入 TIME-WAIT 状态. 服务器接收到报文后关闭连接. 客户端等待 2MSL 后未收到回复, 则认为服务器成功关闭, 客户端关闭连接. # 套接字地址(端口)复用,让服务器绑定的端口立马释放

14,四次挥手状态转变
1).FIN_WAIT_1:第一次挥手。主动关闭的一方(执行主动关闭的一方既可以是客户端,也可以是服务器端,这里以客户端执行主动关闭为例),终止连接时,发送 FIN 给对方,然后等待对方返回 ACK 。调用 close() 第一次挥手就进入此状态。2).CLOSE_WAIT:接收到FIN 之后,被动关闭的一方进入此状态。具体动作是接收到 FIN,同时发送 ACK。之所以叫 CLOSE_WAIT 可以理解为被动关闭的一方此时正在等待上层应用程序发出关闭连接指令。前面已经说过,TCP关闭是全双工过程,这里客户端执行了主动关闭,被动方服务器端接收到FIN 后也需要调用 close 关闭,这个 CLOSE_WAIT 就是处于这个状态,等待发送 FIN,发送了FIN 则进入 LAST_ACK 状态。3).FIN_WAIT_2:主动端(这里是客户端)先执行主动关闭发送FIN,然后接收到被动方返回的 ACK 后进入此状态。4).LAST_ACK:被动方(服务器端)发起关闭请求,由状态2 进入此状态,具体动作是发送 FIN给对方,同时在接收到ACK 时进入CLOSED状态。5).CLOSING:两边同时发起关闭请求时(即主动方发送FIN,等待被动方返回ACK,同时被动方也发送了FIN,主动方接收到了FIN之后,发送ACK给被动方),主动方会由FIN_WAIT_1 进入此状态,等待被动方返回ACK。6).TIME_WAIT:从状态变迁图会看到,四次挥手操作最后都会经过这样一个状态然后进入CLOSED状态。共有三个状态会进入该状态

15,TCP协议中 time wait状态的含义?
根据TCP协议,主动发起关闭的一方,在发送ACK K + 1后,会进入TIME_WAIT状态,持续2*MSL(Max Segment Lifetime)。
在TIME_WAIT状态中,如果TCP client端最后一次发送的ACK丢失了,它将重新发送。TIME_WAIT状态中所需要的时间是依赖于实现方法的。典型的值为30秒、1分钟和2分钟。等待之后连接正式关闭,并且所有的资源(包括端口号)都被释放
为了避免多个请求产生多个连接,也就是避免产生创建连接的三次握手和断开连接四次挥手协议的消耗,和出现大量处于TIME_WAIT状态的连接。
于是,长连接就有存在的意义。至于长连接的保持,可以打开keepalive 选项或者在应用层发送心跳包。
16,为什么连接的时候是三次握手,关闭的时候却是四次握手?
因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,"你发的FIN报文我收到了"。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手
17,为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?
虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSE状态了,但是我们必须假象网络是不可靠的,有可以最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文
18,TCP会自动断开链接吗? TCP服务器如何判断客户端断开链接
【python面试大全-网络编程】TCP的保活定时器能够保证TCP连接一直保持,但是TCP的保活定时器不是每个TCP/IP协议栈就实现了,因为RFC并不要求TCP保活定时器一定要实现
1.理想状态下,一个 TCP 连接可以被长期保持。然而,在实际应用中,客户端或服务器端上维持的一个看似正常的 TCP 连接可能已经断连。TCP 连接主要受到两个方面的影响而导致断连:网络中间节点和客户端 / 服务器节点参与通信的两方节点?
2.在实际网络应用中,两个主机之间的通信往往需要穿越多个中间节点,例如路由器、网关、防火墙等。因此,两个主机之间 TCP 连接的保持同样会受到中间节点的影响,尤其是会受到防火墙(软件或硬件防火墙)的限制。防火墙是一种装置,有多种不同的实现方式(软件实现、硬件设备实现或是软硬件相结合实现),它需要依据一系列规则对进出的信息流进行扫描,并允许安全(符合规则)的信息交互、阻止不安全(违反规则)的信息交互。防火墙的工作特性决定了要维护一个网络连接就需要耗费较多的资源,并且企业防火墙常常位于企业网络的出入口,长时间维护非活跃的 TCP 连接必将导致网络性能的下降。因此,大部分防火墙默认会关闭长时间处于非活跃状态的连接而导致 TCP 连接断连。类似的,如果中间节点异常导致来自客户端关闭连接的请求无法传递到服务器端,也将导致服务器端的相应连接发生断连。
思考:TCP服务器如何判断客户端断开链接
当recv()返回值小于等于0时,socket连接断开。但是还需要判断 errno是否等于 EINTR,如果errno == EINTR 则说明recv函数是由于程序接收到信号后返回的,socket连接还是正常的,不应close掉socket连接
19,TCP UDP编程 python面试大全-网络编程
文章图片
TCP.png python面试大全-网络编程
文章图片
UDP.png 20, tcp ip控制流量
TCP的窗口机制和确认保证了数据传输的可靠性和流量控制网络7层模型有:传输层、网络层、会话层、数据链路层、表示层、应用层、物理层。 TCP是传输层协议# 流量控制: 1).指点对点通信量的控制,是端到端中的问题。流量控制所要做的就是抑制发送端发送数据的速率,以便使接收端来得及接收 2).所谓流量控制就是让发送方的发送速率不要太快,要让接收方来得及接收。# 滑动窗口协议 在TCP连接上实现对发送方的流量控制 以字节为单位 1.流量控制是管理两端的流量,以免会产生发送过块导致收端溢出,或者因收端处理太快而浪费时间的状态。用的是:滑动窗口,以字节为单位, 不是报文段 2.窗口有3种动作:展开(右边向右),合拢(左边向右),收缩(右边向左)这三种动作受接收端的控制当一个连接建立时,连接的每一端分配一个缓冲区来保存输入的数据,并将缓冲区的尺寸发送给另一端。当数据到达时,接收方发送确认,其中包含了自己剩余的缓冲区尺寸。剩余的缓冲区空间的大小被称为窗口( w i n d o w) ,指出窗口大小的通知称为窗口通告(window advertisement) 。接收方在发送的每一确认中都含有一个窗口通告如果接收方应用程序读数据的速度能够与数据到达的速度一样快,接收方将在每一确认中发送一个正的窗口通告。然而,如果发送方操作的速度快于接收方(由于CPU更快,接收到的数据最终将充满接收方的缓冲区,导致接收方通告一个零窗口( zero window) 。发送方收到一个零窗口通告时,必须停止发送,直到接收方重新通告一个正的窗口。 TCP的窗口以字节为单位进行调整,以适应接收方的处理能力。(1)TCP连接阶段,双方协商窗口尺寸,同时接收方预留数据缓存区;(2)发送方根据协商的结果,发送符合窗口尺寸的数据字节流,并等待对方的确认;(3)发送方根据确认信息,改变窗口的尺寸,增加或者减少发送未得到确认的字节流中的字节数。调整过程包括:如果出现发送拥塞,发送窗口缩小为原来的一半,同时将超时重传的时间间隔扩大一倍。

22,TCP协议如何保证传输可靠性
TCP提供一种面向连接、可靠的字节流服务。 1.面向连接:使用TCP的应用(服务端和客户端)在彼此交换数据之前必须先建立一个TCP三次握手连接。在一个TCP连接中,仅有两方进行彼此通信。注意广播和多播不能用于TCP。2.可靠性 TCP通过以下方式来提供可靠性: 1》应用数据被分割成TCP认为最适合发送的数据块。这和UDP完全不同,应用程序产生的数据报长度将保持不变(将数据截断为合理的长度) 2》当TCP发出一个段后,它启动一个定时器。等待目的端确认收到这个报文段。如果不能及时收到一个确认,将重发这个报文段。(超时重发) 3》当TCP收到发自TCP连接另一端数据,它将发送一个确认。这个确认不是立即发送,通常推迟几分之一秒用来对包的完整性进行校验。 4》TCP将保持它首部和数据的检验和。这是一个端到端的检验和,目的是检测数据在传输过程中的任何变化。如果收到段的检验和有差错,TCP将丢弃这个报文段和不确认收到此报文段。 (校验出包有错,丢弃报文段,不给出响应,TCP发送数据端,超时时会重发数据)。 5》既然TCP报文段作为IP数据报来传输,而IP数据报的到达可能会失序,因此TCP报文段的到达也可能会失序。如果必要,TCP将对收到的数据进行重新排序,将收到的数据以正确的顺序交给应用层。 (对失序数据进行重新排序,然后才交给应用层)。 6》既然IP数据报会发生重复,TCP的接收端必须丢弃重复的数据。(对于重复数据,能够丢弃重复数据)。 7》TCP还能提供流量控制。TCP连接的每一方都有固定大小的缓冲空间。TCP的接收端只允许另一端发送接收端缓冲区所能接纳的数据。这将防止较快主机致使较慢主机的缓冲区溢出。(TCP可以进行流量控制,防止较快主机致使较慢主机的缓冲区溢出)TCP使用的流量控制协议是可变大小的滑动窗口协议。 滑动窗口协议机制建议参见:http://blog.chinaunix.net/uid-26275986-id-4109679.html 8》字节流服务 两个应用程序通过TCP连接交换8bit字节构成的字节流。TCP不在字节流中插入记录标识符。我们将这称为字节流服务(bytestreamservice)。 TCP对字节流的内容不作任何解释:: TCP对字节流的内容不作任何解释。TCP不知道传输的数据字节流是二进制数据,还是ASCII字符、EBCDIC字符或者其他类型数据。对字节流的解释由TCP连接双方的应用层解释

23,get和post方法的区别 https://blog.csdn.net/u012813201/article/details/70210812
1.)get参数通过url传递或cookie传递,post放在request body中。 2.)get请求在url中传递的参数是有长度限制的(2048个字符),而post没有限制。 3.)get比post更不安全,因为参数直接暴露在url中,所以不能用来传递敏感信息。 4.)get请求只能进行url编码,而post支持多种编码方式 5.)get请求会浏览器主动cache缓存,而post不会缓存。 6.)get请求参数会被完整保留在浏览历史记录里,而post中的参数不会被保留。GET和POST本质上就是TCP链接# .get产生一个tcp数据包, post产生两个tcp数据包 - 对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据); - POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。 a.并不是所有浏览器都会在POST中发送两次包,Firefox就只发送一次 b.在网络环境好的情况下,发一次包的时间和发两次包的时间差别基本可以无视。而在网络环境差的情况下,两次包的TCP在验证数据包完整性上,有非常大的优点

24,常见的http请求方法
HTTP协议的请求方法有: get, post, patch, put, delete, options, trace, connect,head# get 获取资源 1.请求参数和对应的值附加在URL后面,利用一个问号(“?”)代表URL的结尾与请求参数的开始 2.传递参数长度受限制,1024个字符 3.请求参数直接显示在地址栏中 4.不适合传送私密数据# post 创建资源,会返回创建的资源 1.请求参数放在request body中 2.POST方式对传送的数据大小没有限制 3.表单提交# put 替换整个资源,客户端需要提供新建资源的所有属性。如果新内容为空,要设置 Content-Length 为 0,以区别错误信息,会返回更新的资源 put 是可以创建资源的,但是一般只存在于客户端可以指定资源id的情况下# patch 提交什么就更新什么, 更新资源的部分属性。因为 PATCH 比较新,而且规范比较复杂,所以真正实现的比较少,一般都是用 POST 替代,会返回更新的资源# head HEAD就像GET,只不过服务端接受到HEAD请求后只返回响应头,而不会发送响应内容。当我们只需要查看某个页面的状态的时候,使用HEAD是非常高效的,因为在传输的过程中省去了页面内容。

25,http https的区别 https://www.cnblogs.com/wudaoyongchang/p/6253451.html
https协议是由ssl+http协议构建的可进行加密传输,所有的http数据都是在SSL/TLS协议封装之上传输的,身份认证的网络协议,比http协议安全,HTTPS也属于应用层协议.1.HTTP超文本传输协议,协议运行在TCP之上, 所有传输的内容都是明文,客户端和服务器端都无法验证对方的身份. http 的连接很简单,无状态链接 http 端口802.HTTPS是运行在SSL/TLS之上的HTTP协议, 比http协议安全 安全性的:所有传输的内容都经过加密,加密采用对称加密,但对称加密的密钥用服务器方的证书进行了非对称加密. https 端口443 https 协议需要到 ca 申请证书,一般免费证书很少,需要交费。

26,HTTPS 的优缺点
# 主要作用: 一种是建立一个信息安全通道,来保证数据传输的安全; 另一种就是确认网站的真实性。从安全和性能方向回答# 优点 1.使用 HTTPS 协议可认证用户和服务器,确保数据发送到正确的客户机和服务器; 2.HTTPS 协议是由 SSL+HTTP 协议构建的可进行加密传输.身份认证的网络协议, 要比 http 协议安全,可防止数据在传输过程中不被窃取.改变,确保数据的完整性. 3.HTTPS 是现行架构下最安全的解决方案,虽然不是绝对安全,但它大幅增加了中间人攻击的成本.# 缺点 1.HTTPS 协议的加密范围也比较有限,在黑客攻击.拒绝服务攻击.服务器劫持等方 面几乎起不到什么作用 2.HTTPS 协议还会影响缓存,增加数据开销和功耗,甚至已有安全措施也会受到影响. 3.SSL 证书需要钱.功能越强大的证书费用越高.个人网站.小网站没有必要一般不会用. 4.HTTPS 连接服务器端资源占用高很多,握手阶段比较费时对网站的相应速度有负面 影响. 5.HTTPS 连接缓存不如 HTTP 高效.

27,https的工作原理 , 具体怎么实现加密的 https://www.cnblogs.com/cymiao/p/5126346.html
https://github.com/ljianshu/Blog/issues/50
HTTPS 协议的主要功能基本都依赖于 TLS/SSL 协议,TLS/SSL 的功能实现主要依赖于三类基本算法:散列函数 、对称加密和非对称加密,**其利用非对称加密实现身份认证和密钥协商,对称加密算法采用协商的密钥对数据加密,基于散列函数验证信息的完整性**。一)解决内容可能被窃听的问题——加密 在交换密钥环节使用非对称加密方式,之后的建立通信交换报文阶段则使用对称加密方式 HTTPS采用对称加密和非对称加密两者并用的混合加密机制。二)解决报文可能遭篡改问题——数字签名 * 能确定消息确实是由发送方签名并发出来的,因为别人假冒不了发送方的签名。 * 数字签名能确定消息的完整性,证明数据是否未被篡改过。 数字签名技术就是对“非对称密钥加解密”和“数字摘要“两项技术的应用,它将摘要信息用发送者的私钥加密,与原文一起传送给接收者。接收者只有用发送者的公钥才能解密被加密的摘要信息,然后用HASH函数对收到的原文产生一个摘要信息,与解密的摘要信息对比。如果相同,则说明收到的信息是完整的,在传输过程中没有被修改,否则说明信息被修改过,因此数字签名能够验证信息的完整性。三)解决通信方身份可能被伪装的问题——认证 **可以使用由数字证书认证机构(CA,Certificate Authority)和其相关机关颁发的公开密钥证书**。 首先,服务器的运营人员向数字证书认证机构提出公开密钥的申请。数字证书认证机构在判明提出申请者的身份之后,会对已申请的公开密钥做数字签名,然后分配这个已签名的公开密钥,并将该公开密钥放入公钥证书后绑定在一起。 服务器会将这份由数字证书认证机构颁发的公钥证书发送给客户端,以进行非对称加密方式通信。公钥证书也可叫做数字证书或直接称为证书。接到证书的客户端可使用数字证书认证机构的公开密钥,对那张证书上的数字签名进行验证,**一旦验证通过,客户端便可明确两件事:一,认证服务器的公开密钥的是真实有效的数字证书认证机构。二,服务器的公开密钥是值得信赖的**。# 为什么不一直使用HTTPS? 其中一个原因是,因为**与纯文本通信相比,加密通信会消耗更多的CPU及内存资源**。如果每次通信都加密,会消耗相当多的资源,平摊到一台计算机上时,能够处理的请求数量必定也会随之减少。 **因此,如果是非敏感信息则使用HTTP通信,只有在包含个人信息等敏感数据时,才利用HTTPS加密通信**。 特别是每当那些访问量较多的Web网站在进行加密处理时,它们所承担着的负载不容小觑。除此之外,**想要节约购买证书的开销也是原因之一**。要进行HTTPS通信,证书是必不可少的。而使用的证书必须向认证机构(CA)购买。

28,https采用的是混合加密机制(即使用非对称获得对方的私钥,之后确定安全后使用对称加密来通信)
HTTPS在传输数据之前需要客户端(浏览器)与服务端(网站)之间进行一次握手,在握手过程中将确立双方加密传输数据的密码信息。TLS/SSL中使用了非对称加密,对称加密以及HASH算法。握手过程的简单描述如下:1.浏览器将自己支持的一套加密规则发送给网站。2.网站从中选出一组加密算法与HASH算法,并将自己的身份信息以证书的形式发回给浏览器。证书里面包含了网站地址,加密公钥,以及证书的颁发机构等信息。3.获得网站证书之后浏览器要做以下工作:a) 验证证书的合法性(颁发证书的机构是否合法,证书中包含的网站地址是否与正在访问的地址一致等),如果证书受信任,则浏览器栏里面会显示一个小锁头,否则会给出证书不受信的提示。b) 如果证书受信任,或者是用户接受了不受信的证书,浏览器会生成一串随机数的密码,并用证书中提供的公钥加密。c) 使用约定好的HASH计算握手消息,并使用生成的随机数对消息进行加密,最后将之前生成的所有信息发送给网站。4.网站接收浏览器发来的数据之后要做以下的操作:a) 使用自己的私钥将信息解密取出密码,使用密码解密浏览器发来的握手消息,并验证HASH是否与浏览器发来的一致。b) 使用密码加密一段握手消息,发送给浏览器。5.浏览器解密并计算握手消息的HASH,如果与服务端发来的HASH一致,此时握手过程结束,之后所有的通信数据将由之前浏览器生成的随机密码并利用对称加密算法进行加密。这里浏览器与网站互相发送加密的握手消息并验证,目的是为了保证双方都获得了一致的密码,并且可以正常的加密解密数据,为后续真正数据的传输做一次测试。另外,HTTPS一般使用的加密与HASH算法如下:非对称加密算法:RSA,DSA/DSS # 用于在握手过程中加密生成的密码对称加密算法:AES,RC4,3DES # 用于对真正传输的数据进行加密HASH算法:MD5,SHA1,SHA256 # 用于验证数据的完整性由于浏览器生成的密码是整个数据加密的关键,因此在传输的时候使用了非对称加密算法对其加密。非对称加密算法会生成公钥和私钥,公钥只能用于加密数据,因此可以随意传输,而网站的私钥用于对数据进行解密,所以网站都会非常小心的保管自己的私钥,防止泄漏。TLS握手过程中如果有任何错误,都会使加密连接断开,从而阻止了隐私信息的传输

1,当你的浏览器向服务器请求一个安全的网页(通常是 https://) 2,服务器就把它的证书和公匙发回来 3,浏览器检查证书是不是由可以信赖的机构颁发的,确认证书有效和此证书是此网站的。4,使用公钥加密了一个随机对称密钥,包括加密的URL一起发送到服务器 5,服务器用自己的私匙解密了你发送的钥匙。然后用这把对称加密的钥匙给你请求的URL链接解密 6,服务器用你发的对称钥匙给你请求的网页加密。你也有相同的钥匙就可以解密发回来的网页了

29,HTTP TCP 的关系
1.tcp通过三次握手链接 四次挥手断开链接 2.http 超文本传输协议 , http协议是建立在tcp协议之上的应用 http1.0 短连接http1.1 是长连接 3.http应用层是要基于TCP传输层的连接基础上三次握手: 第一次:C发送一个请求连接的位码SYN和一个随机产生的序列号给Seq,然后S收到了这些数据。 第二次:S收到了这个请求连接的位码,啊呀,有人向我发出请求了么,那我要不要接受他的请求,得实现确认一下,于是,发送了一个确认码 ACN(seq+1),和SYN,Seq给C,然后C收到了,这个是第二次连接。 第三次:C收到了确认的码和之前发送的SYN一比较,偶哟,对上了么,于是他又发送了一个ACN(SEQ+1)给S,S收到以后就确定建立连接,至此,TCP连接建立完成 TCP是底层通讯协议,定义的是数据传输和连接方式的规范 HTTP是应用层协议,定义的是传输数据的内容的规范 HTTP协议中的数据是利用TCP协议传输的,所以支持HTTP也就一定支持TCP

30,cookie和session的区别
# 区别 1.cookie数据存放在客户的浏览器上, session数据存放在服务器上 2.cookie不是很安全,别人可以分析放在本地的COOKIE并进行COOKIE欺骗 3.session会在一定时间内保存在服务器上,当访问增多,会比较占用服务器的性能,考虑到减轻服务器性能方面,应当使用cookie 4.单个cookie保存的数据不能超过4k, 很多浏览器都限制一个站点最多保存20个cookie 5.cookie保存的是字符串, session保存的是对象# 联系 session需要借助cookie才能正常工作,如果客户端完全禁止cookie, session将失效

31,cookie 和session详解
1.会话保持(HTTP协议是无状态的协议。一旦数据交换完毕,客户端与服务器端的连接就会关闭,再次交换数据需要建立新的连接。这就意味着服务器无法从连接上跟踪会话)2.工作原理:给客户端们颁发一个通行证吧,每人一个,无论谁访问都必须携带自己通行证。这样服务器就能从通行证上确认客户身份了3.Cookie实际上是一小段的文本信息。客户端请求服务器,如果服务器需要记录该用户状态,就使用response向客 户端浏览器颁发一个Cookie。客户端浏览器会把Cookie保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务 器。服务器检查该Cookie,以此来辨认用户状态。服务器还可以根据需要修改Cookie的内容。4.Cookie具有不可跨域名性:谷歌不能访问百度的cookie5.cookie有效期: maxAge getMaxAge()方法 setMaxAge(int maxAge) 1)maxAge为负数:表明该cookie仅在本浏览器窗口内有效,关闭窗口后该cookie失效; 为临时cookie,不会被持久化,不会写入cookie文件中,Cookie信息保存在浏 览器内存中,因此关闭浏览器该Cookie就消失了。Cookie默认的maxAge值为–1。 2).maxAge为0,表示删除该cookie 3).response对象提供的Cookie操作方法只有一个添加操作add(Cookie cookie)。 要想修改Cookie只能使用一个同名的Cookie来覆盖原来的Cookie,达到修改的目的。删除时只需要把maxAge修改为0即可。

32,状态保持,会话技术(cookie session)
# 状态保持 - 因为 http 是一种无状态协议,浏览器请求服务器是无状态的。 - 无状态:指一次用户请求时,浏览器、服务器无法知道之前这个用户做过什么,每次请求都是一次新的请求。 - 无状态原因:浏览器与服务器是使用 socket 套接字进行通信的,服务器将请求结果返回给浏览器之后,会关闭当前的 socket 连接,而且服务器也会在处理页面完毕之后销毁页面对象。 - 有时需要保持下来用户浏览的状态,比如用户是否登录过,浏览过哪些商品,购物车等 - 实现状态保持主要有两种方式: - 在客户端存储信息使用Cookie - 在服务器端存储信息使用Session# 无状态协议: 1. 协议对于事务处理没有记忆能力 2. 对同一个 url 请求没有上下文关系 3. 每次的请求都是独立的,它的执行情况和结果与前面的请求和之后的请求是无直接关系的,它不会受前面的请求应答情况直接影响,也不会直接影响后面的请求应答情况 4. 服务器中没有保存客户端的状态,客户端必须每次带上自己的状态去请求服务器# cookie 服务端生成,发送给客户端浏览器,浏览器保存,下次请求同一网站时就发送该cookie给服务器 # 应用 判断用户是否已经登录网站 网站的广告推送 购物车 make_response.set_cookie("xxx":"xxx",max_age=秒)# Session 服务器端进行状态保持的方案就是Session Session依赖于Cookie flask自带的session是把key-value加密后存储在了cookie里,而Flask-Session则提供了把session信息存在服务器的支持。 需要设置:SECRET_KEY= "", 不然会报错 设置session里面的数据:session['name']='python' ---> name=session.get('name') 可以存储在以下数据库中 : redis/memcached/filesystem / mongodb / sqlalchemy

33,flask中session实现原理(flask_session) http://cizixs.com/2017/03/08/flask-insight-session/
# 请求刚进来的时候,flask把session封装在RequestContext对象里面,最开始是空的,把RequestContext push放到某个地方,通过open_sessiong给它赋值,视图函数进行相关操作,最后执行save_sessionsession 可以看做是在不同的请求之间保存数据的方法,因为 HTTP 是无状态的协议,但是在业务应用上我们希望知道不同请求是否是同一个人发起的。比如购物网站在用户点击进入购物车的时候,服务器需要知道是哪个用户执行了这个操作。session 是通过在客户端设置 cookie 实现的,每次客户端发送请求的时候会附带着所有的 cookie,而里面保存着一些重要的信息(比如这里的用户信息),这样服务器端就能知道客户端的信息,然后根据这些数据做出对应的判断,就好像不同请求之间是有记忆的。 # 请求过程 open_session - 请求过来的时候,flask 会根据 cookie 信息创建出 session 变量(如果 cookie 不存在,这个变量有可能为空),保存在该请求的上下文中- 视图函数可以获取 session 中的信息,实现自己的逻辑处理- flask 会在发送 response 的时候,根据 session 的值,把它写回到 cookie 中我们知道,每次请求过来的时候,我们访问的 request 和 session 变量都是 RequestContext 实例的变量,它初始化了 session 变量,保存在 RequestContext 上,这样后面就能直接通过 from flask import session 来使用它。如果没有设置 secret_key 变量, open_session 就会返回 None,这个时候会调用 make_null_session 来生成一个空的 session,这个特殊的 session 不能进行任何读写操作,不然会报异常给用户。# 应答过程 save_session flask 会在请求过来的时候自动解析 cookie 的值,把它变成 session 变量。开发在视图函数中可以使用它的值,也可以对它进行更新。最后再返回的 response 中,flask 也会自动把 session 写回到 cookie。我们来看看这部分是怎么实现的!# 解密session session 在 cookie 中的值,是一个字符串,由句号分割成三个部分。第一部分是 base64 加密的数据,第二部分是时间戳,第三部分是校验信息。 In [1]: from itsdangerous import *In [2]: s = 'eyJ1c2VybmFtZSI6ImNpeml4cyJ9.C5fdpg.fqm3FTv0kYE2TuOyGF1mx2RuYQ4'In [3]: data, timstamp, secret = s.split('.')In [4]: base64_decode(data) Out[4]: '{"username":"cizixs"}'In [5]: bytes_to_int(base64_decode(timstamp)) Out[5]: 194502054In [7]: time.strftime('%Y-%m-%d %H:%I%S', time.localtime(194502054+EPOCH)) Out[7]: '2017-03-01 12:1254'# 总结 将服务器产生key-value存在redis,同时生成一个seesion_id存在cookie flask 默认提供的 session 功能还是很简单的,满足了基本的功能。但是我们看到 flask 把 session 的数据都保存在客户端的 cookie 中,这里只有用户名还好,如果有一些私密的数据(比如密码,账户余额等等),就会造成严重的安全问题。可以考虑使用 flask-session 这个三方的库,它把数据保存在服务器端(本地文件、redis、memcached),客户端只拿到一个 sessionid。session 主要是用来在不同的请求之间保存信息,最常见的应用就是登陆功能。虽然直接通过 session 自己也可以写出来不错的登陆功能,但是在实际的项目中可以考虑 flask-login 这个三方的插件,方便我们的开发

34,flask_login 登录状态的记录
is_authenticated() 判断是否登录,登录返回True,否则返回False
is_active() 是否允许登录,即用户是否已被禁用,如禁用返回False
is_anonymous() False为普通用户
get_id() 返回用户唯一标识符,使用Unicode编码
即继承Flask-Login提供的UserMixin类,它里边包含了这些方法的默认实现,修改后的User模型为
35,CSRF
- CSRF全拼为Cross Site Request Forgery,译为跨站请求伪造。 - CSRF指攻击者盗用了你的身份,以你的名义发送恶意请求。 - 包括:以你名义发送邮件,发消息,盗取你的账号,甚至于购买商品,虚拟货币转账...... - 造成的问题:个人隐私泄露以及财产安全 - CSRG 不会效验GET请求,CSRF,只会校验['POST', 'PUT', 'PATCH', 'DELETE'],不会校验get请求,手动往cookie里面设置csrf_token,往服务器的表单设置token# 步骤 -1. CSRFProtect(app)开启CSRF保护,避免受到CSRF的攻击 from flask.ext.wtf import CSRFProtect CSRFProtect(app)-2. 生成 csrf_token 的值 # 导入生成 csrf_token 值的函数 from flask_wtf.csrf import generate_csrf # 调用函数生成 csrf_token csrf_token = generate_csrf()-3. 将 csrf_token 的值传给前端浏览器 ,用过cookie - 实现思路:可以在请求勾子函数中完成此逻辑 - after_request 在请求index函数之后执行,如果不报错,每次都执行,因为在请求函数之后执行,所有会接受一个参数 @app.after_request def after_request(response): # 调用函数生成 csrf_token csrf_token = generate_csrf() # 通过 cookie 将值传给前端 response.set_cookie("csrf_token", csrf_token) return response-4. 在前端请求时带上 csrf_token 值 在 Form 表单中添加一个隐藏的的字段,值也是 csrf_token {{ form.csrf_token }} (服务器也保存一份隐藏的csrf_token)- 根据登录和注册的业务逻辑,当前采用的是 ajax 请求 - 所以在提交登录或者注册请求时,需要在请求头中添加 X-CSRFToken 的键值对-5.后端接受到请求,以会以下几件事件 -从cookie中取出csrf_token -从表单数据中取出隐藏的csrf_token -进行对比 if request.method == "POST": to_account = request.form.get("to_account") money = request.form.get("money") # 取出表单中的 csrf_token form_csrf_token = request.form.get("csrf_token") # 取出 cookie 中的 csrf_token cookie_csrf_token = request.cookies.get("csrf_token") # 进行对比 if cookie_csrf_token != form_csrf_token: return 'token校验失败,可能是非法操作'# 防御: 1.通过 referer、token 或者 验证码 来检测用户提交。 2.尽量不要在页面的链接中暴露用户隐私信息。 3.对于用户修改删除等操作最好都使用post 操作 。 4.避免全站通用的cookie,严格设置cookie的域。

36,URL是什么
URL(Uniform Resource Locator),统一资源定位符,用于定位互联网上资源,俗称网址
scheme://host.domain:port/path/filename
scheme - 定义因特网服务的类型。常见的协议有 http、https、ftp、file,其中最常见的类型是 http,而 https 则是进行加密的网络传输。 host - 定义域主机(http 的默认主机是 www) domain - 定义因特网**域名**,比如 w3school.com.cn port - 定义主机上的端口号(http 的默认端口号是 80) path - 定义服务器上的路径(如果省略,则文档必须位于网站的根目录中)。 filename - 定义文档/资源的名称

37,关于http协议:浏览器输入地址后发生了什么? https://blog.csdn.net/u013363501/article/details/63692528
https://www.cnblogs.com/kongxy/p/4615226.html
# 1.在浏览器中输入url 用户输入url,例如http://www.baidu.com。其中http为协议,www.baidu.com为网络地址,及指出需要的资源在那台计算机上。一般网络地址可以为域名或IP地址,此处为域名。使用域名是为了方便记忆,但是为了让计算机理解这个地址还需要把它解析为IP地址1. 如果浏览器已经缓存了这个ip就直接请求 * 浏览器缓存:浏览器会按照一定的频率缓存 DNS 记录。 * 操作系统缓存:如果浏览器缓存中找不到需要的 DNS 记录,那就去操作系统中找。 * 路由缓存:路由器也有 DNS 缓存。 * ISP 的 DNS 服务器:ISP 是互联网服务提供商(Internet Service Provider)的简称,ISP 有专门的 DNS 服务器应对 DNS 查询请求。 * 根服务器:ISP 的 DNS 服务器还找不到的话,它就会向根服务器发出请求,进行递归查询(DNS 服务器先问根域名服务器.com 域名服务器的 IP 地址,然后再问.baidu 域名服务器,依次类推)2. 编辑Hosts文件可达到多种目的,如:加快域名解析、方便局域网用户、屏蔽网站、顺利连接系统等 3. Hosts是一个没有扩展名的系统文件,其作用就是将一些常用的网址域名与其对应的IP地址建立一个关联“数据库”,当用户在浏览器中输入一个需要登录的网址时,系统会首先自动从Hosts文件中寻找对应的IP地址,一旦找到,系统会立即打开对应网页,如果没有找到,则系统再会将网址提交DNS域名解析服务器进行IP地址的解析# 2.应用层DNS解析域名 查找域名对应的IP地址 **DNS 是一个网络服务器,我们的域名解析简单来说就是在 DNS 上记录一条信息记录**。# 3.TCP链接: tcp三次握手 为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误 保证数据的传输是双工的 并且安全可靠# 3.应用层客户端发送HTTP请求 浏览器对请求进行包装,包装成请求报文,返回的信息是否需要缓存,以及客户端是否发送cookie <请求报文分析> 1.请求行: 请求方法(GET POST) 请求路径URL HTTP协议版本; 例如:POST /form/entryHTTP/1.1 \r\n 2.请求头: 3.空行: 必须是空行, 通知服务器以下不再有请求头例如: \r\n 4.请求包体: 只在POST方法中使用/get请求没有请求体# 4.传输层tcp 传输报文 位于传输层的TCP协议为传输报文提供可靠的字节流服务。它为了方便传输,将大块的数据分割成以报文段为单位的数据包进行管理,并为它们编号,方便服务器接收时能准确地还原报文信息。TCP协议通过“三次握手”等方法保证传输的安全可靠。# 5.服务器接收数据 并响应请求; 1.服务器可能会有很多台,到底指定哪台服务器来处理请求,这需要一个负载均衡设备来平均分配所有用户的请求 2.还有请求的数据是存储在分布式缓存里还是一个静态文件中,或是在数据库里 3.当数据返回浏览器时,浏览器解析数据发现还有一些静态资源(如CSS、JS或者图片)时又会发起另外的HTTP请求由于http是无状态的 但是http1.0有一个keep-alive的请求字段,可以在一定时间内不断开连接#应用层方面 数据交换主要通过 http 协议, http 协议是无状态协议,这里可以谈一谈 post、get 的区别以及 RESTFul 接口设计,然后可以讲服务器 server 模型 epoll、select 等,接着可以根据实际经验讲下 server 处理流程,比如我: server 这边 Nginx 拿到请求,进行一些验证,比如黑名单拦截之类的,然后 Nginx 直接处理静态资源请求,其他请求 Nginx 转发给后端服务器,这里我用 uWSGI, 他们之间通过 uwsgi 协议通讯,uWSGI 拿到请求,可以进行一些逻辑, 验证黑名单、判断爬虫等,根据 wsgi 标准,把拿到的 environs 参数扔给 Django ,Django 根据 wsgi 标准接收请求和 env, 然后开始 startresponse ,先跑 Django 相关后台逻辑,Django 拿到请求执行 request middleware 内的相关逻辑,然后路由到相应 view 执行逻辑,出错执行 exception middleware 相关逻辑,接着 response 前执行 response middleware 逻辑,最后通过 wsgi 标准构造 response, 拿到需要返回的东西,设置一些 headers,或者 cookies 之类的,最后 finishresponse 返回,再通过 uWSGI 给 Nginx ,Nginx 返回给浏览器。# 6.服务器返回响应结果; 如果服务器返回的content-type是accept中的任何一个,浏览器都能解析 # 7.关闭TCP连接; 客户端:“兄弟,我这边没数据要传了,咱关闭连接吧。” 服务端:“收到,我看看我这边有木有数据了。” 服务端:“兄弟,我这边也没数据要传你了,咱可以关闭连接了。” 客户端:“好嘞。” # 8.浏览器解析HTML; 解析iHTML以构建DOM树 –> 构建渲染树 –> 布局渲染树 –> 绘制渲染树 * 根据 HTML 解析出 DOM 树 * 根据 CSS 解析生成 CSS 规则树 * 结合 DOM 树和 CSS 规则树,生成渲染树 * 根据渲染树计算每一个节点的信息 * 根据计算好的信息绘制页面 # 9.浏览器布局渲染;

38, http中缓存是如何实现的,怎么知道要缓存,浏览器缓存原理 https://www.cnblogs.com/chenqf/p/6386163.html
http://www.cnblogs.com/skynet/archive/2012/11/28/2792503.html
https://github.com/ljianshu/Blog/issues/23
python面试大全-网络编程
文章图片
浏览器缓存.png
请求报文和响应报文 浏览器缓存机制,其实主要就是HTTP协议定义的缓存机制(如: Expires; Cache-control等)强制缓存对比缓存# 强制缓存如果生效,不需要再和服务器发生交互,而对比缓存不管是否生效,都需要与服务端发生交互。两类缓存规则可以同时存在,强制缓存优先级高于对比缓存,也就是说,当执行强制缓存的规则时,如果缓存生效,直接使用缓存,不再执行对比缓存规则在没有缓存数据的时候,浏览器向服务器请求数据时,服务器会将数据和缓存规则一并返回,缓存规则信息包含在响应header中 响应header中会有两个字段来标明失效规则 # (Expires/Cache-Control) HTTP 1.0 Expires # 现在默认浏览器均默认使用HTTP 1.1,所以它的作用基本忽略。 HTTP 1.1 Cache-Control # 常见的取值有private、public、no-cache、max-age,no-store,默认为private(客户端可以缓存)。 Cache-Control仅指定了max-age,所以默认为private,缓存时间为31536000秒(365天)# 对比缓存 服务器返回304标识 对比缓存,顾名思义,需要进行比较判断是否可以使用缓存。 浏览器第一次请求数据时,服务器会将缓存标识与数据一起返回给客户端,客户端将二者备份至缓存数据库中。 再次请求数据时,客户端将备份的缓存标识发送给服务器,服务器根据缓存标识进行判断, 不同,说明资源又被改动过,则响应整片资源内容,返回状态码200; 相同,说明资源无新修改,则响应HTTP 304,告知浏览器继续使用所保存的cache。缓存标识的传递是我们着重需要理解的 响应头: Last-Modified服务器在响应请求时,告诉浏览器资源的最后修改时间 If-Modified-Since再次请求服务器时,浏览器通过此字段通知服务器上次请求时,服务器返回的资源最后修改时间。 Etag:服务器响应请求时,告诉浏览器当前资源在服务器的唯一标识(生成规则由服务器决定) if-NOne-Match再次请求服务器时,浏览器通过此字段通知服务器客户段缓存数据的唯一标识。# 总结 对于强制缓存,服务器通知浏览器一个缓存时间,在缓存时间内,下次请求,直接用缓存,不在时间内,执行比较缓存策略。 对于比较缓存,将缓存信息中的Etag和Last-Modified通过请求发送给服务器,由服务器校验,返回304状态码时,浏览器直接使用缓存。

39,socket的原理 https://www.cnblogs.com/wangcq/p/3520400.html
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。 是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元。它是网络通信过程中端点的抽象表示网络中进程之间如何通信? # [消息传递 同步 共享内存 远程过程调用] 解决的问题是如何唯一标识一个进程 本地: 通过进程PID来唯一标识一个进程 网络: 网络层:ip 可以唯一标识网络中的主机, 传输层的 协议+端口 可以唯一标识主机中的进程# 什么是socket 是“open—write/read—close”模式的一种实现 socket 三个参数(协议族socket类型指定协议) 协议族:AF_INET、AF_INET6 SOCKET类型: SOCK_STREAM、SOCK_DGRAM 指定协议:TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议 socketbindlistenacceptreadwrite close

40,轮询和长轮询 websocket
# 轮询: 每2秒钟发送请求。短轮询不适用于那些同时在线用户数量比较大,并且很注重性能的Web应用# 长轮询: 最多hang住30s(兼容性好) - 实时 - 在线 ajax实现 当服务器收到客户端发来的请求后,服务器端不会直接进行响应,而是先将这个请求挂起,然后判断服务器端数据是否有更新。如果有更新,则进行响应,如果一直没有数据,则到达一定的时间限制(服务器端设置)才返回。 。 客户端JavaScript响应处理函数会在处理完服务器返回的信息后,再次发出请求,重新建立连接。长轮询和短轮询比起来,明显减少了很多不必要的http请求次数,相比之下节约了资源。长轮询的缺点在于,连接挂起也会导致资源的浪费。# websocket实现(兼容性不太好) WebSocket是Html5定义的一个新协议,与传统的http协议不同,该协议可以实现服务器与客户端之间全双工通信。简单来说,首先需要在客户端和服务器端建立起一个连接,这部分需要http。连接一旦建立,客户端和服务器端就处于平等的地位,可以相互发送数据,不存在请求和响应的区别。WebSocket的优点是实现了双向通信,缺点是服务器端的逻辑非常复杂。现在针对不同的后台语言有不同的插件可以使用。实现Web端即时通讯的方法:实现即时通讯主要有四种方式 它们分别是轮询、长轮询(comet)、长连接(SSE)、WebSocket

41,TCP粘包和分包
# 关于分包和粘包 粘包:发送方发送两个字符串”hello”+”world”,接收方却一次性接收到了”helloworld”. 分包:发送方发送字符串”helloworld”,接收方却接收到了两个字符串”hello”和”world”.# TCP为什么会分包 TCP是以段(Segment)为单位发送数据的,建立TCP链接后,有一个最大消息长度(MSS).如果应用层数据包超过MSS,就会把应用层数据包拆分,分成两个段来发送.这个时候接收端的应用层就要拼接这两个TCP包,才能正确处理数据.# TCP为什么会粘包 有时候,TCP为了提高网络的利用率,会使用一个叫做Nagle的算法.该算法是指,发送端即使有要发送的数据,如果很少的话,会延迟发送.如果应用层给TCP传送数据很快的话,就会把两个应用层数据包“粘”在一起,TCP最后只发一个TCP数据包给接收端.# 如何处理 在进行TCP Socket开发时,都需要处理数据包粘包和分包的情况.使用的语言是Python.实际上解决该问题很简单,在应用层下,定义一个协议:消息头部+消息长度+消息正文即可.

42,TCP协议里的backlog含义是什么?
backlog (等待连接最大数量)其实是一个连接队列,以下是backlog队列大小公式。
backlog队列总和=未完成三次握手队列 + 已经完成三次握手队列
43,ARP协议
地址解析协议(Address Resolution Protocol),其基本功能为透过目标设备的IP地址,查询目标的MAC地址,以保证通信的顺利进行。它是IPv4网络层必不可少的协议,不过在IPv6中已不再适用,并被邻居发现协议(NDP)所替代。
根据IP地址获取物理地址的一个TCP/IP协议
44,urllib 和 urllib2 的区别
urllib 和urllib2都是接受URL请求的相关模块,但是urllib2可以接受一个Request类的实例来设置URL请求的headers,urllib仅可以接受URL。这意味着,你不可以伪装你的User Agent字符串等。 urllib提供urlencode方法用来GET查询字符串的产生, 而urllib2没有。这是为何urllib常和urllib2一起使用的原因。目前的大部分http请求都是通过urllib2来访问的

45,requests 模块
安装: pip install requests - requests的底层实现就是urllib; 简单易用 - requests在python2 和python3中通用,方法完全一样(urllib 是不一样的) - requests能够自动帮助我们解压(gzip压缩的等)网页内容核心代码: response = requests.get(url)/// response的常用属性: 1.response.text 获取的是字符串数据, 使用字符集是通过响应头推断出来的,一般情况推断都是错的 2.respones.content 二进制形式的响应数据bytes格式 通过deocde() ---> 可以转换成 字符串数据 3.response.status_code 响应状态码 4.response.headers 响应头 5.response.request.url 请求url 6.response.url 获取响应的url ///response.text 和response.content的区别 response.text: - 类型:str - 解码类型: 根据HTTP头部对响应的编码作出有根据的推测,推测的文本编码 - 如何修改编码方式:response.encoding=”gbk” response.content - 类型:bytes - 解码类型: 没有指定 - 如何修改编码方式:response.content.deocde(“utf8”)更推荐使用response.content.deocde()的方式获取响应的html页面

46,request.session登录验证的爬虫方案
使用session模拟登录,获取登陆的cookies import requests #1.创建session对象,可以保存Cookie值 session = requests.session()#采用requests的session进行get/post操作 headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"}#2.准备需要登录的用户名和密码 data = https://www.it610.com/article/{"email":"398492476@qq.com","password":"zlf27588968"}#3.发送附带用户名和密码的请求,并获取登录后的Cookie值,保存在session里 session.post("http://www.renren.com/PLogin.do",data=https://www.it610.com/article/data,headers=headers)# 4.session包含用户登录后的Cookie值,可以直接访问那些登录后才可以访问的页面 response = session.get("http://www.renren.com/410043129/profile",headers=headers)# 5. 打印响应内容 print(response.content.decode())

47,常见反爬手段和解决思路
1.通过headers中的User-Agent字段来反爬:是使用User-Agent池来解决,我们可以考虑收集一堆User-Agent的方式,或者是随机生成User-Agent 2.通过referer字段或者是其他字段来反爬(防盗链)3.通过cookie来反爬 - 如果目标网站不需要登录 每次请求带上前一次返回的cookie,比如requests模块的session(亚马逊) - 如果目标网站需要登录 准备多个账号,通过一个程序获取账号对应的cookie,组成cookie池,其他程序使用这些cookie4.通过js来反爬(通过js生成了请求参数/实现了数据的加密) 可以通过点击perserve log按钮实现观察页面跳转情况5.通过验证码来反爬,通过打码平台或者是机器学习的方法识别验证码,其中打码平台廉价易用6.通过ip地址来反爬:同一个ip大量请求了对方服务器,有更大的可能性会被识别为爬虫,对应的通过购买高质量的代理ip的方式能够解决问题7.其它方式反爬(自定义字体来反爬 通过css反爬)

48,多线程 多进程 线程池 协程池爬虫
参考爬虫课件. 多线程爬虫里面主要python解释器有个GIL锁的存在# 多线程爬虫思路 threadingqueue 这两个模块 如何实现多线程爬虫 1. 创建 URL队列, 响应队列, 数据队列 在init方法中 2. 在生成URL列表中方法中,把URL添加URL队列中 3. 在请求页面的方法中,从URL队列中取出URL执行,把获取到的响应数据添加响应队列中 4. 在处理数据的方法中,从响应队列中取出页面内容进行解析, 把解析结果存储数据队列中 5. 在保存数据的方法中, 从数据队列中取出数据,进行保存 6. 开启几个线程来执行上面的方法# 多进程爬虫 多进程中要使用multiprocessing.JoinableQueue 因为普通队列无法实现多进程间通信 把原来的threading.Thread修改为multiprocessing.Process 把queue.Queue修改为multiprocessing.JoinableQueue queue = multiprocessing.JoinableQueue() # 多进程中创建队列# 线程池爬虫 from multiprocessing.dummy import Pool # 导入线程池 p.apply_async(func, ('任务1', ))# 执行异步任务 from queue import Queue # 线程池中使用queue.Queue负责线程间的通讯把多线程版该线程池版: 去掉线程相关代码 导入线程池 from multiprocessing.dummy import Pool 在init方法中创建线程池 把run_use_more_task函数中, 使用线程池异步任务执行函数 在调用队列的join方法前,小睡一下# 协程池爬虫 from gevent importmonkey monkey.patch_all() 导入猴子补丁 from gevent.pool import Pool # 导入协程池 在协程池中使用队列, 与在线程池使用队列一样.

49,linux 多路复用的理解
1. IO多路复用是指内核一旦发现进程指定的一个或者多个IO条件准备读取,它就通知该进程。IO多路复用适用如下场合: 当客户处理多个描述符时(一般是交互式输入和网络套接口),必须使用I/O复用。 当一个客户同时处理多个套接口时,而这种情况是可能的,但很少出现。 如果一个TCP服务器既要处理监听套接口,又要处理已连接套接口,一般也要用到I/O复用。 如果一个服务器即要处理TCP,又要处理UDP,一般要使用I/O复用。 如果一个服务器要处理多个服务或多个协议,一般要使用I/O复用。2. 与多进程和多线程技术相比,I/O多路复用技术的最大优势是系统开销小,系统不必创建进程/线程,也不必维护这些进程/线程,从而大大减小了系统的开销3. 目前支持I/O多路复用的系统调用有 select,pselect,poll,epoll,I/O多路复用就是通过一种机制,一个进程可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但select,pselect,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间。

50,async/await 异步IO
asyncio是Python 3.4版本引入的标准库,直接内置了对异步IO的支持,用asyncio提供的@asyncio.coroutine可以把一个generator标记为coroutine(协程)类型,然后在coroutine(协程)内部用yield from调用另一个coroutine(协程)实现异步操作Python从3.5版本开始为asyncio提供了async和await的新语法; import asyncio@asyncio.coroutine def hello(): print("Hello world!") # 异步调用asyncio.sleep(1): r = yield from asyncio.sleep(1) print("Hello again!")# 获取EventLoop: loop = asyncio.get_event_loop() # 执行coroutine loop.run_until_complete(hello()) loop.close()异步操作需要在coroutine中通过yield from完成; 多个coroutine可以封装成一组Task然后并发执行。

51, select、poll、epoll之间的区别, 考验在linux开发中,服务器端处理请求的选择问题
http://www.cnblogs.com/Anker/p/3265058.html
https://www.jianshu.com/p/dfd940e7fca2
https://blog.csdn.net/chen8238065/article/details/48315085
其实所有的I/O都是轮询的方法,只不过实现的层面不同罢了.1.select和epoll这两个机制都I/O复用的解决方案,select为POSIX标准中的,而epoll为Linux所特有 2.select,poll,epoll都是IO多路复用的机制。I/O多路复用就通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作 3.但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的 4.而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间 5.边缘触发是指每当状态变化时发生一个 io 事件,条件触发是只要满足条件就发生一个 io 事件基本上select有3个缺点: 1)连接数受限, poll链表方式解决 2)查找配对速度慢, epoll只有活跃可用的FD才会调用callback函数 3)数据由内核拷贝到用户态,epoll内存拷贝,利用mmap()文件映射内存加速与内核空间的消息传递 poll改善了第一个缺点 epoll改了三个缺点.# select 基本原理 select 函数监视的文件描述符分3类,分别是writefds、readfds、和exceptfds。调用后select函数会阻塞,直到有描述符就绪(有数据 可读、可写、或者有except),或者超时(timeout指定等待时间,如果立即返回设为null即可),函数返回。当select函数返回后,可以通过遍历fdset,来找到就绪的描述符本质 select本质上是通过设置或者检查存放fd标志位的数据结构来进行下一步处理优点 其良好跨平台支持 缺点 1)单个进程所打开的FD是有一定限制的,它由FD_SETSIZE设置,默认值是1024。(poll没有最大链接数的限制) 2)对socket进行扫描时是线性扫描,即采用轮询的方法,效率较低 (epoll避免了轮询) 3)需要维护一个用来存放大量fd的数据结构,这样会使得用户空间和内核空间在传递该结构时复制开销大# poll poll本质上和select没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态,如果设备就绪则在设备等待队列中加入一项并继续遍历,如果遍历完所有fd后没有发现就绪设备,则挂起当前进程,直到设备就绪或者主动超时,被唤醒后它又要再次遍历fd。这个过程经历了多次无谓的遍历优点 没有最大连接数的限制,原因是它是基于链表来存储的缺点 - 大量的fd的数组被整体复制于用户态和内核地址空间之间,而不管这样的复制是不是有意义。 - poll还有一个特点是“水平触发”,如果报告了fd后,没有被处理,那么下次poll时会再次报告该fd。注意 select和poll都需要在返回后,通过遍历文件描述符来获取已经就绪的socket。事实上,同时连接的大量客户端在一时刻可能只有很少的处于就绪状态,因此随着监视的描述符数量的增长,其效率也会线性下降# epoll ,其中tornado使用的就是epoll的 epoll使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的copy只需一次基本原理 - epoll支持水平触发和边缘触发,最大的特点在于边缘触发,它只告诉进程哪些fd刚刚变为就绪态,并且只会通知一次 - epoll使用“事件”的就绪通知方式,通过epoll_ctl注册fd,一旦该fd就绪,内核就会采用类似callback的回调机制来激活该fd,epoll_wait便可以收到通知。优点 - 没有最大并发连接的限制 - 效率提升,不是轮询的方式,不会随着FD数目的增加效率下降。只有活跃可用的FD才会调用callback函数;即Epoll最大的优点就在于它只管你“活跃”的连接,而跟连接总数无关 - 内存拷贝,利用mmap()文件映射内存加速与内核空间的消息传递;即epoll使用mmap减少复制开销

52,边缘触发 水平触发的区别
边缘触发是指每当状态变化时发生一个 io 事件,
条件触发是只要满足条件就发生一个 io 事件
53, python网络编程-epoll模型详解 https://www.cnblogs.com/maociping/p/5132583.html
# 内核epoll模型讲解 首先我们来定义流的概念,一个流可以是文件,socket,pipe等可以进行I/O操作的内核对象。不管是文件,还是套接字(socket),还是管道(pipe),我们都可以把他们看作流。之后我们来讨论I/O操作,通过read,我们可以从流中读入数据;通过write,我们可以往流中写入数据。现在假定1种情形,我们需要从流中读数据,但是流中还没有数据,(典型的例子为,客户端要从socket读数据,但是服务器端还没有把数据传回来),这时候该怎么办?阻塞:阻塞是个什么概念呢?比如某个时候你在等快递,但是你还不知道快递什么时候过来,而且你也没有别的事可以干(或者说接下来的事要等快递来了才能做);那么你可以去睡觉了,因为你知道快递把货送来时一定会给你打电话(假定一定能叫醒你)。非阻塞忙轮询:接着上面等快递的例子,如果用忙轮询的方法,那么你需要知道快递员的手机号,然后每分钟给他打个电话:“你到了没?”很明显一般人不会用第二种做法,不仅显得无脑,浪费话费不说,还占用了快递员大量的时间。大部分程序也不会用第二种做法,因为第一种方法经济而简单,经济是指消耗很少的CPU时间,如果线程睡眠了,就掉出了系统的调度队列,暂时不会去瓜分CPU宝贵的时间片。为了了解阻塞是如何进行的,我们来讨论缓冲区,以及内核缓冲区,最终把I/O事件解释清楚。缓冲区的引入是为了减少频繁I/O操作而引起频繁的系统调用(你知道它很慢的),当你操作一个流时,更多的是以缓冲区为单位进行操作,这是相对于用户空间而言。对于内核来说,也需要缓冲区。假设有一个管道,进程A为管道的写入方,B为管道的读出方。假设一开始内核缓冲区是空的,B作为读出方,被阻塞着。然后首先A往管道写入,这时候内核缓冲区由空的状态变到非空状态,内核就会产生一个事件告诉B该醒来了,这个事件姑且称之为“缓冲区非空”。但是“缓冲区非空”事件通知B后,B却还没有读出数据;且内核许诺了不能把写入管道中的数据丢掉这个时候,A写入的数据会滞留在内核缓冲区中,如果内核也缓冲区满了,B仍未开始读数据,最终内核缓冲区会被填满,这个时候会产生一个I/O事件,告诉进程A,你该等等(阻塞)了,我们把这个事件定义为“缓冲区满”。假设后来B终于开始读数据了,于是内核的缓冲区空了出来,这时候内核会告诉A,内核缓冲区有空位了,你可以从长眠中醒来了,继续写数据了,我们把这个事件叫做“缓冲区非满”。也许事件Y1已经通知了A,但是A也没有数据写入了,而B继续读出数据,知道内核缓冲区空了。这个时候内核就告诉B,你需要阻塞了!,我们把这个时间定为“缓冲区空”。这四种情形涵盖了四个I/O事件,内核缓冲区满,内核缓冲区空,内核缓冲区非空,内核缓冲区非满。这四个I/O事件是进行阻塞同步的根本。(如果不能理解“同步”是什么概念,请学习操作系统的锁,信号量,条件变量等任务同步方面的相关知识)。然后我们来说说阻塞I/O的缺点。但是阻塞I/O模式下,一个线程只能处理一个流的I/O事件。如果想要同时处理多个流,要么多进程(fork),要么多线程(pthread_create),很不幸这两种方法效率都不高。于是再来考虑非阻塞忙轮询的I/O方式,我们发现可以同时处理多个流(把一个流从阻塞模式切换到非阻塞模式再此不予讨论):1 while true { 2for i in stream[]; { 3if i has data 4read until unavailable 5} 6 } 我们只要不停的把所有流从头到尾问一遍,又从头开始。这样就可以处理多个流了,但这样的做法显然不好,因为如果所有的流都没有数据,那么只会白白浪费CPU。这里要补充一点,阻塞模式下,内核对于I/O事件的处理是阻塞或者唤醒,而非阻塞模式下则把I/O事件交给其他对象(后文介绍的select以及epoll)处理甚至直接忽略。为了避免CPU空转,可以引进一个代理(一开始有一位叫做select的代理,后来又有一位叫做poll的代理,不过两者的本质是一样的)。这个代理比较厉害,可以同时观察许多流的I/O事件,在空闲的时候,会把当前线程阻塞掉,当有一个或多个流有I/O事件时,就从阻塞态中醒来,于是我们的程序就会轮询一遍所有的流(于是我们可以把“忙”字去掉了)。代码长这样: 1 while true { 2select(streams[]) 3for i in streams[] { 4if i has data 5read until unavailable 6} 7 } 于是,如果没有I/O事件产生,我们的程序就会阻塞在select处。但是依然有个问题,我们从select那里仅仅知道了,有I/O事件发生了,但却并不知道是那几个流(可能有一个,多个,甚至全部),我们只能无差别轮询所有流,找出能读出数据,或者写入数据的流,对他们进行操作。但是使用select,我们有O(n)的无差别轮询复杂度,同时处理的流越多,每一次无差别轮询时间就越长。再次说了这么多,终于能好好解释epoll了。 epoll可以理解为event poll,不同于忙轮询和无差别轮询,epoll只会把哪个流发生了怎样的I/O事件通知我们。此时我们对这些流的操作都是有意义的(复杂度降低到了O(1))。 在讨论epoll的实现细节之前,先把epoll的相关操作列出:1 epoll_create创建一个epoll对象,一般epollfd = epoll_create() 2 epoll_ctl (epoll_add/epoll_del的合体),往epoll对象中增加/删除某一个流的某一个事件 3比如 4 epoll_ctl(epollfd, EPOLL_CTL_ADD, socket, EPOLLIN); //注册缓冲区非空事件,即有数据流入 5 epoll_ctl(epollfd, EPOLL_CTL_DEL, socket, EPOLLOUT); //注册缓冲区非满事件,即流可以被写入 6 epoll_wait(epollfd,...)等待直到注册的事件发生 7 (注:当对一个非阻塞流的读写发生缓冲区满或缓冲区空,write/read会返回-1,并设置errno=EAGAIN。而epoll只关心缓冲区非满和缓冲区非空事件)。

54,epoll代码
import select epoll = select.epoll() #创建一个epoll对象epoll.register(文件句柄, 事件类型) #注册要监控的文件句柄和事件事件类型: select.EPOLLIN 可读事件 select.EPOLLOUT 可写事件 select.EPOLLERR 错误事件 select.EPOLLHUP 客户端断开事件:epoll.unregister(文件句柄) # 销毁文件句柄 epoll.poll(timeout) #当文件句柄发生变化,则会以列表的形式主动报告给用户进程, timeout为超时时间,默认为 - 1,即一直等待直到文件句柄发生变化,如果指定为1 那么epoll每1秒汇报一次当前文件句柄的变化情况,如果无变化则返回空 epoll.fileno() # 返回epoll的控制文件描述符(Return epoll.modfiy(fineno, event) # fineno为文件描述符event为事件类型作用是修改文件描述符所对应的事件 epoll.fromfd(fileno) #从1个指定的文件描述符创建1个epoll对象 epoll.close()#关闭epoll对象的控制文件描述符

55,epoll代码(服务端接收客户端数据并返回)
import socket import select import queue# 创建socket对象 serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 设置IP地址复用 serversocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)server_address = ("127.0.0.1", 8888) serversocket.bind(server_address) serversocket.listen(10) # 服务端设置非阻塞 serversocket.setblocking(False) # 超时时间 timeout = 10 # 创建epoll事件对象,后续要监控的时间添加到其中 epoll = select.epoll() # 注册服务器监听fd到等待读事件集合 epoll.register(serversocket.fileno(), select.EPOLLIN) # 保存链接客户端消息的字典,格式为{} message_queues = {} # 文件句柄到所对应的字典, 格式为{句柄: 对象} fd_to_socket = {serversocket.fileno(): serversocket, }while True: print("等待活动链接")events = epoll.poll(timeout)# 轮询注册的时间集合, 返回值为[(文件句柄, 对应的事件)] if not events: print("epoll超时无活动连接, 重写轮询") continue print("有", len(events), "个新事件, 开始处理")for fd, event in events: socket = fd_to_socket[fd] if socket == serversocket:# 如果活动socket为当前服务器socket, 表示有新连接 connection, address = serversocket.accept() print("开始连接") connection.setblocking(False)# 新连接socket设置为非阻塞 epoll.register(connection.fileno(), select.EPOLLIN)# 注册新连接到fd到等待读事件集合 fd_to_socket[connection.fileno()] = connection# 把新连接的文件句柄以及对象保存到字典 message_queues[connection] = queue.Queue()# 以新连接的对象为键值, 值存储在队列总, 保存每个连接的信息# 关闭事件 elif event & select.EPOLLHUP: print("client close") epoll.unregister(fd)# 在epoll中注销客户端的文件句柄 fd_to_socket[fd].close()# 关闭客户端的文件句柄 del fd_to_socket[fd]# 在字典中删除与已关闭客户端相关的信息# 可读事件 elif event & select.EPOLLIN: data = socket.recv(1024)# 接收数据 if data: print("接收到客户端数据:", data, "客户端为:", socket.getpeername()) message_queues[socket].put(data)# 将数据放入对应客户端的字典 epoll.modify(fd, select.EPOLLOUT)# 修改读取到消息的连接到等待写事件集合# 可写事件 elif event & select.EPOLLOUT: try: msg = message_queues[socket].get_nowait()# 从字典中获取对应客户端的信息except queue.Empty: print(socket.getpeername(), "queue empty") epoll.modify(fd, select.EPOLLIN)# 修改文件句柄为读书剑else: print("发送数据", data, "客户端", socket.getpeername()) socket.send(msg)epoll.unregister(serversocket.fileno())# 在epoll中注销服务端文件句柄 epoll.close()# 关闭epoll serversocket.close()# 关闭服务器socket

    推荐阅读