第四章 TCP/IP通信实例:访问Internet上的Web服务器
如图:
HTTP协议是一种应用层协议,它默认使用的传输层协议是TCP协议。
4.2.1 HTTP代理服务器的工作原理
在HTTP通信链上,客户端和目标服务器之间通常存在某些中转代理服务器,它们提供对目标资源的中转访问。
一个HTTP请求可能被多个代理服务器转发,后面的服务器成为前面服务器的上游服务器。
代理服务器按照其使用方式和作用,分为正向代理服务器,反向代理服务器和透明代理服务器。
正向代理要求客户端自己设置代理服务器的地址,客户的每次请求都将直接发送到该代理服务器,并由代理服务器来请求目
标资源。(例如:处于防火墙内的局域网机器要访问Internet,或者要访问一些被屏蔽掉的国外网站,就需要使用正向代理服
务器)。
反向代理则被设置在服务器端,因而客户端无须进行任何设置。
反向代理是指用代理服务器来接收Internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从内部服务器上得到
的结果返回给客户端。这种情况下,代理服务器对外就表现为一个真实的服务器。
各大网站通常分区域设置了多个代理服务器,所以在不同的地方ping同一个域名可能得到不同的IP地址,因为这些IP地址实际
上是代理服务器的IP地址。
如图:
图中,正向代理服务器和客户端主机处于同一个逻辑网络中,该逻辑网络可以是一个本地LAN,也可以是一个更大的网络。
反向代理服务器和真正的Web服务器也位于同一个逻辑网络中,这通常由提供网站的公司来配置和管理。
透明代理只能设置在网关上。用户访问 Internet 的数据报必然都经过网关,如果在网关上设置代理,则该代理对用户来说显
然是透明的。
透明代理可以看做正向代理的一种特殊情况。
代理服务器通常还提供缓存目标资源的功能(可选),这样用户下次访问同一资源时速度将很快。
4.4 访问DNS服务器
例子:
向DNS服务器查询域名 www.baidu.com对应的IP地址,并得到了回复,该回复包括一个主机别名和两个IP地址。
squid 程序通过读取 /etc/resolv.conf 文件获得DNS服务器的IP地址,然后将控制权传递给内核中的UDP模块,UDP模块将DNS
查询报文封装成UDP数据报,同时把源端口号和目的端口号加入UDP数据报头部,然后UDP模块调用IP服务,IP模块将UDP数
据报封装成IP数据报,并把源端IP地址和DNS服务器的IP地址加入IP数据报头部,接下来,IP模块查询路由表以决定如何发送
该IP数据报。
根据路由策略,目标IP地址仅能匹配路由表中的默认路由项,因此该IP数据报先被发送至路由器,然后通过路由器来转发。
若主机的ARP缓存中没有与路由器对应的缓存项,需要发起一个ARP广播以查询路由器的IP地址;路由器通过ARP应答告诉
主机自己的MAC地址;最终,以太网驱动程序将IP数据报封装成以太网帧发送给路由器。此后,代理服务器再次发送数据
到Internet时将不再需要ARP查询。
需要指出的是,虽然IP数据报是先发送给路由器,再由它转发给目标主机,但是其头部的目标IP地址却是最终的目标主机
(DNS服务器)的IP地址,而不是中转路由器的IP地址。
这说明,IP头部的源端IP地址和目的端IP地址在转发过程中始终不变(例外:源路由选择),但帧头部的源端物理地址和目
的端物理地址在转发过程中则是一直在变化。
4.5 本地名称查询
一般来说,通过域名来访问 Internet上的某台主机时,需要使用DNS服务来获取该主机的IP地址。但如果我们通过主机名来访
问本地局域网上的机器,则可通过本地的静态文件来获得该机器的IP地址。
Linux将目标主机名及其对应的IP地址存储在/etc/hosts配置文件中,当需要查询某个主机名对应的IP地址时,程序将首先检查
这个文件。
当wget访问某个Web服务器时,它先读取环境变量http_proxy,如果该环境变量被设置,并且我们没有阻止wget使用代理服
务,则wget将通过http_proxy指定的代理服务器来访问Web服务,但http_proxy环境变量中包含主机名,因此wget将首先读
取 /etc/hosts 配置文件,试图通过它来解析主机名对应的IP地址。
如果程序在 /etc/hosts 文件中未找到目标机器名对应的IP地址,它将求助于DNS 服务。
用户可以通过修改 /etc/host.conf文件来自定义系统解析主机名的方法和顺序(一般是先访问本地文件 /etc/hosts,再访问DNS
服务)。
4.6 HTTP通信
4.6.1 HTTP请求
HTTP请求的部分内容如下:
第一行是请求行,其中GET是请求方法,表示客户端以只读的方式申请资源。
常见的HTTP请求方法:
如图:
这些方法中,HEAD,GET,OPTIONS 和 TRACE 被视为安全的方法,因为它们只是从服务器获得资源或信息,而不对服务
器进行任何修改,而POST,PUT,DELETE 和 PATCH 则影响服务器上的资源。
另一方面,GET,HEAD,OPTIONS,TRACE,PUT 和 DELETE等请求方法被认为是等幂的,即多次连续的,重复的请求和
只发送一次该请求具有完全相同的效果。而POST方法则不同,连续多次发送同样一个请求可能进一步影响服务器上的资源。
注意:Linux上提供了几个命令:HEAD,GET 和 POST。其含义基本与HTTP协议中的同名请求方法相同。它们适合用来快速
测试Web服务器。
“http://www.baidu.com/index.html”是目标资源的URL。
其实“http”是所谓的scheme,表示获取目标资源需要使用的应用层协议,其他常见的 scheme 还有ftp,rtsp 和 file等。
“www.baidu.com”指定资源所在的目标主机。
“index.html”指定资源文件的名称,这里指的是服务器根目录(站点的根目录,而不是服务器的文件系统根目录“/”)中的
索引文件。
“HTTP/1.0” 表示客户端(wget程序)使用的HTTP版本号是1.0
HTTP请求内容中的第2-4行都是HTTP请求的头部字段。
一个HTTP请求可以包含多个头部字段。
一个头部字段用一行表示,包含字段名称,冒号,空格和字段的值。
HTTP请求中的头部字段可按任意顺序排列。
“User-Agent:Wget/1.12(linux-gnu)”表示客户端使用的程序是wget。
“Host:www.baidu.com”表示目标主机名是www.baidu.com。HTTP协议规定HTTP请求中必须包含的头部字段就是目标主机名。
“Connection:close”是我们执行wget命令时传入的,用以告诉服务器处理完这个HTTP请求之后就关闭连接。
在旧的HTTP协议中,Web客户端和Web服务器之间的一个TCP连接只能为一个HTTP请求服务。当处理完客户的一个HTTP请
求之后,Web服务器就主动将TCP连接关闭了。此后, 同一客户如果要在发送一个HTTP请求的话,必须与服务器建立一个
新的TCP连接。
注意:同一个客户的多个连续的HTTP请求不能共用一个TCP连接,这称为短连接。
长连接与之相反,是指多个请求可以使用同一个TCP连接。
长连接在编程上复杂,但性能上却有很大提高:极大地减少了网络上为建立TCP连接导致的负荷,同时对每次请求而言缩减
了处理时间。
HTTP请求和应答中的“connection”头部字段就是专门用于告诉对方一个请求完成之后该如何处理连接的。
例如:
1.立即关闭连接(值为“close”)
2.保持一段时间以等待后续请求(值为“keep-alive”)
在所有头部字段之后,HTTP请求必须包含一个空行,以标识头部字段的结束。
请求行和每个头部字段都必须以
符),不能有其他字符,甚至是空白字符。
在空行之后,HTTP请求可以包含可选的消息体。
如果消息体非空,则HTTP请求的头部字段中必须包含描述该消息体长度的字段“Content-Length”。
4.6.2 HTTP应答
HTTP应答的部分内容如下:
第一行是状态行。“HTTP/1.0”是服务器使用的HTTP协议的版本号。
通常,服务器需要使用和客户端相同的HTTP协议版本。
“200 OK”是状态码和状态信息。
常见状态码:
第2-7行是HTTP应答的头部字段。其表示方法是HTTP请求的头部字段相同。
“Serber:BWS/1.0”表示目标Web服务器程序的名字是BWS(Baidu Web Server)
“Content-Length:8024”表示目标文档的长度为8024字节,这个值和wget输出的文档长度一致。
“Content-Type:text/html;charset = gbk”表示目标文档的MIME类型。
其中“text”是主文档类型,“html”是子文档类型。
“text/html”表示目标文档index.html是text类型中的html文档。
“charset”是text文档类型的是一个参数,用于指定文档的字符编码。
“Set-Cookie:BAIDUID=A12S3D123HBJKH34123:FG=1;expires=Wed,04-Jul-42 00:10:47 GMT;path=/;domain=baidu.com”表示服务
器传送一个Cookie给客户端。其中,“BAIDUID”指定Cookie的名字;
“expires”指定Cookie的生存时间;“domain”和“path”指定该Cookie生效的域名和路径。
HTTP是一种无状态的协议,即每个HTTP请求之间没有任何上下文关系。
如果服务器处理后续HTTP请求时需要用到前面的HTTP请求的相关信息,客户端必须重传这些信息。这就导致HTTP请求必须
传输更多的数据。
HTTP协议的无状态特性在交互式Web应用程序上显得不适应,因为交互程序通常要承上启下。
因此,我们要使用额外的手段来保持HTTP连接状态,常见的解决方法就是Cookie。
Cookie是服务器发送给客户端的特殊信息(通过HTTP应答的头部字段“Set-Cookie”),客户端每次向服务器发送请求的时
候都需要带上这些信息(通过HTTP请求的头部字段“Cookie”)。这样服务器就可以区分不同的客户。
基于浏览器的自动登录就是用Cookie实现的。
“Via:1.0 localhost(squid/3.0 STABLE18)”表示HTTP应答在返回过程中经历过的所有代理服务器的地址和名称。
这里的localhost实际上指的是“192.168.1.108”。这个头部字段的功能有点类似于IP协议的记录路由功能。
在所有头部字段之后,HTTP应答必须包含一个空行,以标识头部字段的结束。状态行和每个头部字段都必须以
白字符。
空行之后是被请求文档index.html的内容,其长度是8024字节。
4.7 实例总结
这个实例分析了TCP/IP协议各族的部分协议:应用层的 HTTP 和 DNS,传输层的 TCP 和 UDP ,网络层的 IP ,数据链路层
的 ARP ,以及它们之间是如何协作完成网络通信的。
方法:使用tcpdump抓包,然后观察各层协议的头部内容以推断其工作原理。