大纲
1.什么是OSI开放系统互连
2.OSI七层模型各层的作用
3.TCP/IP协议的简介
4.TCP和UDP的简介
5.TCP连接的三次握手
6.TCP连接的四次挥手
7.TCP/IP中的数据包
8.TCP通过确认应答与序列号提高可靠性
9.HTTP请求的传输过程
10.HTTP协议报文结构
11.Socket、短连接、长连接、网络IO流程
12.Linux的IO模型详情
13.Linux的IO模型核心
14.select、poll、epoll的区别
15.水平触发和边缘触发
1.什么是OSI开放系统互连
OSI是Open System Interconnect的简称,即开放系统互连参考模型。OSI为开放系统的计算机互连提供了一个共同的基础和标准框架,并为保持相关标准的一致性和兼容性提供了共同的参考。
OSI采用了分层的结构化技术,共分七层,分别是:应用层、表示层、会话层、传输层、网络层、数据链路层、物理层。
(应表会传网数物)
2.OSI七层模型各层的作用
(1)物理层的简介
(2)数据链路层的简介
(3)网络层的简介
(4)传输层的简介
(5)会话层的简介
(6)表示层的简介
(7)应用层的简介
(1)物理层的简介
物理层并不是指物理媒体本身,而是指开放系统中利用物理媒体实现物理连接的功能描述,物理层的媒体包括架空明线、平衡电缆、光纤、无线信道等。
物理层的主要功能如下:
功能一:为数据端设备提供传送数据的通路
数据通路可以是一个物理媒体,也可以是多个物理媒体连接而成。一次完整的数据传输,包括激活物理连接、传送数据和终止物理连接。激活就是在通信的两个数据终端设备之间建立起连接,形成一条通路。
功能二:传输数据
物理层如果要形成适合数据传输所需的实体来为数据传输进行服务,那么一要保证数据能在其上正确通过,二要提供足够的带宽来减少信道上的拥塞,其中带宽指的是每秒能通过的比特(Bit)数。此外数据传输的方式应该满足如下几种情况的需要:点到点、一点到多点、串行或并行、半双工或全双工、同步或异步。
(2)数据链路层的简介
数据链路层可以粗略理解为数据通道,因为物理层已经为终端设备间的数据通信提供传输介质及其连接。其中介质是长期的,连接是有生存期的。在连接的生存期内,收发两端可以进行不等的一次或多次数据通信。每次通信都要经过建立通信联络和拆除通信联络两个过程。这种建立起来的数据收发关系就叫做数据链路。
在物理媒体上传输的数据难免受到各种不可靠因素的影响而产生差错。为弥补物理层不足,为上层提供无差错的数据传输,需要对数据检错和纠错。
数据链路层应具备如下功能:
一.链路连接的建立、拆除和分离
二.差错检测和恢复、链路标识、流量控制等
三.独立的链路产品中最常见的当属网卡,网桥也是一种链路产品
(3)网络层的简介
当数据终端增多时,它们之间会有中继设备进行相连,此时会出现一台终端要求不只是与惟一一台而是能和多台终端通信的情况。这就产生了把任意两台数据终端设备的数据链接起来的问题,也就是路由。
另外,当一条物理信道建立后被一对用户使用,往往有许多空闲时间被浪费。这时候为了避免浪费,自然就会希望能够让多对用户共用一条链路。所以为了解决这一问题,于是就出现了逻辑信道技术和虚拟电路技术。
(4)传输层的简介
有一个既定的事实,即世界上各种通信子网在性能上存在着很大差异。如电话交换网、分组交换网、公用数据交换网、局域网等通信子网都可互连,但它们提供的吞吐量、传输速率、数据延迟等数据传输却是各不相同。
而对于会话层来说,却要求有一性能恒定的界面,所以传输层的作用就是:在终端用户之间提供透明的数据传输,向上层提供可靠的数据传输服务。
(5)会话层的简介
会话层就是会话单位的控制层,其主要作用是:按照在应用进程间约定的原则、按正确顺序收发数据,进行各种形态的对话。
会话层规定了会话服务期间,用户间的会话连接建立、拆除和数据传送流程。会话层提供的服务是应用建立和维持会话,并能使会话获得同步。会话层使用校验点可使通信会话在通信失效时从校验点继续恢复通信,这种能力对于传送大的文件极为重要。
(6)表示层的简介
表示层的主要作用是把应用层提供的信息变换为能够共同理解的形式,提供字符代码、数据格式、控制信息格式、加密等的统一表示。表示层的作用之一是为异种机通信提供一种公共语言,以便能进行互操作。
之所以需要表示层,是因为不同的计算机体系结构使用的数据表示法不同。例如,IBM主机使用EBCDIC编码,而大部分PC机使用的是ASCII码。在这种情况下,便需要表示层来完成这种转换。
(7)应用层的简介
应用层的主要作用就是向应用程序提供服务,这些服务按其向应用程序提供的特性分成组,并称为服务元素。有些可以为多种应用程序共同使用,有些则只能为较少的一类应用程序使用。应用层是开放系统的最高层,是直接为应用进程提供服务的。在实现多个系统应用进程相互通信的同时,完成一系列业务处理所需要的服务。
3.TCP/IP协议的简介
TCP/IP是Transmission Control Protocol/Internet Protocol的简写,TCP/IP的字面上的意思是传输控制协议/因特网互联协议。TCP/IP协议由网络层的IP协议和传输层的TCP协议组成,采用的是4层模型。在很多情况下,TCP/IP协议是利用IP进行通信时所用到的协议群的统称。常说的TCP/IP协议通常是指TCP/IP协议族,而不仅仅是TCP协议和IP协议。
通俗而言:TCP负责发现传输的问题,一旦发现有问题就发出信号要求重新传输。所以TCP可以保证所有数据安全正确地传输到目的地,IP则是给因特网的每一台联网设备规定一个地址。
4.TCP和UDP的简介
(1)TCP和UDP简单对比
(2)TCP的简介
(3)UDP的简介
(4)TCP和UDP的优缺点
(5)传输层中的端口号
(6)端口号的确定
(1)TCP和UDP简单对比
TCP是面向连接的、可靠的流协议。TCP通过三次握手建立连接,通讯完成时需要拆除连接。
UDP是面向无连接的通讯协议。UDP通讯时不需要接收方进行确认,属于不可靠的传输,因此可能会出现丢包的现象。
(2)TCP的简介
TCP是面向连接的通信协议,通过三次握手建立连接,通讯完成时拆除连接。由于TCP是面向连接的,所以只能用于端到端的通讯。
TCP提供的是一种可靠的数据流服务,采用带重传的肯定确认技术来实现传输的可靠性。
TCP还采用一种称为滑动窗口的方式进行流量控制,所谓窗口实际表示接收能力,用以限制发送方的发送速度。
如果IP数据包中有已封好的TCP数据包,那么IP将把它们向上传送到TCP。TCP会将数据包进行排序以及进行错误检查,同时实现虚电路间的连接。TCP数据包中包括序号和确认,所以收到的包可被排序,损坏的包可被重传。
TCP将它的信息送到更高层的应用程序,例如Telnet的服务程序和客户程序,而更高层的应用程序又会将信息送回给TCP层。TCP层便将它们向下传送到IP层、设备驱动程序和物理介质,最后到接收方。面向连接的服务(如Telnet、FTP、SMTP)需要高度的可靠性,所以使用TCP。
(3)UDP的简介
UDP是面向无连接的通讯协议,UDP数据包括目的端口号和源端口号。由于基于UDP的通讯不需要连接,所以UDP可以实现广播发送。
UDP通讯时不需要接收方确认,属于不可靠的传输,可能会出现丢包现象。所以实际应用中采用UDP进行通讯时,需要程序员编程验证。
UDP与TCP位于同一层,但它不管数据包的顺序、错误或重发。因此,UDP不被应用于那些使用虚电路的面向连接的服务。UDP主要用于那些面向查询应答的服务,例如NFS。相对于FTP或Telnet,这些服务需要交换的信息量较小。
使用UDP的服务包括:
一.NTP(网络时间协议)和DNS(DNS也使用TCP),包总量较少的通信
二.视频、音频等多媒体通信(即时通信)
三.限定于LAN等特定网络中的应用通信
四.广播通信(广播、多播)
(4)TCP和UDP的优缺点
TCP主要用于在传输层必须要实现可靠传输的情况,UDP主要用于需要高速传输、对实时性要求较高的通信或广播通信。
(5)传输层中的端口号
数据链路中的地址和IP中的地址,分别指的是MAC地址和IP地址。MAC地址用来识别同一链路中不同的计算机,IP地址用来识别TCP/IP网络中互连的主机和路由器。
在传输层也有这种类似于地址的概念,那就是端口号。端口号用来识别同一台计算机中可以进行通信的不同应用程序。因此,端口号也被称为程序地址。
一台计算机上同时可以运行多个程序,传输层协议正是利用端口号识别正在进行通信的应用程序,并准确传输数据。
MAC地址(数据链路层):识别同一链路中不同的计算机,可以理解为网卡的地址。
IP地址(传输层/网络层):识别TCP/IP网络中互连的主机和路由器。
程序地址或端口号(传输层):识别同一台计算机中进行通信的不同应用程序。
数据报文的构成(识别一个通信的5个元素)如下:源IP地址、目标IP地址、协议号、源端口号、目标端口号、数据。
(6)端口号的确定
标准既定的端口号:
这种方法也叫静态方法,它是指每个应用程序都有其指定端口号,但并不是可随意使用任一端口号。例如HTTP、FTP等广为使用的应用协议中所使用的端口号就是固定的。这些端口号被称为知名端口号,分布在0~1023之间。除知名端口号之外,还有一些端口号被正式注册。它们分布在1024~49151之间,不过这些端口号可用于任何通信用途。
时序分配法:
服务端有必要确定监听端口号,但接受服务的客户端没必要确定端口号。客户端的应用程序可以不用自己设置端口号,而交给操作系统进行分配,动态分配的端口号范围是49152 ~ 65535。
5.TCP连接的三次握手
TCP提供面向有连接的通信传输,面向有连接是指在数据通信开始之前先做好两端之间的准备工作。
所谓三次握手是指:建立一个TCP连接时,需要客户端和服务器端发送三个包以确认连接建立。在Socket编程中,三次握手的过程由客户端执行connect()方法来触发。
第一次握手:
首先客户端将标志位SYN设置为1,并随机产生一个值seq=x。然后将SYN=1,ACK=0,seq=x封装成数据包发送给服务器端,发起连接请求。接着客户端进入SYN_SENT状态,等待服务器端确认。
第二次握手:
服务器端收到数据包后,通过标志位SYN=1知道客户端在请求建立连接。于是将标志位SYN和ACK设置为1,值ack=x+1,并随机产生一个值seq=y。然后将SYN=1,ACK=1,ack=x+1,seq=y封装成数据包发送给客户端,确认连接请求。接着服务器端进入SYN_RCVD状态。
第三次握手:
客户端收到服务器端返回的确认后,检查ack是否为x+1,ACK是否为1。如果正确则将标志位ACK设置为1,值ack=y+1,并将该数据包发送给服务器端。服务器端收到数据包后检查ack是否为y+1,ACK是否为1。如果是则表明连接建立成功,客户端和服务器端进入ESTABLISHED状态。从而完成三次握手,之后客户端与服务器端就可以开始传输数据了。
6.TCP连接的四次挥手
所谓四次挥手是指:断开一个TCP连接时,需要客户端和服务端发送四个包以确认连接断开。在Socket编程中,这一过程由客户端或服务端任一方执行close()方法触发。
由于TCP连接是全双工的,因此每个方向都必须要单独进行关闭。也就是当一方完成数据发送任务后,发送一个FIN来终止这一方向的连接。收到一个FIN只是意味着这一方向上没有数据流动了,即不会再收到数据。但是在这个TCP连接上仍然能够发送数据,直到这一方向也发送了FIN。首先进行关闭的一方将执行主动关闭,而另一方则执行被动关闭。
第一次挥手:
客户端发出断开连接的报文,并且停止发送数据。在断开连接的报文中,FIN=1,序列号seq=u。这个序列号u等于前面已经传送过来的数据的最后一个字节的序号加1,此时客户端会进入FIN-WAIT-1(终止等待1)状态。TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。
第二次挥手:
服务器收到断开连接的报文,发出确认报文。在确认报文中,ACK=1,ack=u+1,序列号seq=v。此时服务端就进入了CLOSE-WAIT(关闭等待)状态。这时处于半关闭状态,即客户端已没有数据要发送了。但如果服务器若发送数据给客户端,客户端依然需要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。
客户端收到服务器的确认报文后,就会进入FIN-WAIT-2(终止等待2)状态,等待服务器发送断开连接的报文。在收到服务器的断开连接报文之前,还需要接收服务器发送的最后的数据。
第三次挥手:
服务器将最后的数据发送完毕后,会向客户端发送断开连接的报文。由于在半关闭状态下,服务器很可能又发送了一些数据。在断开连接的报文中,FIN=1,ack=u+1,序列号为seq=w。此时服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。
第四次挥手:
客户端收到服务器的断开连接报文后,必须发出确认。在确认报文中,ACK=1,ack=w+1,序列号seq=u+1。此时客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2∗MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。
服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。
7.TCP/IP中的数据包
包是全能性术语,帧用于表示数据链路层中包的单位,片是IP中数据的单位,段则表示TCP数据流中的信息,消息是指应用协议中数据的单位。
每个分层中,都会对所发送的数据附加一个首部。在这个首部中会包含该层必要的信息,如发送的目标地址和协议相关信息。通常,为协议提供的信息为包首部,所要发送的内容为数据。在下一层的角度看,从上一层收到的包全部都被认为是本层的数据。
网络中传输的数据包由两部分组成:一部分是协议所要用到的首部,另一部分是上一层传过来的数据。首部的结构由协议的具体规范详细定义。在数据包的首部,明确标明了协议应该如何读取数据。反过来说,看到首部,也就能够了解该协议必要的信息和所要处理的数据。
上图客户端发送的包到达服务端后,每一层的流转处理流程如下:
一.应用程序处理
首先应用程序会进行编码处理,这些编码相当于OSI的表示层功能。编码转化后,邮件不一定马上被发送出去。这种何时建立通信连接何时发送数据的管理功能,相当于OSI的会话层功能。
二.TCP模块的处理
TCP根据应用的指示,负责建立连接、发送数据以及断开连接,TCP提供将应用层发来的数据顺利发送至对端的可靠传输。为了实现这一功能,需要在应用层数据的前端附加一个TCP首部。
三.IP模块的处理
IP将TCP传过来的TCP首部和TCP数据合起来当做自己的数据,并在TCP首部的前端加上自己的IP首部。IP包生成后,参考路由控制表决定接受此IP包的路由或主机。
四.网络接口(以太网驱动)的处理
从IP传过来的IP包对于以太网来说就是数据,给这些数据附加上以太网首部并进行发送处理,生成的以太网数据包将通过物理层传输给接收端。
五.网络接口(以太网驱动)的处理
主机收到以太网包后,首先从以太网包首部找到MAC地址判断是否为发送给自己的包。如果不是则丢弃数据,如果是则从以太网包首部中的类型确定数据类型。确定数据类型后,再将数据包传给相应的模块,如 IP、ARP等。
六.IP模块的处理
IP模块接收到数据后也做类似的处理,从数据包首部中判断此IP地址是否与自己的IP地址匹配。如果匹配则根据首部的协议类型将数据发送给对应的模块,如TCP/UDP。另外对于有路由器的情况,接收端地址往往不是自己的地址。此时需要借助路由控制表,查出应发送的主机或路由器之后再进行数据转发。
七.TCP模块的处理
在TCP模块中,首先会计算一下校验和,判断数据是否被破坏。然后检查是否在按照序号接收数据。最后检查端口号,确定具体的应用程序。数据被完整地接收以后,会传给由端口号识别的应用程序。
八.应用程序的处理
接收端应用程序会直接接收发送端发送的数据,通过解析数据,展示相应的内容。
8.TCP通过确认应答与序列号提高可靠性
TCP是如何提高可靠性:确认应答、序列号。开发时可借鉴TCP提高可靠性的方法来提高程序的可靠性。
在TCP中,当发送端发出的数据到达接收端的主机时,接收端主机会返回一个已收到消息的通知,这个消息叫做确认应答(ACK)。
当发送端将数据发出之后会等待接收端的确认应答(ACK)。如果有确认应答,说明数据已经成功到达接收端。反之,则数据丢失的可能性很大。如果发送端在一定时间内没等到接收端的确认应答,则认为数据丢失要重发。因此即使出现丢包,发送端仍能保证数据能到达接收端,从而实现可靠传输。
发送端未收到确认应答并不意味着数据一定丢失,也有可能是接收端已收到数据,但是返回的确认应答在途中丢失了,这种情况也会导致发送端误以为数据没有到达接收端而重发数据。
对于接收端来说,反复收到相同的数据是不可取的。为了对上层应用提供可靠的传输,接收端必须放弃重复的数据包。因此引入了序列号,即按顺序给发送数据的每一个字节都标上号码的编号。接收端会查询收到的TCP数据包首部中的序列号和数据长度,将自己下一步应该接收的序列号作为确认应答发送回去。
通过序列号和确认应答号,TCP既能够识别是否已经接收到数据,又能够判断是否需要接收数据,从而实现可靠的传输。
9.HTTP请求的传输过程
(1)HTTP请求的传输过程介绍
(2)一次完整的HTTP请求的8个过程
(3)建立TCP连接
(4)客户端向服务器发送请求行
(5)客户端发送请求头信息
(6)客户端发送请求数据
(7)服务器向客户端返回响应行
(8)服务器返回响应头信息
(9)服务器返回响应数据
(10)服务器关闭TCP连接
(1)HTTP请求的传输过程介绍
首先作为发送端的客户端在应用层(HTTP协议)发出一个HTTP请求,然后在传输层(TCP协议)把从应用层处收到的数据(HTTP请求报文)进行分割,并在各个报文上打上标记序号及端口号后转发给网络层,接着在网络层(IP协议)增加作为通信目的地的MAC地址后转发给链路层。这样一来,发往网络的通信请求就准备齐全了。
接收端的服务器在链路层接收到数据,按序往上层发送,一直到应用层。当数据传输到应用层时,才算真正接收到由客户端发送过来的HTTP请求。
(2)一次完整的HTTP请求的8个过程
一.建立TCP连接
二.客户端向服务器发送请求行
三.客户端发送请求头信息
四.服务器向客户端返回响应行
五.服务器返回响应头信息
六.服务器向客户端发送响应数据
七.服务器关闭TCP连接
(3)建立TCP连接
HTTP连接是无状态的,建立前需要进行3次握手。连接在HTTP工作开始前,客户端首先要通过网络与服务器建立连接。该连接是通过TCP来完成的,该协议与IP协议共同构建Internet。即著名的TCP/IP协议族,因此Internet又被称作是TCP/IP网络。
HTTP是比TCP更高层次的应用层协议。根据规则,只有低层协议建立之后,才能进行高层协议的连接。因此,首先要建立TCP连接,一般TCP连接的端口号是80。
(4)客户端向服务器发送请求行
一旦建立了TCP连接,客户端就会向服务器发送请求行。例如:GET/sample/hello.html HTTP/1.1。
(5)客户端发送请求头信息
客户端发送请求命令后,便会向服务器发送请求头信息。之后客户端发送一空白行给服务器,表示结束请求头信息的发送。
(6)客户端发送请求数据
接着以Content-Type请求头信息所描述的格式发送请求数据。
(7)服务器向客户端返回响应行
服务器收到客户端发来的请求后,将会向客户端返回响应行。例如HTTP/1.1 200 OK,响应结果的第一部分是协议版本号和响应状态码。
(8)服务器返回响应头信息
服务器返回响应后,便会向客户端返回响应头信息,其中会发送一个空白行来表示响应头信息的发送到此为结束。
(9)服务器返回响应数据
接着以Content-Type响应头信息所描述的格式返回响应数据。
(10)服务器关闭TCP连接
一般情况下,一旦服务器向客户端返回了请求数据,它就要关闭TCP连接。如果客户端或服务器在其头信息加入了Connection:keep-alive,那么TCP连接在发送后将仍然保持打开状态,于是客户端可以继续通过相同的连接发送请求。保持TCP连接可以节省为每个请求建立新连接的时间,以及节约网络带宽。
10.HTTP协议报文结构
用于HTTP协议交互的信息被称为HTTP报文。请求端(客户端)的HTTP报文叫做请求报文,响应端(服务器端)的叫做响应报文。HTTP报文本身是由多行(用CR+LF作换行符)数据构成的字符串文本。HTTP报文大致可分为报文首部和报文主体两部分。两者由最初出现的空行(CR+LF)来划分,通常并不一定有报文主体。
一.请求报文结构
请求报文的首部内容由以下数据组成:请求行——包含用于请求的方法、请求URI和HTTP版本,请求头——包含表示请求的各种条件和属性的各类首部。比如通用首部、请求首部、实体首部以及RFC里未定义的首部如Cookie等。
二.响应报文结构
响应行——包含表明响应结果的状态码、原因短语和HTTP版本,响应头——包含表示请求的各种条件和属性的各类首部。比如通用首部、响应首部、实体首部以及RFC里未定义的首部如Cookie等。
11.Socket、短连接、长连接、网络IO流程
(1)Socket
(2)短连接和长连接
(3)网络IO读写的流程
(1)Socket
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。Socket使用门面模式把复杂的TCP/IP协议族隐藏在Socket接口后面。
主机A的应用程序要能和主机B的应用程序通信,必须通过Socket建立连接。而建立Socket连接必须需要底层TCP/IP协议来建立TCP连接,建立TCP连接需要底层IP协议来寻址网络中的主机。
网络层使用的IP协议可以根据IP地址来找到目标主机,但是一台主机上可能运行着多个应用程序,为了与指定的应用程序通信,还要指定TCP或UPD的地址也就是端口号。
下面是一对TCP客户端与服务端进程之间的交换流程时间表。服务端首先启动,稍后某个时刻客户端启动,它试图连接到服务端。假设客户端给服务端发送一个请求,服务端处理该请求后返回一个响应。这个过程一直持续下去,直到客户端关闭连接。
客户端和服务端都从调用socket()方法开始,它会返回一个套接字描述符。随后客户端调用connect()方法,服务端则调用bind()、listen()和accept()。客户端和服务端的数据交互则通过调用read()和write()方法来实现。关闭连接时,则通过调用close()方法来实现。
(2)短连接和长连接
短连接的过程:连接 -> 传输数据 -> 关闭连接。浏览器和服务端每进行一次HTTP操作就建立一次连接,任务结束就中断连接。
短连接是指:建立Socket(TCP)连接后,发送 + 接收完数据就马上断开连接。
长连接的过程:连接 -> 传输数据 -> 保持连接 -> 传输数据 -> ... -> 关闭连接。
长连接是指:建立Socket(TCP)连接后,不管是否使用都保持连接,安全性较差。
每个TCP连接都需要三步握手,这需要时间。如果每个操作都是先连接,再操作的话那么处理速度会降低很多。所以长连接适用于操作频繁的通信,但是长连接的数量不能太多。
数据库的连接一般都用长连接。如果用短连接,那么频繁的通信会造成频繁的Socket创建,降低性能。所以,连接池一般使用的都是长连接。
网站的HTTP服务一般都用短连接。因为长连接会消耗服务端的资源,而且网站服务通常需要处理大量的用户请求。所以请求量大,但每个请求不会频繁操作的情况下,使用短连接更好。
(3)Java IO读写的底层流程
用户程序进行IO读写,基本上会用到系统调用read和write。系统调用read把数据从内核缓冲区(内核空间)复制到进程缓冲区(用户空间),系统调用write把数据从用户缓冲区(用户空间)复制到内核缓冲区(内核空间)。它们并不等价数据在内核缓冲区和磁盘之间的交换。
Java服务端处理网络请求的典型过程:
步骤一:接收客户端请求
Linux通过网卡,读取客户断的请求数据,将数据读取到内核缓冲区。
步骤二:获取请求数据
服务端从内核缓冲区读取数据到Java进程缓冲区。
步骤三:服务器端业务处理
Java服务端在自己的用户空间中处理客户端请求。
步骤四:服务器端返回数据
Java服务端已构建好的响应,从Java进程缓冲区写入内核缓冲区。
步骤五:发送数据给客户端
Linux内核通过网络IO ,将内核缓冲区中的数据,写入网卡。网卡通过底层的通讯协议,会将数据发送给目标客户端。
由于系统调用是在内核态中运行的,所以进行系统调用时,系统需要从用户态切换到内核态。系统从用户态切换到内核态的这个过程会产生性能损耗。在切换之前需要保存用户态的状态,包括寄存器、程序指令等。然后才能执行内核态的系统调用指令,最后还要恢复用户态。
用户态和内核态表示的是操作系统中的不同执行权限。两者最大的区别在于:运行在用户空间中的进程不能直接访问操作系统内核的指令和程序,运行在内核空间中的进程可以直接访问操作系统内核的指令和程序。进行权限划分是为了避免用户在进程中直接操作危险的系统指令,从而影响进程和系统的稳定。
12.Linux的IO模型详情
(1)同步和异步、阻塞和非阻塞
(2)Linux的IO模型之阻塞IO模型(同步)
(3)Linux的IO模型之非阻塞IO模型(同步)
(4)Linux的IO模型之IO复用模型(同步)
(5)Linux的IO模型之信号驱动IO(同步)
(6)Linux的IO模型之异步IO模型(异步)
(7)Linux的5个IO模型对比
(1)同步和异步、阻塞和非阻塞
同步和异步关注的是:结果的通知方式。同步是指:调用方需要主动等待结果的返回。异步是指:调用方不需要主动等待结果的返回,调用方会通过其他方式(如状态通知、回调函数等)来等待结果的返回。
阻塞和非阻塞关注的是:等待结果返回的调用方的状态。阻塞是指:结果返回之前,当前线程被挂起,不做任何事情。非阻塞是指:结果在返回之前,当前线程可以做其他事情,不会被挂起。
同步和异步关注的是:是否亲自主动获取消息,是否由其他人通知自己。
阻塞和非阻塞关注的是:当前事情还没好时是否还能做其他事情。
一.同步阻塞
同步阻塞基本也是编程中最常见的模型。比如客户去商店买衣服,去了之后发现衣服卖完了,就在店里面一直等,期间不做任何事(包括看手机),等着商家进货,直到有货为止,效率很低。
二.同步非阻塞
同步非阻塞在编程中可以抽象为一个轮询模式。客户去了商店之后,发现衣服卖完了,这个时候不需要傻傻的等着,客户可以去其他地方比如奶茶店买杯水,但是还是需要客户时不时的主动去商店问老板新衣服到了没有。
三.异步阻塞
异步阻塞这个编程里面用的较少。这类似于写了个线程池,submit()后马上future.get(),此时线程还是挂起的。客户去商店买衣服,发现衣服没有了,于是就给老板留给电话,告诉老板说衣服到了就打电话进行通知,然后就一直守着电话不做其他事情。
四.异步非阻塞
客户去商店买衣服,衣服没了,然后给老板留下电话,衣服到了就打电话通知。然后客户就可以随心所欲地去做其他事情,也不用操心衣服什么时候到。只要衣服一到,老板就会打电话进行通知,收到通知后再去买衣服即可。
(2)Linux的IO模型之阻塞IO模型(同步)
一.解析一
应用程序调用一个IO方法,导致应用程序阻塞,等待数据准备好。如果数据没有准备好,则一直等待。如果数据准备好,则将数据从内核空间拷贝到用户空间,然后返回是否成功。
二.解析二
当调用recv()方法时,系统首先查是否有准备好的数据。如果数据没有准备好,那么系统就处于等待状态。如果数据已经准备好,则将数据从内核空间复制到用户空间,然后返回。在套接应用程序中,当调用recv()方法时,未必用户空间就已经存在数据,那么此时recv()方法就会处于等待状态。
三.阻塞IO(BIO)的优点
程序简单,在阻塞等待数据期间,用户线程挂起,用户线程基本不会占用CPU资源。
四.阻塞IO(BIO)的缺点
一般情况下,需要为每个连接配套一条独立的线程,或者说一条线程维护一个连接成功的IO流的读写。在并发量小的情况下,这没什么问题,内存和线程切换开销比较小。但是当在高并发的场景下,需要大量的线程来维护大量的网络连接。因此,基本上BIO模型在高并发场景下是不可用的。
(3)Linux的IO模型之非阻塞IO模型(同步)
一.解析一
把一个Socket设置为非阻塞就是告诉系统内核:当所请求的IO操作无法完成时,不要将进程睡眠,而是返回一个错误。这样IO操作函数将不断测试数据是否已经准备好。如果没有准备好,则继续测试,直到数据准备好为止。在这个不断测试的过程中,会大量的占用CPU的时间,所以该模型不应该被推荐。
二.解析二
把一个Socket设置为非阻塞就是告诉系统内核:在调用Sockets API时,不要让线程睡眠,而应该让函数立即返回。在返回时,该函数返回一个错误代码。
下图展示了一个非阻塞模式的套接字多次调用recv()函数的过程。前三次调用recv()函数时,内核数据还没有准备好。因此recv()函数会立即返回EWOULDBLOCK错误代码。第四次调用recv()函数时,数据已经准备好,被复制到应用程序的缓冲区中。因此recv()函数会返回成功,之后应用程序便开始处理数据。
三.非阻塞IO(NIO)的特点
应用程序线程需不断进行IO系统调用,轮询数据是否已准备好。如果还没准备好,则继续轮询,直到完成系统调用为止。
四.非阻塞IO(NIO)的优点
每次发起IO系统调用,在内核等待数据过程中可以立即返回。用户线程不会阻塞,实时性较好。
五.非阻塞IO(NIO)的缺点
需要不断重复发起IO系统调用,不断询问内核。这种不断的轮询将会占用大量的CPU时间,系统资源利用率比较低。
(4)Linux的IO模型之IO复用模型(同步)
一.解析一(两次调用,两次返回)
IO多路复用模型,是通过一种新的系统调用来实现的。一个进程可以监视多个文件描述符,一旦某个描述符就绪(可读/可写),那么内核就会通知程序进行相应的IO系统调用。非阻塞的BIO就是一个线程只能轮询自己的一个文件描述符。
IO多路复用模型的基本原理就是select/epoll系统调用。单个线程不断的轮询select/epoll系统调用所负责的成百上千的Socket连接,当某个或某些Socket网络连接有数据到达了,就返回这些可以读写的连接。好处是通过一次select/epoll调用,就能查询可读写的成百上千个网络连接。
在这种模式中首先不是进行read系统调动,而是进行select/epoll系统调用。当然前提是要将目标网络连接提前注册到select/epoll可查询的Socket列表,然后才可以开启整个IO多路复用模型的读流程。
步骤一:
首先进行select/epoll系统调用,查询可以读的连接。内核会通过select系统调用查询所有可查询的Socket列表。当任何一个Socket中的数据准备好了,select系统调用就会返回。注意:当用户线程调用了select系统调用时,整个线程是会被阻塞的。
步骤二:
用户线程获得了目标连接后,会发起read系统调用,此时用户线程被阻塞。然后内核开始将数据从内核缓冲区复制到用户缓冲区,接着内核返回结果。
步骤三:
用户线程才解除阻塞的状态,此时用户线程终于读取到数据,继续执行。
二.解析二(两次调用,两次返回)
对一个IO端口,两次调用两次返回,比阻塞IO并没有什么优越性,关键是能实现同时对多个IO端口进行监听。IO复用模型会用到select、poll、epoll函数,这几个函数也会阻塞进程。但是和阻塞IO不同的是,这几个函数可以同时阻塞多个IO操作,而且可以同时对多个读操作和写操作的IO函数进行检测。直到有数据可读或可写时,才真正调用IO操作函数。
当用户进程调用了select,那么整个进程会被阻塞。而同时,内核会监视所有select负责的Socket。当任何一个Socket中的数据准备好了,select系统调用就会返回。这时用户进程再调用read系统调用,将数据从内核拷贝到用户进程。
这个图和阻塞IO的图其实并没有太大的不同,事实上还更差一些。因为这里需要使用两个系统调用(select和recvfrom),而阻塞IO只调用了一个系统调用(recvfrom)。但是select的优势在于它可以同时处理多个连接,select的优势并不是能更快处理单个连接,而是能处理更多的连接。
三.多路复用IO(NIO)的特点
多路复用IO需要用到两个系统调用:一个是select/epoll查询调用,一个是IO的读取调用。负责select/epoll查询调用的线程需要不断进行select/epoll轮询,查找出可以进行IO操作的连接。
四.多路复用IO(NIO)的优点
使用select/epoll的优势是:可以同时处理成千上万个连接。与一条线程维护一个连接相比,IO多路复用技术的最大优势是:系统不必创建线程,也不必维护这些线程,从而大大减小了系统的开销。Java的NIO技术,使用的就是IO多路复用模型。
五.多路复用IO(NIO)的缺点
select/epoll系统调用,本质上也是属于同步IO + 阻塞IO。都需要在读写事件就绪后,自己负责进行读写,这个读写过程是阻塞的。
(5)Linux的IO模型之信号驱动IO(同步)
首先允许套接口进行信号驱动IO。然后安装一个信号处理函数,进程继续运行并不阻塞。当数据准备好时,进程会收到一个SIGIO信号,此时可以在信号处理函数中调用IO操作函数处理数据。
(6)Linux的IO模型之异步IO模型(异步)
AIO的基本流程是:
一.用户线程通过系统调用,告知内核启动某个IO操作,用户线程返回
二.内核在整个IO操作(包括数据准备、数据复制)完成后,通知用户程序
三.用户程序收到通知后便可执行后续的业务操作
内核的数据准备是将数据从网卡读取到内核缓冲区,内核的数据复制是将数据从内核缓冲区拷贝到用户空间的缓冲区。
AIO的详细流程是:
一.当用户线程调用read系统调用后就可以去做其它事了,用户线程不阻塞
二.内核开始IO处理的第一个阶段——准备数据,当内核一直等到数据准备好了,就会将数据从内核缓冲区拷贝到用户缓冲区
三.内核会给用户线程发送一个信号,或者回调用户线程注册的回调接口,告诉用户线程read操作已经完成了
四.用户线程接着就可以读取用户缓冲区的数据,完成后续的业务操作
异步IO模型的特点:在内核的等待数据和复制数据两个阶段,用户线程都不会阻塞。用户线程需要接收内核IO操作完成的事件,或者注册IO操作完成的回调函数到操作系统的内核中。
(7)Linux的5个IO模型对比
其实常用的模型是阻塞IO模型和IO复用模型。
13.Linux的IO模型核心
(1)阻塞IO模型(BIO)
(2)非阻塞IO模型(NIO)
(3)IO复用模型(NIO)
(1)阻塞IO模型(BIO)
应用程序调用一个IO方法,导致应用程序阻塞,等待数据准备好。如果数据没有准备好,则一直等待。如果数据准备好,则将数据从内核空间拷贝到用户空间,然后返回是否成功。
阻塞IO(BIO)的优点:
程序简单,在阻塞等待数据期间,用户线程挂起,用户线程基本不会占用CPU资源。
阻塞IO(BIO)的缺点:
一般情况下,需要为每个连接配套一条独立的线程,或者说一条线程维护一个连接成功的IO流的读写。在并发量小的情况下,这没什么问题,内存和线程切换开销比较小。但是当在高并发的场景下,需要大量的线程来维护大量的网络连接。因此,基本上BIO模型在高并发场景下是不可用的。
(2)非阻塞IO模型(NIO)
把一个Socket设置为非阻塞就是告诉系统内核:当所请求的IO操作无法完成时,不要将进程睡眠,而是返回一个错误。这样IO操作函数将不断测试数据是否已经准备好。如果没有准备好,继续测试,直到数据准备好为止。在这个不断测试的过程中,会大量的占用CPU的时间,所以该模型不应该被推荐。
非阻塞IO(NIO)的特点:
应用程序线程需要不断进行IO系统调用,轮询数据是否已经准备好。如果还没准备好,则继续轮询,直到完成系统调用为止。
非阻塞IO(NIO)的优点:
每次发起IO系统调用,在内核等待数据过程中可以立即返回。用户线程不会阻塞,实时性较好。
非阻塞IO(NIO)的缺点:
需要不断重复发起IO系统调用,不断询问内核。这种不断轮询将占用大量CPU时间,系统资源利用率低。
(3)IO复用模型(NIO)
IO多路复用模型,是通过一种新的系统调用来实现的。一个进程可以监视多个文件描述符,一旦某个描述符就绪(可读/可写),那么内核就会通知程序进行相应的IO系统调用。非阻塞的BIO就是一个线程只能轮询自己的一个文件描述符。
IO多路复用模型的基本原理就是select/epoll系统调用。单个线程不断的轮询select/epoll系统调用所负责的成百上千的Socket连接,当某个或某些Socket网络连接有数据到达了,就返回这些可以读写的连接。好处是通过一次select/epoll调用,就能查询可读写的成百上千个网络连接。
一.select版的多路复用
二.epoll版的多路复用
在这种模式中首先不是进行read系统调动,而是进行select/epoll系统调用。当然前提是要将目标网络连接提前注册到select/epoll可查询的Socket列表,然后才可以开启整个IO多路复用模型的读流程。
步骤一:
首先进行select/epoll系统调用,查询可以读的连接,内核会通过select系统调用查询所有可查询的Socket列表。当任何一个Socket中的数据准备好了,select系统调用就会返回。注意:当用户线程调用了select系统调用时,整个线程是会被阻塞的。
步骤二:
用户线程获得了目标连接后,会发起read系统调用,此时用户线程被阻塞。然后内核开始将数据从内核缓冲区复制到用户缓冲区,接着内核返回结果。
步骤三:
用户线程才解除阻塞的状态,此时用户线程终于读取到数据,继续执行。
三.多路复用IO(NIO)的特点
多路复用IO需要用到两个系统调用:一个是select/epoll查询调用,一个是IO的读取调用。负责select/epoll查询调用的线程:需要不断进行select/epoll轮询,查找出可以进行IO操作的连接。
四.多路复用IO(NIO)的优点
使用select/epoll的优势是:可以同时处理成千上万个连接。与一条线程维护一个连接相比,IO多路复用技术的最大优势是:系统不必创建线程,也不必维护这些线程,从而大大减小了系统的开销。Java的NIO技术,使用的就是IO多路复用模型。
五.多路复用IO(NIO)的缺点
select/epoll系统调用,本质上也是属于同步IO + 阻塞IO。都需要在读写事件就绪后,自己负责进行读写,这个读写过程是阻塞的。
14.select、poll、epoll的区别
(1)支持一个进程所能打开的最大连接数
(2)FD剧增后带来的IO效率问题
(3)消息传递方式
15.水平触发和边缘触发
(1)Level_triggered(水平触发)
当被监控的文件描述符上有可读写事件发生时,epoll_wait()会通知处理程序去读写。如果这次没有把数据一次性全部读写完(如读写缓冲区太小),那么下次调用 epoll_wait()时,它还会通知你在上没读写完的文件描述符上继续读写。当然如果处理程序一直不去读写,它会一直通知处理程序。
如果系统中有大量不需要读写的就绪文件描述符,而它们每次都会返回,这样会大大降低处理程序检索处理程序关心的就绪文件描述符的效率。
(2)Edge_triggered(边缘触发)
当被监控的文件描述符上有可读写事件发生时,epoll_wait()会通知处理程序去读写。如果这次没有把数据全部读写完(如读写缓冲区太小),那么下次调用epoll_wait()时,它不会通知处理程序,也就是它只会通知处理程序一次,直到该文件描述符上出现第二次可读写事件才会通知处理程序。
这种模式比水平触发效率高,系统不会充斥大量处理程序不关心的就绪文件描述符。select()、poll()模型都是水平触发模式,信号驱动IO是边缘触发模式。epoll()模型即支持水平触发,也支持边缘触发,默认是水平触发。