|
10 网络应用程序和服务
本章探讨基本的网络应用--在用户空间运行的客户端和服务器,它们位于应用层。由于这一层位于堆栈的顶层,离最终用户很近,因此你可能会发现这部分内容比第 9 章的内容更容易理解。事实上,你每天都在与网络浏览器等网络客户端应用程序交互。
为了完成工作,网络客户端需要连接到相应的网络服务器。Unix 网络服务器有多种形式。服务器程序可以自己监听端口,也可以通过辅助服务器进行监听。我们将介绍一些常见的服务器,以及有助于了解和调试服务器运行的工具。
网络客户端使用操作系统的传输层协议和接口,因此了解 TCP 和 UDP 传输层的基础知识非常重要。让我们从使用 TCP 的网络客户端开始了解网络应用。
10.1 服务基础
TCP服务最容易理解,因为它们建立在简单、不间断的双向数据流基础之上。了解它们如何工作的最佳方法也许是直接与 TCP 80 端口上的未加密网络服务器通信,以了解数据是如何在连接中移动的。例如,运行以下命令连接到 IANA 文档示例网络服务器:您应该会得到如下响应,表明与服务器的连接成功:- $ telnet localhost 80
- Trying 127.0.0.1...
- Connected to localhost.
- Escape character is '^]'.
- ...
- $ telnet localhost 81 # 没有开放的端口
- Trying 127.0.0.1...
- telnet: Unable to connect to remote host: Connection refused
复制代码 现在输入这两行- GET / HTTP/1.1
- # 按两次ENTER键。服务器会发送一堆 HTML 文本作为回应。要终止连接,请按 CTRL-D。两次ENTER键也会退出。
- HTTP/1.1 408 Request Timeout
- Date: Fri, 26 Jul 2024 03:00:44 GMT
- Server: Apache/2.4.41 (Ubuntu)
- Content-Length: 296
- Connection: close
- Content-Type: text/html; charset=iso-8859-1
- <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
- <html><head>
- <title>408 Request Timeout</title>
- </head><body>
- <h1>Request Timeout</h1>
- <p>Server timeout waiting for the HTTP request from the client.</p>
- <hr>
- <address>Apache/2.4.41 (Ubuntu) Server at 127.0.1.1 Port 80</address>
- </body></html>
- Connection closed by foreign host.
复制代码 注意:HTTP 1.1 和它的前身 HTTP 1.0 一样,已经过时;现在使用的是 HTTP/2、QUIC 和新兴的 HTTP/3 等更新的协议。
在最后一行后按两次 ENTER 键。服务器会发送一堆 HTML 文本作为回应。要终止连接,请按 CTRL-D。
本练习演示了
- 本地网络服务器进程在监听 TCP 80 端口。
- telnet 是发起连接的客户端。
必须按 CTRL-D 键终止连接的原因是,由于大多数网页需要多次请求才能加载,因此保持连接开放是合理的。如果你在协议层面探索网络服务器,你可能会发现这种行为各不相同。例如,许多服务器在连接打开后,如果没有很快收到请求,就会迅速断开连接。
telnet 最初是用来登录远程主机的。客户端程序可能没有默认安装在你的发行版上,但很容易作为一个额外的软件包安装。尽管 telnet 远程登录服务器完全不安全(稍后将了解到),但 telnet 客户端在调试远程服务时非常有用。如果你正在寻找一个通用的网络客户端,可以考虑第 10.5.3 节中介绍的 netcat。
10.2 近距离观察
在上一个示例中,你通过 telnet 使用 HTTP 应用层协议与网络上的网络服务器进行了手动交互。虽然你通常会使用网络浏览器来进行这种连接,但让我们在 telnet 的基础上更进一步,使用一个知道如何与 HTTP 应用层对话的命令行程序。我们将使用带有特殊选项的 curl 工具来记录通信细节:- $ curl --trace-ascii trace_file http://www.example.org/
复制代码 你会得到大量 HTML 输出。忽略它(或将其重定向到 /dev/null),转而查看新创建的文件 trace_file。如果连接成功,在curl尝试与服务器建立TCP连接时,文件的第一部分应该如下所示:- == Info: Trying 93.184.216.34...
- == Info: TCP_NODELAY set
- == Info: Connected to www.example.org (93.184.216.34) port 80 (#0)
复制代码 到目前为止,你所看到的一切都发生在传输层或以下。然而,如果连接成功,curl 会尝试发送请求(“头”);这是应用层开始的地方:- => Send header, 79 bytes (0x4f)
- 2 0000: GET / HTTP/1.1
- 0010: Host: www.example.org
- 0027: User-Agent: curl/7.58.0
- 0040: Accept: */*
- 004d:
复制代码 第1行是curl的调试输出,告诉你接下来要做什么。其余几行显示了curl发送给服务器的内容。粗体文字是发送给服务器的内容;开头的十六进制数字是curl添加的调试偏移量,帮助你跟踪发送或接收了多少数据。
在第2行,你可以看到curl首先向服务器发送了一条GET命令(就像使用telnet一样),然后是一些额外的服务器信息和一行空行。接下来,服务器会发送一个回复,首先是它自己的报头:
[code] |
|