HTTP 的里外上下

今天参加了一个面试,再次清醒地认识到自己的编程基础是何等薄弱。

面试官说得对,无论是爬虫还是 Web 开发,不都应该对 HTTP 有一个最基本的把握和理解吗?这话说得我无言以对。

HTTP 是什么?

HTTP 是用于服务器-客户端通讯的协议之一。协议最基本的要点是请求和响应。在 HTTP 协议之上,客户端发送请求,服务器响应请求,并将响应内容发送给客户端,客户端收到服务器发来的内容后,进行自主处理。

如今,绝大多数的网络通讯都是基于 HTTP 协议。HTTP 协议本身有两个特点:

TCP/IP 协议族

这是一个协议族,最出名的是 TCP、UDP 和 IP 协议。除却少数一些使用场景之外,大部分网络通信都是基于 TCP 协议进行传输数据,而 IP 协议提供了基本的命名方法和传送机制,因为客户端中的一段信息,想要被服务器接收到,中间其实要经过多次中转,中转会途径各种不同类型的网络和硬件,为保证数据一致性,它们必须一致性地采取 TCP/IP 协议封装数据,然后发送数据。

socket 通信

客户端和主机之间的网络和硬件,通过一致性地采用 TCP/IP 协议处理数据,保证主机可以有效可靠地接收客户端发出去的信息。

很显然,TCP/IP 协议是应用于传输层的协议。也就是说,当客户端发出数据之后,数据怎样被传输将遵守的协议。当我们将传输过程悬置,认为它已经完美时,就可以在抽象层面上认为客户端发出去的数据将会直接到达主机。

那么,问题就来了,客户端怎么发出数据,主机怎么接收数据?这就涉及到 socket。

但是,socket 是个什么东西呢?我们说,TCP/IP 协议是传输层使用的协议。然后,应用层协议是什么呢?其实就是 HTTP 这类协议,其它类型的应用层协议还包括 FTP、SMTP等。

我们希望在使用应用层传输数据时,不需要关心底层的传输。但想要传输数据,必然需要使用传输层,也就是说,应用层和传输层之间必然需要建立起联系。可我们也知道,应用层协议有很多种,传输层协议也不止一种,如果分别建立联系的话,事情就会变得相当复杂,而且一点也不优雅。

这个时候,就有了 socket。socket 就是这样一种通用接口,负责帮助应用层和传输层沟通。这样一来,应用层那里需要关心的就只是如何将应用层协议规定好的内容格式转化为 socket 接口可以发送和接收的数据格式;而传输层所要关心的只是在传输的起点将 socket 传来的数据封装为 TCP/IP 协议下的数据格式,然后经过漫长的中转,到达主机,在传输的终点将 TCP/IP 协议封装下的数据转化为 socket 可以接收的数据格式。最后,socket 再将数据转化为应用层使用的格式。终于,经历过这么一个虽说复杂但是又很清晰优雅的转换之后,主机的应用层成功地接收到相应协议下的数据,然后就是有针对性地进行处理并反馈响应信息。

总结

综上,HTTP、TCP/IP、socket 这三者是相互牵扯,并各种分工和侧重的。

一般意义上,客户端和主机之间想要进行通讯,第一步就是建立起有效地连接。这种连接的抽象,在面对程序员群体时,可以是客户端 socket 进程与主机 socket 进程之间的连接。

在客户端 socket 进程与主机 socket 进程建立连接时,会经过三次会话,也就说在传输层会发生三次数据传输。这三次会话是程式化的,在 socket 接口中是不可见的,因为使用 socket 的应用层并不需要知晓这些细节。同样地,在断开连接时,也会经历几次程式化地会话,共有四次,而这些也是应用层所不需要知晓,也不会去关心的。

如何实践?

作为程序员,可以编写简单的基于 HTTP 协议进行通信的 server 程序和 client 程序,也可以编写简单的基于 socket 接口进行通信的 server 程序和 client 程序。此外,在使用 HTTP 协议进行通信时,肯定是有相应模块提前实现了基于 socket 进行通信的 server 接口和 client 接口。

所有这些之上,到了最核心的问题,一个 Web 应用是怎么被构建起来的?我们知道 Web 应用也是基于客户端-服务器模型,通讯协议基本是 HTTP。

那么编写 Web 应用时,需要在多大程度上面对 HTTP 协议呢?答案是几乎为零。

在 Web 应用的编写中,有着极为复杂的 HTML 页面和交互逻辑,这些才是 Web 应用的核心,也是 Web 应用的主业务。在这种思路之下,Web 应用如何做到基于 HTTP 协议进行通讯呢?答案是应用框架。

后端语言,例如 Python,Java 等等,都会有很多 Web 框架可供选择。Web 框架负责的部分主要是完成 URL 到处理函数的双向映射。

然而我们知道,HTTP 协议中并不涉及 URL 映射问题,协议的关键是请求头、请求体和响应头、响应体,以及它们要严格遵循的内容格式。

由于 Web 应用框架分散在各个后端语言中,千奇百怪,各种个样。这个时候,就需要一个更底层的接口,负责统一实现 HTTP 协议本身,保证各个语言以相同的模型解析基于 HTTP 协议的请求,构造基于 HTTP 协议的响应。

这个统一接口就是 Web Server Gate Interface,简称 WSGI。

也就是说,WSGI 负责统一 HTTP 协议的具体实现,而 Web 框架负责基于 WSGI 重点解决 URL 高效映射的问题。

然而,所有这些到最后都不再是瓶颈,极度复杂的 HTML 交互型页面才是瓶颈。这个时候,就到了各种前端框架各放异彩的时候,而这就是另外一个故事了。

以上这些,有些需要反复理解,有些需要反复实践,然后在此基础之上,还可以建立起各种延伸,包括 cookie 的必要性,session 的必要性,等等。

上面有提到,HTTP 的一大特点是无状态。但是,有很多场景,服务器需要识别客户端用户具体是谁,并为它保存一系列状态信息。

因此,保存状态信息的数据结构就是 session,一般存放于内存之中,大型服务器可能会使用 session 集群。

session 通过 session-id 跟踪用户会话,而客户端那块保存 session 的一种实现方式就是 cookie,通过一系列键值对,完成对用户的标识。


不知是该恭喜,还是该怎样,总之阅读到该文的,你是第 人。每一次刷新,都是不同的自己。