对于应用层网络协议的一些理解

大学里面对计算机网络的内容理解得不深,直到自己构建了Web应用的时候才稍稍有些接触。与我关系最为密切的就是 HTTP 协议,如今已经发展到了 HTTP/2 ,然而我对于这个协议还是停留在 HTTP 方法、RestFul API 等这些东西上。看起了《计算机网络 - 自顶向下方法》这本书,希望能有所收获。

应用层中的 HTTP 协议

OSI 7层模型将网络协议栈分为7层,自上而下分别是应用层、表示层、会话层、运输层、网络层、链路层、物理层,层于层之间松耦合。而五层网络模型将应用层、表示层、会话层合并为应用层,并表示应用层的事情交由软件开发者来决定。

身为一个软件开发者,平时接触得最多的就是 HTTP 这个应用层协议,这里提到的应用层是五层网络模型中应用层的概念,在这本书中也是就此模型展开讨论。我们最为熟悉的是HTTP报文中的MethodURL 以及 body部分,在 Ajax 请求当中,URL 是我们要请求的API接口 ,Method 是我们用来请求的方法,body往往对应着请求中 data 这个对象,这个对象是我们要传输给API接口的参数,而在响应中,body 中的 data 是我们要从这个API接口获得的数据。可以说开发者每次书写一个 Ajax 请求,就相当于完成了一次 HTTP报文 的编写。

我们也会关注报文当中的头部,比如 content-type 表明了body中是什么类型的内容,可以是 text/html 也可以是 application/json ; 比如令人头疼的跨域 access-control-allow-origin ;比如在采用token作为认证手段的时候,在头部加入 Authorization

以上所说的这些,都是 HTTP 规定的一部分,协议本身就是一种规范,HTTP规范了报文中各个字段的含义,以及开发者可以进行的编码。

当然规范不止这些,规范中还规定了 HTTP 报文在运输层中采用 TCP 的运输方式等等,关于 TCP 的内容我们之后再谈。

介绍这些规范并不是这篇文章的本意,我想通过对HTTP的介绍来理解应用层协议这个概念,从我的角度来看,应用层协议是用来规范开发者和运输层两方的行为,即开发者如何应用 HTTP 协议,运输层如何保证协议规定的要求。那开发者可不可以自己来定义一个私人的应用层协议呢?这当然可以,套接字(socket)可以实现你的想法。

从概念上说,最简单的两个步骤,首先定义你的报文格式,你可以采取和HTTP协议一样的方式去定义。然后来选择运输层协议,是用 TCP 建立一个可靠的传输方式,还是使用 UDP 进最大可能交付。完成这些之后应该可以保证将报文从一方传递给另外一方,但是另一方收到之后会不会向你发送一个类似于 HTTP 的响应,这可能需要你对自己的协议进行进一步的完善,一个应用层协议的落地总是会有相应的代码来保证实现。

细说 HTTP 协议

说些我在之前的开发过程中,因为知识积累不够被我忽略掉的部分。

HTTP应用了TCP作为运输层协议,这意味着开发者需要思考服务端与客户端的连接方式,是使用持续连接还是非持续连接

非持续连接

对于每一个HTTP请求,在请求之前都需要与服务端建立一次socket连接,这涉及到“三次握手”的过程。之后正式发送HTTP请求,服务器做出响应并传输对象,提示TCP断开连接。

这里指的是服务端将客户端请求的内容,客户端可能会请求一个HTML文件,或者是一个JSON对象,服务端将其想要的放入响应的body当中,并发送响应报文。

客户端接收响应报文,TCP收到传输完成的确认后断开连接。

一般会先请求HTML文件,再根据文件产生的引用发出对其它资源的请求,非持续链接每一次都会重新建立连接并断开连接,因此每一次请求到获得响应耗费的时间都是两个RTT + 资源下载的时间。当然,浏览器对于这些请求可以采取并行的方式,即同时发出多个HTTP请求,这种方式可以明显提高客户端获取资源的效率。

持续连接

当请求头中的connection字段为keep-alive时,说明该请求支持持续链接(长连接)。在HTTP/1.1中,持续连接时默认支持的,这意味着如果没有人为改变,那么所有的连接都是持续连接,而在HTTP/1.0中需要手动在header中加入connection:keep-alive才可以支持持续连接。

在持续连接当中,多个请求可以复用一条socket连接来进行资源的请求与响应,比起非持续连接,这样减少了每次建立连接的时间。HTTP/1.1还允许请求不必等待上一次响应的结果即可发出,这样更可以提高客户端获取资源的效率。

持续连接即使在所有请求结束之后依然保持连接,保持的时间长短和设置有关。

connection: keep-alive #使用持续连接
keep-alive: max=5, timeout=120 #最大保持5个连接,或者连接空闲120秒后断开连接

另外,持续连接中还有 持久连接(persistent)管道连接 的说法。

持久连接强调了一个连接关闭的机制,在所有HTTP事务结束之后必须显示的关闭连接 connection:close 。HTTP事务指得是请求和响应结果,可以理解成客户端发出一次请求并最终获得响应得过程。

管道连接就是上文中所说,无需等待上一次请求的响应,可以发射多条请求并获得服务端的响应。

总结

应用层协议是留给开发者的一块具有创造力的土壤,开发者不需要考虑更加复杂的事情,要么是对现有应用层协议的应用,要么是创造一个软件或硬件独有的协议并完成交互。而协议在创造的过程中,最后要进行决定的就是运输层的事情:你要选择哪款协议,TCP / UDP ? 选择它,完善它并实现它。

HTTP是非常重要的应用层协议,请求与响应报文与开发者之间的关系;每一次请求,客户端与服务端维持连接的方式。

另外,在持续连接当中,服务端要维持与客户端的连接。实际上建立连接的socket是一个四元组,每一个四元组即是一条连接。客户端要发起连接的时候会随机分配一个本地的端口号,这样这个源端口号和源IP地址在这个四元组中发往目的地址,在持续连接的条件下,服务端就会维护这个四元组,直到连接断开。

(src_ip, src_port, dst_ip, dst_port)