本文章讲解的内容是计算机网络总结。
开放式系统互联模型(Open System Interconnection Model,简称OSI模型)是一种概念模型,由国际标准化组织提出,是一种试图使各种计算机在世界范围内互连为网络的标准框架,它在1984年定义于ISO/IEC 7498-1标准。
OSI模型将计算机网络体系结构分为七层,其中第一层在底部,第七层在顶部:
互联网协议套件(Internet Protocol Suite,简称IPS)是一个网络通信模型,以及一整个网络传输协议家族,为网际网络的基础通讯架构,又称为TCP/IP协议族(TCP/IP Protocol Suite或TCP/IP Protocols),简称TCP/IP,该协议家族涉及到两个重要的核心协议:TCP(传输控制协议)和IP(网际协议)。
传输控制协议(Transmission Control Protocol,简称TCP)是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义,位于OSI模型的传输层。
应用层向TCP层发送用于网间传输的、用8位字节表示的数据流,然后TCP把数据流分割成适当长度的报文段(通常受该计算机连接的网络的数据链路层的最大传输单元(MTU)的限制),然后TCP把结果包传给IP层,由它来透过网络将包传送到接收端实体的TCP层,TCP为了保证不发生丢包,会给每一个包一个序号,同时序号也可以保证传送到接收端实体的包按序接收,然后接收端实体对已成功收到的包发回一个相应的确认消息(ACK),如果发送端实体在合理的往返时延(RTT)内未收到确认,那么对应的数据包就被假设为已丢失并进行重传。
TCP用一个校验和函数来检验数据是否有错误,在发送和接收时都要计算校验和。
数据在TCP层称为流(Stream),数据分组称为分段(Segment),作为比较,数据在IP层称为数据报(Datagram),数据分组称为分片(Fragment),UDP(用户数据报协议)中的数据分组称为消息(Message)。
TCP协议的运行可以分为三个阶段:连接创建(Connection Establishment)、数据传输(Data Transfer)、连接终止(Connection Termination)。
操作系统将TCP连接抽象为套接字(Socket)表示的本地端口(Local End-Point),作为编程接口给程序使用,在TCP连接的生命期内,本地端口要经历一系列的状态改变。
TCP用三次握手(three-way handshake,又称为三路握手)过程创建一个连接。在连接创建的过程中,很多参数都要被初始化,例如:序号被初始化,它可以保证按序传输和连接的强壮性。
一对终端同时初始化一个它们之间的连接是可能的,但是通常是由一端(服务器端)打开一个套接字(Socket)然后监控来自另一端(客户端)的连接,这个称为被动打开(passive open),服务器端被被动打开以后,客户端就能开始创建主动打开(active open)。
服务器端执行listen函数后,就在服务器上创建两个队列:
发送第一个SYN包的一端执行主动打开(active open),接收这个SYN包并且发回下一个SYN包的另一端执行被动打开(passive open)。
在Socket编程中,客户端调用connect函数触发三次握手。
要注意的是,如果服务器端接收到了客户端发的SYN包后,回送SYN/ACK包的时候掉线,服务器端没有收到客户端回送的ACK包,这个时候该连接就处于一个中间状态(既没有成功,也没有失败),服务器端如果在一定时间内重发SYN/ACK包,在Linux下,默认重试次数是五次,重试的间隔时间从一秒开始每次都翻倍,例如:五次的重试时间间隔是1秒、2秒、4秒、8秒、16秒,总共需要1秒+2秒+4秒+8秒+16秒=31秒的时间,第五次发出后还要等32秒才知道第五次超时,也就是说,总共需要1秒+2秒+4秒+8秒+16秒+32秒=63秒的时间,TCP才会断开这个连接,可以使用三个TCP参数来调整行为:
主机收到一个收到一个TCP包的时候,用两端的IP地址与端口号来标识这个TCP包属于哪个session,使用一张表来存储所有的session,表中每条称作Transmission Control Block(TCB),TCB结构的定义包括连接使用的源端口、目的端口、目的IP、序号、应答序号、对方窗口大小、己方窗口大小、TCP状态、TCP输入/输出队列、应用层输出队列和TCP的重传有关变量等。
服务器端的连接数量是无限的,只会受到内存限制;客户端的连接数量,在以前由于发送第一个SYN包到服务器之前需要先分配一个随机空闲的端口,这限制了客户端IP地址的对外发出连接的数量上限,从Linux 4.2开始,有了Socket选项IP_BIND_ADDRESS_NO_PORT,它通知Linux内核不保留usingbind使用端口号为0时内部使用的临时端口(Ephemeral Port),在connect时会自动选择端口以组成独一无二的四元组(同一个客户端端口可用于连接不同服务器套接字,同一个服务器端口可用于接受不同客户端套接字的连接)。
要注意的是,对于不能确认的包、接收单还没读取的数据,都会占用的操作系统的资源。
在TCP的数据传输状态,可以使用以下机制保证TCP的可靠性和强壮性:
TCP用**四次握手(four-way handshake,又称为四路握手、四次挥手)**过程终结一个连接。
MSL(Maximum Segment Lifetime)是指报文段最大生存时间,每个具体TCP实现必须选择一个MSL,它是任何报文被丢弃前在网络内的最长时间,这个时间是有限的,因为TCP报文段是以IP数据报在网络内传输,而IP数据报是有限制其生存时间的TTL字段,
客户端通常执行主动关闭(active open),并且进入TIME_WAIT(时间等待)状态;服务端通常执行被动关闭(passive close),但是不会进入**TIME_WAIT(时间等待)**状态。
在Socket编程中,任何一方调用close函数都可以触发四次握手。
网际协议(Internet Protocol,简称IP,又称为互联网协议),是用于分组交换数据网络的协议。
为了根据源主机和目的主机的地址来传送数据,IP定义了寻址方法和数据报的封装结构,第一个架构的主要版本为IPv4,目前仍然是广泛使用的互联网协议,不过世界各地正在积极部署IPv6。
用户数据报协议(User Datagram Protocol,简称UDP,又称为用户数据报协议)是一种简单的面向数据报的通信协议,位于OSI模型的传输层,该协议由David P. Reed在1980年设计且在RFC 768中规范。
在TCP/IP模型中,UDP为网络层以上和应用层以下提供了一个简单的接口。
UDP只提供数据的不可靠传输,它一旦把应用程序发给网络层的数据发送出去,就不保留数据备份,所以UDP被认为是不可靠的数据报协议,要注意的是,UDP在IP数据报的头部仅仅加入了复用和数据校验字段。
UDP适用于不需要或者在程序中执行错误检查和纠正的应用,它避免了协议栈中此类处理的开销,对时间有较高要求的应用程序通常使用UDP,因为丢弃数据包比等待或者重传导致延迟更可取。
地址解析协议(Address Resolution Protocol,简称ARP)是一个通过解析网络层地址来寻找数据链路层地址的网络传输协议,它在IPv4中极其重要,最初在1982年的RFC 826中提出纳入互联网标准STD 37。
ARP是通过网络地址来定位MAC地址,它已经在很多网络层和数据链路层之间得以实现,包括IPv4、Chaosnet、DECnet和Xerox PARC Universal Packet(PUP)使用IEEE 802标准,光纤分布式数据接口、X.25、帧中继和异步传输模式(ATM)、IEEE 802.3和IEEE 802.11标准中IPv4占了多数流量。
在IPv6中邻居发现协议(NDP)用于代替ARP。
邻居发现协议(Neighbor Discovery Protocol,简称NDP或者ND)是TCP/IP协议栈的一部分,用于IPv6,它工作在数据链路层,负责链路上发现其他节点和相应的IP地址,并且确定可用路由和维护关于可用路径和其他活动节点的信息可达性。
统一资源标识符(Uniform Resource Identifier,缩写:URI)用于标识某一互联网资源名称的字符串,这种标识允许用户对网络(一般指万维网)中的资源通过特定的协议进行交互操作。
统一资源定位符(Uniform Resource Locator,缩写:URL)和统一资源名称(Uniform Resource Name,缩写:URN)都属于URI的子类,URI可以为URL和URN两者之一或者同时是URI和URN。
统一资源定位符的标准格式如下所示:
[协议类型]/[服务器地址]:[端口号]/[资源层级UNIX文件路径][文件名]?[查询]#[片段ID]
统一资源定位符的完整格式如下所示:
[协议类型]/[访问资源需要的凭证信息]@[服务器地址]:[端口号]/[资源层级UNIX文件路径][文件名]?[查询]#[片段ID]
要注意的是,[访问资源需要的凭证信息]、[端口号] 、[查询]、[片段ID]都属于选填项。
HTTP(HyperText Transfer Protocol)是一种用于分布式、协作式和超媒体信息系统的应用层协议。HTTP是万维网的数据通信的基础。通过HTTP协议或者HTTPS协议请求的资源由统一资源标识符(Uniform Resouce Identifiers,简称URI)来标识。设计HTTP的最初目的是为了提供一种发布和接收HTML页面的方法。
HTTP是一个客户端(用户)和服务端(网站)之间请求和应答的标准,通常使用TCP协议。
尽管TCP/IP协议是互联网上最流行的应用,但是在HTTP协议中并没有规定它必须使用或者它支持的层,事实上HTTP可以在任何互联网协议或者其他网络上实现。HTTP假定其下层协议提供可靠的传输,因此任何能够提供这种保证的协议都可以被其使用,所以HTTP在TCP/IP协议族使用TCP作为其传输层。
通常,由HTTP客户端发起一个请求,创建一个到服务器指定的端口(默认是80端口)的TCP连接,HTTP服务器则在那个端口监听客户端的请求,一旦收到请求,服务器会向客户端返回一个状态,例如:HTTP/1.1 200 OK和返回的内容,例如:请求的文件、错误信息和其它信息。
HTTP是属于应用层的协议,但是它还需要使用其他层的协议配合来完成信息的交换,涉及到如下的协议:
HTTP/1.1相较于HTTP/1.0的区别主要体现在如下:
HTTP/2是超文本传输协议第二版,简称h2(基于TLS/1.2或以上版本的加密连接)或者h2c(非加密连接),是HTTP协议的第二个主要版本,在万维网使用。
HTTP/2是HTTP协议自1999年HTTP/1.1发布后的首个更新,主要基于SPDY协议,它由互联网工程任务组(IETF)的Hypertext Transfer Protocol Bis(httpbis)工作小组进行开发,该组织于2015年5月以RFC 7540正式发表,HTTP/2的标准化工作由Chrome、Opera、Firefox、Internet Explorer 11、Safari、Amazon Silk和Edge等浏览器提供支持。
HTTP/2采用二进制格式传输数据,而不是HTTP/1.x的文本格式,解析起来会更加高效。
有两个概念:
HTTP/1.x的请求报文和响应报文,都是由起始行(start line)、头部字段(header)、消息正文(entity)组成,各部分之间以文本换行符分隔,而HTTP/2将请求数据和响应数据分割为更小的帧,并且采用二进制编码。
在HTTP/2中,同一个域名下所有通信都在单个连接上完成,这个连接可以承载任意数量的双向数据流,每个数据流都是以消息的形式发送,消息是由一个或者多个帧组成,多个帧可以乱序发送,根据帧首部的流标识可以重新组装。
在HTTP/1.x中,如果想并发多个请求,就必须使用多个TCP链接,而且浏览器为了控制资源,还会对单个域名有6~8个的TCP链接请求限制;在HTTP/2中,因为存在二进制分帧,所以不再依赖TCP链接去实现多流并行,有如下特性:
服务端可以在发送页面HTML时主动推送其它资源,而不需要等待浏览器解析到相应位置,例如:服务端可以主动把JS文件和CSS文件推送给客户端,而不需要等待客户端解析HTML时再发送这些请求。
客户端可以选择是否接收,如果服务端推送的资源已经被浏览器缓存过,浏览器可以发送RST_STREAM帧来拒收。
主动推送遵守同源策略,服务器不会随便推送第三方资源给客户端。
HTTP每次通信,都会携带一组头部,用于描述这次通信的资源、浏览器属性,在HTTP/1.x中,因为需要等待带着ACK的响应回来以后才能继续发送请求,所以会导致请求的大小越来越大,有时候甚至大于TCP窗口的初始大小,而且携带大量冗余头部消息,浪费带宽资源;在HTTP/2中,它会对消息头采用HPACK(专门为HTTP/2头部设计的压缩格式)进行压缩传输,节省消息头占用的网络流量。
压缩策略是在客户端和服务端使用首部表来跟踪和存储之前发送的键值对,对于相同的数据,不再每次通过请求和响应发送,首部表在连续存续期内始终存在,它是由客户端和服务端共同渐进地更新,每个新的键值对有可能会追加到这个首部表的的末尾,也有可能替换这个首部表之前的值。
HTTP协议的报文分为请求报文和响应报文,它们由三大部分组成:
起始行(start line)和头部字段(header)并称为请求头或者响应头,也称为Header,消息正文(entity)称为实体,也称为Body,要注意的是,HTTP协议规定每次发送必须由Header,但是可以没有Body,Header和Body之间必须要有一个空行(CRLF),例子如下:
可以看到报文是用ASCII文本书写的,每一行之间都存在换行,最后一行会加上一个回车换行符。
HTTP协议请求方法分为九种,如下所示:
要注意的是,HTTP/1.0定义三种请求方法:GET请求、HEAD方法和POST请求;HTTP/1.1新增六种请求方法:PUT请求、DELETE请求、CONNECT请求、OPTIONS请求、TRACE请求和PATCH请求。
HTTP头字段(HTTP header fields)是指在超文本传输协议(HTTP)的请求(request)和响应(response)消息中的消息头部分
HTTP头字段是在请求行或者响应行之后传输的,这些字段是以明文的字符串格式传输,它一个键值对,以冒号分隔键名和键值,以回车(CR)加上换行(LF)符号序列结尾。
HTTP头字段根据实际用途分为以下四种类型:
Cache-Control:指向从服务端直到客户端在内的所有的缓存机制告知,它们是否可以缓存这个对象,它的单位是秒,它可以出现在请求标头和响应标头中,例子如下所示:
Cache-Control: no-cache
Connection:决定当前事务(三次握手和四次挥手)完成后,是否关闭网络连接,例子如下所示:
Connection: keep-alive
Connection: Upgrade
Date:指这条消息发送时的日期和时间(按照RFC 7231中定义的**“超文本传输协议日期”格式来表示),它可以出现在请求标头和响应标头**中,例子如下所示:
Date: Mon, 8 Mar 2021 03:00:00 GMT
Pragma:指报文指令,例子如下所示:
Pragma: no-cache
Trailer:指报文末端的首部一览,例子如下所示:
Trailer: Max-Forwards
Transfer-Encoding:指定报文主体的传输编码格式,例子如下所示:
Transfer-Encoding: chunked
Upgrade:用来升级为其他协议,例子如下所示:
Upgrade: HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11
Via:指代理服务器的相关信息,例子如下所示:
Via: 1.0 fred, 1.1 p.example.net
Warning:指错误通知,例子如下所示:
Warning: 199 Miscellaneous warning
HTTP状态码(HTTP Status Code)是用来表示网页服务器超文本传输协议响应状态的三位数字代码,它由RFC 2616规范定义的,并得到RFC 2518、RFC 2817、REC 2295、RFC 2774和RFC 4918等规范扩展。
HTTP状态码分为五种状态,分别为消息、成功、重定向、客户端错误和服务器错误,如下所示:
1XX类型的状态码代表请求已被接受,需要继续处理,要注意的是,这类响应是临时响应,只包含状态行和某些可选的响应头信息,并且以空行结束。
表示服务端已经接受到请求头,并且客户端应该继续发送请求主体(例如:POST请求),如果请求已经完成,就会忽略这个响应。
表示服务端已经理解客户端的请求,并且将通过Upgrade消息头通知客户端采用不同的协议完成这个请求。
表示服务端已经收到并且正在处理请求,但是无响应可用,要注意的是,WebDAV请求可能包含许多涉及文件操作的子请求,需要很长时间才能完成请求。
表示在最终的HTTP消息之前返回一些响应头字段。
2XX类型的状态码代表请求已成功被服务端接收、理解、并且接受。
表示请求已成功,请求所希望的响应头字段或者数据体将随此响应返回。
表示请求已经被实现,而且有一个新的资源已经依据请求的需要而创建,并且其URI已经随Location头消息返回。
表示服务器已接受请求,但是尚未处理。
服务器是一个转换代理服务器(Transforming Proxy),例如:网络加速器,它是以状态码200为起源,但是回应了原始响应的修改版本。
服务器成功处理请求,但是没有返回任何内容。
服务器成功处理请求,但是没有返回任何内容,与状态码204不相同的地方是这个响应要求请求者重置文档视图。
服务器成功处理部分GET请求。
表示之后的消息体是一个XML消息,并且可能根据之前子请求数量的不同,包含一系列独立的响应代码。
DAV绑定的成员已经在多状态响应之前的部分被列举,并且未被再次包含。
服务器已经满足了对资源的请求,对实体请求的一个或者多个实体操作的结果表示。
3XX类型的状态码代表客户端采取进一步的操作才能完成请求。通常这类型的状态码用来重定向,后续的请求地址(重定向目标)在本次响应的Location域中指明。
被请求的资源有一系列可供选择的回馈消息,每个都有自己特定的地址和浏览器驱动的商议信息,用户或者浏览器能够自行选择一个首选的地址进行重定向。
被请求的资源已永久移动到新位置,并且将来任何对此资源的引用都应该使用本响应返回的若干个URI之一,如果可能,拥有链接编辑功能的客户端应当自动把请求的地址修改为从服务端反馈回来的地址,除非额外指定,否则这个响应也是可缓存的。
要求客户端执行临时重定向(Moved Temporarily),由于这样的重定向是临时的,客户端应当继续向原有地址发送以后的请求,只有在Cache-Control或者Expires中进行了指定的情况下,这个响应才是可缓存的。
对应当前请求的响应可以在另一个URI上被找到,当响应于POST请求、PUT请求或者DELETE请求接收到响应时,客户端应该假定服务器已经收到数据,并且应该使用单独的GET请求消息发出重定向。
表示资源在由请求头字段中的If-Modified-Since或If-None-Match参数指定的这一版本之后,未曾被修改,在这种情况下,由于客户端仍然具有以前下载的副本,因此不需要重新传输资源。
被请求的资源必须在指定的代理才能被访问。
后续请求应使用指定的代理,要注意的是,在最新版的规范中,这个状态码不再被使用。
请求应该与另一个URI重复,但后续的请求应仍使用原始的URI, 与状态码302相反,当重新发出原始请求时,不允许更改请求方法,例如:应该使用另一个POST请求来重复POST请求。
请求和所有将来的请求应该使用另一个URL重复。
4XX类型的状态码代表客户端可能发生了错误,妨碍了服务器处理。
由于客户端明显的错误(例如:请求语法错误、大小太大、无效的请求或者欺骗性路由请求),服务端不能或不会处理处理该请求。
语义是指未认证,即用户没有必要的凭证,表示当前请求需要用户验证。
最初的意图是可能被用作某种形式的数字现金或者在线支付方案的一部分,但是几乎没有哪家服务商使用,而且这个状态码通常不被使用。
服务端已经理解请求,但是拒绝执行它,与状态码401不同的是,身份验证并不能提供任何帮助,而且这个请求也不应该被重复提交。
请求失败,请求所希望得到的资源未被在服务端上发现,但是允许用户的后续请求。
请求行中指定的请求方法不能被用于请求相应的资源,该响应必须返回一个Allow头信息用以表示出当前资源能够接受的请求方法的列表。
请求的资源的内容特性无法满足请求头中的条件,因而无法生成响应实体,该请求不可接受。
与状态码401类似,只不过客户端必须在代理服务器上进行身份验证,代理服务器必须返回一个Proxy-Authenticate用来进行身份验证,客户端可以返回一个Proxy-Authorization信息头用来验证。
请求超时,客户端没有在服务端预备等待的时间内完成一个请求的发送,客户端可以随时再次提交这一请求而无需进行任何更改。
表示因为请求存在冲突无法处理该请求,例如:多个同步更新之间的编辑冲突。
表示所请求的资源不再可用,当资源被有意地删除并且资源应该被清除的时候,应该使用该状态码。
服务端拒绝在在没有定义Content-Length头字段的情况下接受请求。
服务端在验证请求的头字段中给出先决条件时,没能够满足其中的一个或者多个;这个状态码允许客户端在获取资源的时候在请求的元信息(请求头字段数据)中设置先决条件,以此避免该请求方法被应用到其希望的内容以外的资源上。
表示服务端拒绝处理当前请求,因为该请求提交的实体数据大小超过服务端愿意或者能够处理的范围,在这种情况下,服务端可以关闭连接以免客户端继续发送该请求。
表示请求的URI长度超过服务端能够理解的长度,因此服务端拒绝对该请求提供服务。
因为请求中提交的互联网媒体类型不是服务端中所支持的格式,所以请求被拒绝,例如:客户端将图像上传格式是svg,但是服务端要求图像使用上传格式是jpg。
客户端已经要求文件的一部分(Byte serving),但是服务端不能提供该部分,例如:客户端要求文件的一部分超出文件尾端。
在请求头字段Expect中指定的预期内容无法被服务端满足,或者这个服务端是一个代理服显的证据证明在当前路由的下一个节点上,Expect的内容无法被满足。
该状态码是在1998年作为IEIF的传统愚人节笑话,在RFC 2343(超文本咖啡壶控制协议)中定义的,并不需要在真实的HTTP服务器中定义。
无法产生响应的服务端,例如:因为连接重用。
请求格式正确,但是由于含有语义错误,无法响应。
当前资源被锁定。
由于之前的某个请求发生的错误,导致当前请求失败,例如:RPOPPATCH。
服务端拒绝处理在Early Data中的请求,以规避可能的重放冲击。
客户端应该切换到Upgrade头字段中给出的不同协议,例如:TLS/1.0。
原服务端要求该请求满足一定条件,这是为了防止未更新问题,即客户端通过读取(GET)一个资源的状态,并且更改它,并且将它写(PUT)回服务端,但是这期间第三方已经改在服务端上更改了该资源的状态,因此导致了冲突。
用户在给定的时间内发送了太多的请求,用于网络限速。
服务端不愿意处理请求,因为一个或者多个头字段过大。
该请求因为法律的要求而被拒绝,由IETF在2015年核准后新增加。
5XX类型的状态码代表服务端无法完成明显有效的请求,服务端可能在处理请求的过程中有错误状态或者异常状态发生,也可能是服务端意识到以当前的软硬件资源无法完成对请求的处理。
通用错误消息,服务端遇到一个未曾预料的状况,导致了它无法完成对请求的处理,没有给出具体错误信息。
服务端不支持当前请求所需要的某个功能,当服务端无法识别请求的方法,并且无法支持其对任何资源的请求,例如:网络服务API的新功能。
作为网关或者代理工作的服务端尝试执行请求时,从上游服务端接收到无效的响应。
由于临时的服务端维护或者过载,服务端当前无法处理请求,这个状况是暂时的,并且将在一段时间以后恢复。如果能够预计延迟时间,那么响应中可以包含一个Retry-After头字段以标明这个延迟时间;如果没有给出这个Retry-After头字段,那么客户端应当以处理状态码500的方式处理它。
作为网关或者代理工作的服务端尝试执行请求时,未能及时从**上游服务端(URI标识出的服务端,例如:HTTP、FTP或者LDAP)或者辅助服务端(例如:DNS)**收到响应。
服务端不支持,或者拒绝支持在请求中使用的HTTP版本,这意味着服务端不能或者不愿使用与客户端相同的版本,响应中应当包含一个描述为何版本不被支持以及服务端支持哪些协议的实体。
它由**《透明内容协商协议》(RFC 2295)扩展,代表服务端存在内部配置错误**,被请求的协商变元资源被配置为透明内容协商中使用自己,因此一个协商处理中不是一个合适的重点。
服务端无法存储完成请求所必须的内容,这个状况被认为是临时的。
服务端在处理请求时陷入死循环,可以代替状态码208。
获取资源所需要的策略没有被满足。
客户端需要进行身份验证才能获得网络访问权限,这是为了限制用户群访问特定网络,例如:连接WiFi热点时的强制网络门户)。
**参考文献:**维基百科
我的GitHub:TanJiaJunBeyond
Android通用框架:Android通用框架
我的掘金:谭嘉俊
我的简书:谭嘉俊
我的CSDN:谭嘉俊