2. 基础知识
本章将介绍Web编程的基础知识。其主要目的是在通过特定语言和环境进行实践之前,先让读者了解Web编程的基本原则。文中提供了大量示例,建议读者亲自测试,以便逐步“领悟”Web开发的核心理念。
2.1. Web 应用程序的组成部分
![]() |
编号 | 作用 | 常见示例 |
OS 服务器 | Linux、Windows | |
Web 服务器 | Apache (Linux、Windows) IIS (NT)、PWS(Win9x) | |
在服务器端执行的脚本。它们可以由服务器模块 或服务器外部的程序执行(CGI)。 | PERL (Apache, IIS, PWS) VBSCRIPT (IIS,PWS) JAVASCRIPT (IIS,PWS) PHP (Apache, IIS, PWS) JAVA (Apache, IIS, PWS) C#,VB.NET (IIS) | |
数据库——该数据库可以位于与 ,也可通过互联网位于另一台机器上。 | Oracle (Linux, Windows) MySQL (Linux, Windows) Access (Windows) SQL Server (Windows) | |
OS 客户端 | Linux、Windows | |
Web 浏览器 | Netscape、Internet Explorer | |
在浏览器中客户端执行的脚本。 这些脚本无法访问客户端的磁盘。 | VBscript (IE) JavaScript (IE, Netscape) Perl脚本 (IE) 小程序 JAVA |
2.2. 带表单的Web应用程序中的数据交换
![]() |
编号 | 角色 |
浏览器首次请求 URL 时,请求的是 (http://machine/url)。未传递任何参数。 | |
Web 服务器向其发送了该 URL 的网页。 该页面可以是静态的,也可以由服务器脚本(SA)动态生成,该脚本可能使用了数据库中的内容(SB、SC)。 在此,脚本将检测到 URL 是在未传递参数的情况下被请求的,并生成初始页面 WEB。 浏览器接收该页面并进行显示(CA)。浏览器端的脚本(CB)可能已修改了服务器发送的初始页面。 随后,通过用户(CD)与脚本(CB)之间的交互,网页将被修改。特别是表单将被填写。 | |
用户提交表单数据,这些数据随后将被发送至Web服务器。 浏览器将重新请求初始的URL或根据具体情况请求其他页面,同时将表单值传输给服务器。为此,它可以使用两种方法,即GET和POST。 收到客户端请求后,服务器将触发与所请求的URL关联的脚本(SA),该脚本将检测参数并进行处理。 | |
服务器返回由程序生成的页面 WEB(SA、SB、SC)。此步骤与前面的步骤 2 完全相同。 后续交互将按照步骤2和步骤3进行。 |
2.3. 相关资源
以下提供了一份资源列表,用于安装和使用某些 Web 开发工具。附录中提供了这些工具的安装指南。
- 《Apache:安装与部署》,O'Reilly | |
- 《Perl编程》,Larry Wall,O'Reilly - 《Perl 应用》,Neuss 和 Vromans 著,O'Reilly 出版 - Active Perl 随附的文档 | |
- 《使用 PHP 进行 Web 编程》,Lacroix 著,Eyrolles 出版社 - PHP用户手册,可从PHP网站获取 | |
- WEB 与 WinNT 数据库之间的接口,Alex Homer,Eyrolles | |
- JAVA 《Servlets》,作者:Jason Hunter,O'Reilly出版社 - 《Java 网络编程》,Elliotte Rusty Harold 著,O'Reilly 出版 - 《JDBC与Java》,George Reese,O'Reilly | |
- MySQL手册可在MySQL网站上获取 - 《Linux 上的 Oracle 8i》,Gilles Briard 著,Eyrolles 出版社 - 《NT 环境下的 Oracle 8i》,Gilles Briard 著,Eyrolles 出版社 |
2.4. 符号说明
下文将假设已安装若干工具,并采用以下符号:
符号 | 含义 |
Apache 服务器树的根目录 | |
Apache 提供的网页根目录。网页必须位于此根目录下。因此,URL http://localhost/page1.htm 对应于文件 <apache-DocumentRoot>\page1.htm。 | |
与别名 cgi-bin 关联的目录树根目录,可在此放置 Apache 的 CGI 脚本。 因此,URL http://localhost/cgi-bin/test1.pl 对应于文件 <apache-cgi-bin>\test1.pl。 | |
由 PWS 生成的网页根目录。 网页必须位于此根目录下。因此,URL http://localhost/page1.htm 对应于文件 <pws-DocumentRoot>\page1.htm。 | |
Perl 语言树的根目录。可执行文件 perl.exe 通常位于 <perl>\bin. | |
PHP语言树的根节点。可执行文件php.exe通常位于<php>. | |
Java 树的根节点。与 Java 相关的可执行文件位于 <java>\bin. | |
目录下。Servlet示例位于<tomcat>\webapps\examples\servlets中,JSP和<tomcat>\webbapps\examples\jsp中则包含页面示例 |
关于这些工具的安装帮助,请参阅附录。
2.5. 静态网页、动态网页
静态网页由一个文件 HTML 表示。而动态网页则由 Web 服务器“即时”生成。在本节中,我们将通过使用不同 Web 服务器和不同编程语言进行多种测试,以展示 Web 概念的普适性。
2.5.1. 静态页面 HTML(HyperText 标记语言)
请看以下 HTML 代码:
<html>
<head>
<title>essai 1 : une page statique</title>
</head>
<body>
<center>
<h1>Une page statique...</h1>
</body>
</html>
生成了以下网页:
测试

test1
- 启动 Apache 服务器
- 将脚本 essai1.html 放入 <apache-DocumentRoot>
- 使用浏览器查看 URL http://localhost/essai1.html
- 停止 Apache 服务器
测试2
- 启动 PWS 服务器
- 将脚本 essai1.html 放入 <pws-DocumentRoot>
- 使用浏览器查看 URL http://localhost/essai1.html
2.5.2. 一个 ASP 页面(Active Server Pages)
脚本 essai2.asp:
<html>
<head>
<title>essai 1 : une page asp</title>
</head>
<body>
<center>
<h1>Une page asp générée dynamiquement par le serveur PWS</h1>
<h2>Il est <% =time %></h2>
<br>
A chaque fois que vous rafraîchissez la page, l'heure change.
</body>
</html>
生成以下网页:

测试
- 启动服务器 PWS
- 将脚本 essai2.asp 放入 <pws-DocumentRoot>
- 使用浏览器访问 URL http://localhost/essai2.asp
2.5.3. 一个名为 PERL 的脚本(实用提取与报告语言)
essai3.pl脚本:
#!d:\perl\bin\perl.exe
($secondes,$minutes,$heure)=localtime(time);
print <<HTML
Content-type: text/html
<html>
<head>
<title>essai 1 : un script Perl</title>
</head>
<body>
<center>
<h1>Une page générée dynamiquement par un script Perl</h1>
<h2>Il est $heure:$minutes:$secondes</h2>
<br>
A chaque fois que vous rafraîchissez la page, l'heure change.
</body>
</html>
HTML
;
第一行是可执行文件 perl.exe 的路径。如有需要,请进行调整。该脚本由 Web 服务器执行后,将生成以下页面:

测试
- Web 服务器:Apache
- 仅供参考,请根据Apache的版本,在<apache>\confs 目录中,并查找提及 cgi-bin 的行,以确定放置 essai3.pl 的 <apache-cgi-bin> 目录。
- 将脚本 essai3.pl 放入 <apache-cgi-bin>
- 访问网址 http://localhost/cgi-bin/essai3.pl
请注意,加载页面 perl 所需的时间比加载页面 asp 更长。这是因为 Perl 脚本由 Perl 解释器执行,而该解释器必须在执行脚本之前加载。它不会一直驻留在内存中。
2.5.4. 一个 PHP 脚本(个人主页,HyperText 处理器)
脚本 essai4.php
<html>
<head>
<title>essai 4 : une page php</title>
</head>
<body>
<center>
<h1>Une page PHP générée dynamiquement</h1>
<h2>
<?
$maintenant=time();
echo date("j/m/y, h:i:s",$maintenant);
?>
</h2>
<br>
A chaque fois que vous rafraîchissez la page, l'heure change.
</body>
</html>
上述脚本生成了以下网页:
测试


-
请查阅 Apache 的配置文件 srm.conf 或 httpd.conf(位于 <Apache>\confs 中)
-
仅供参考,请检查 php 的配置行
test1
-
启动 Apache 服务器
-
将 essai4.php 放入 <apache-DocumentRoot>
-
请求 URL 中的 http://localhost/essai4.php
test2
-
启动 PWS 服务器
-
仅供参考,检查 PWS 的 PHP 配置
-
将 essai4.php 放入 <pws-DocumentRoot>\php
-
请求 URL http://localhost/essai4.php
2.5.5. 一个 JSP 脚本(Java Server Pages)
heure.jsp脚本
<% //显示时间的 Java 程序 %>
<%@ page import="java.util.*" %>
<%
// 用于计算时间的代码 JAVA
Calendar calendrier=Calendar.getInstance();
int heures=calendrier.get(Calendar.HOUR_OF_DAY);
int minutes=calendrier.get(Calendar.MINUTE);
int secondes=calendrier.get(Calendar.SECOND);
// 时、分、秒是全局变量
// 可在代码 HTML 中使用
%>
<% // 代码 HTML %>
<html>
<head>
<title>Page JSP affichant l'heure</title>
</head>
<body>
<center>
<h1>Une page JSP générée dynamiquement</h1>
<h2>Il est <%=heures%>:<%=minutes%>:<%=secondes%></h2>
<br>
<h3>A chaque fois que vous rechargez la page, l'heure change</h3>
</body>
</html>
该脚本由Web服务器执行后,将生成以下页面:

测试
- 将脚本 heure.jsp 放置在 <tomcat>\jakarta-tomcat\webapps\examples\jsp(Tomcat 3.x)或 <tomcat>\webapps\examples\jsp(Tomcat 4.x)中
- 启动 Tomcat 服务器
- 访问 URL http://localhost:8080/examples/jsp/heure.jsp
2.5.6. 结论
前面的示例表明:
- HTML 页面可由程序动态生成。这正是 Web 编程的全部意义所在。
- 所使用的语言和Web服务器可以多种多样。目前主要呈现以下趋势:
- Apache/PHP(Windows、Linux)和 IIS/PHP(Windows)组合
- 在Windows平台上,将IIS服务器与.NET语言(如C#、VB.NET等)结合使用的ASP.NET技术
- Java Servlet 技术及 JSP 页面,可在不同服务器(Tomcat、Apache、IIS)和不同平台(Windows、Linux)上运行。本文将重点探讨后一种技术。
2.6. 浏览器端脚本
HTML 页面可包含由浏览器执行的脚本。浏览器端脚本语言种类繁多,以下列举部分:
语言 | 支持的浏览器 |
VBScript | IE |
Javascript | IE, Netscape |
PerlScript | IE |
Java | IE, Netscape |
让我们来看几个例子。
2.6.1. 一个包含 Vbscript 脚本的网页,在浏览器端
页面 vbs1.html
<html>
<head>
<title>essai : une page web avec un script vb</title>
<script language="vbscript">
function reagir
alert "Vous avez cliqué sur le bouton OK"
end function
</script>
</head>
<body>
<center>
<h1>Une page Web avec un script VB</h1>
<table>
<tr>
<td>Cliquez sur le bouton</td>
<td><input type="button" value="OK" name="cmdOK" onclick="reagir"></td>
</tr>
</table>
</body>
</html>
上方的 HTML 页面不仅包含 HTML 代码,还包含一个供加载该页面的浏览器执行的程序。代码如下:
<script language="vbscript">
function reagir
alert "Vous avez cliqué sur le bouton OK"
end function
</script>
<script></script> 标签用于在 HTML 页面中界定脚本。 这些脚本可以使用不同的语言编写,而 <script> 标签中的 language 选项则指定了所使用的语言。此处为 VBScript。 我们在此不作详细说明。上述脚本定义了一个名为 réagir 的函数,用于显示一条消息。该函数何时被调用?下方的代码行 HTML 给出了答案:
onclick 属性指定了当用户点击 OK 按钮时要调用的函数名称。当浏览器加载此页面后,用户点击 OK 按钮,将显示以下页面:

测试
只有 IE 浏览器能够执行 VBScript 脚本。Netscape 需要安装插件才能执行。可以进行以下测试:
测试1
- Apache 服务器
- vbs1.html脚本位于<apache-DocumentRoot>中
- 使用 IE 浏览器访问网址 http://localhost/vbs1.html
测试2
- PWS 服务器
- vbs1.html脚本位于<pws-DocumentRoot>中
- 使用浏览器 IE 访问网址 http://localhost/vbs1.html
2.6.2. 一个包含JavaScript脚本的网页,在浏览器端
该页面:js1.html
<html>
<head>
<title>essai 4 : une page web avec un script Javascript</title>
<script language="javascript">
function reagir(){
alert ("Vous avez cliqué sur le bouton OK");
}
</script>
</head>
<body>
<center>
<h1>Une page Web avec un script Javascript</h1>
<table>
<tr>
<td>Cliquez sur le bouton</td>
<td><input type="button" value="OK" name="cmdOK" onclick="reagir()"></td>
</tr>
</table>
</body>
</html>
这与上一页的内容完全相同,只是将VBScript语言替换为JavaScript语言。JavaScript的优势在于它既被IE浏览器也支持,同时也兼容Netscape浏览器。执行后得到的结果相同:

测试
test1
- Apache服务器
- js1.html脚本在<apache-DocumentRoot>中
- 使用 IE 或 Netscape 浏览器访问网址 http://localhost/js1.html
测试2
- PWS 服务器
- js1.html 脚本位于 <pws-DocumentRoot> 中
- 使用浏览器 IE 或 Netscape 访问网址 http://localhost/js1.html
2.7. 客户端与服务器的交互
让我们回到最初的示意图,该图展示了Web应用程序中的参与者:
![]() |
本文重点探讨客户端与服务器端之间的交互。这些交互通过网络进行,在此有必要回顾两台远程机器之间交互的一般结构。
2.7.1. OSI 模型
由国际标准化组织(ISO)定义的开放系统互连参考模型(OSI)描述了一个理想网络,其中机器间的通信可通过七层模型来表示:
![]() |
每一层从下层接收服务,并向上层提供服务。假设位于不同机器A和B上的两个应用程序想要通信:它们在Application层进行通信。 它们无需了解网络运行的所有细节:每个应用程序将想要传输的信息交给下层——即 Présentation 层。因此,应用程序只需了解与 Présentation 层交互的规则即可。 一旦信息进入Présentation层,它就会根据其他规则传递到Session层,以此类推,直到信息到达物理介质并被物理传输到目标机器。 在那里,它将经历与在发送方机器上所经历的相反的处理过程。
在每一层,负责发送信息的发送方进程将其发送给另一台机器上属于同一层的接收方进程。这一过程遵循某些规则,即所谓的“该层协议”。因此,最终的通信图示如下:
![]() |
各层的作用如下:
负责在物理介质上传输比特。 该层包含数据处理终端设备(E.T.T.D.),如终端或计算机,以及数据电路终端设备(E.T.C.D.),如调制解调器、多路复用器和集中器。该层的主要关注点包括: . 信息编码方式的选择(模拟或数字) . 传输模式的选择(同步或异步)。 | |
隐藏物理层的物理特性。检测并纠正传输错误。 | |
管理通过网络发送的信息应遵循的路径。这被称为routage:确定信息应遵循的路径,以便其到达收件人。 | |
允许两个应用程序之间进行通信,而之前的层仅支持机器之间的通信。该层提供的一项服务可能是多路复用:传输层可以使用同一条网络连接(机器到机器)来传输属于多个应用程序的信息。 | |
该层包含允许应用程序在远程机器上建立并维持工作会话的服务。 | |
该层旨在统一不同机器上的数据表示形式。因此,来自机器A的数据将在发送至网络前,由机器A的Présentation层按照标准格式进行“包装”。 数据到达目标机器B的Présentation层后,该层将通过标准格式识别数据,并对其进行重新包装,以便机器B上的应用程序能够识别。 | |
在此层级,通常存在与用户密切相关的应用程序,例如电子邮件或文件传输。 |
2.7.2. TCP/IP 模型
OSI 模型是一个理想的模型。TCP/IP 协议套件以如下形式接近该模型:
![]() |
- 网络接口(计算机的网卡)承担 OSI 模型中第 1 层和第 2 层的功能
- IP层(互联网协议)承担第3层(网络层)的功能
- TCP层(传输控制协议)或UDP层(用户数据报协议)承担第4层(传输层)的功能。 TCP协议确保机器间交换的数据包能顺利到达目的地。若未能送达,则会将丢失的数据包重新发送。 UDP协议不承担此项工作,因此需要由应用程序开发人员来处理。正因如此,在并非100%可靠的互联网环境中,TCP协议才是最常用的。 因此,我们称之为 TCP-IP 网络。
- 应用层涵盖了 OSI 模型中第 5 至第 7 层的功能。
Web 应用程序位于 Application 层,因此基于 TCP-IP 协议。 客户端和服务器端的 Application 层之间交换消息,这些消息由模型的第 1 至第 4 层负责转发至目的地。为了相互理解,两台机器的应用程序层必须“使用”相同的语言或协议。 Web应用程序所用的协议称为HTTP(HyperText传输协议)。这是一种文本型协议,即c.a.d,机器通过在网络上交换文本行来实现互通。 这些交互是标准化的,即客户端拥有若干消息来向服务器精确表达其需求,而服务器也拥有若干消息向客户端返回响应。这种消息交互的形式如下:
![]() |
客户端 --> 服务器
当客户端向Web服务器发出请求时,会发送
- 格式为 HTTP 的文本行来表明其需求
- 空行
- 可选的文档
服务器 --> 客户端
当服务器向客户端响应时,它会发送
- 格式为 HTTP 的文本行,以说明其发送的内容
- 一行空行
- 可选地发送一个文档
因此,双向通信采用相同的格式。在两种情况下,都可能发送文档,尽管客户端向服务器发送文档的情况较为罕见。 但 HTTP 协议对此有所规定。正因如此,例如互联网服务提供商的用户才能将各类文档上传至该服务商托管的个人网站。交换的文档可以是任意类型的。以浏览器请求包含图片的网页为例:
- 浏览器连接到Web服务器并请求所需的页面。所请求的资源通过URL(统一资源定位符)进行唯一标识。浏览器仅发送HTTP头部信息,而不发送任何文档。
- 服务器向其响应。它首先发送 HTTP 头部,表明将发送何种类型的响应。如果请求的页面不存在,则可能返回错误。 如果页面存在,服务器将在响应的 HTTP 头部中说明,随后将发送一个 HTML 文档(HyperText 标记语言)。 该文档由一系列 HTML 格式的文本行组成。HTML 文本包含标签(标记),这些标签向浏览器提供如何显示文本的指示。
- 客户端根据服务器的 HTTP 标头得知,它将收到一个 HTML 文档。 它将分析该文档,并可能发现其中包含图片引用。这些图片并不在 HTML 文档中。因此,它向同一台 Web 服务器发出新请求,以获取所需的第一个图片。该请求与步骤 1 中的请求完全相同,只是请求的资源不同。 服务器将处理此请求,向客户端发送所请求的图片。此次,在响应中,HTTP的头部信息将明确指出所发送的文档是一张图片,而非HTML文档。
- 客户端接收所发送的图像。步骤3和4将不断重复,直到客户端(通常是浏览器)获取到所有能够完整显示该页面的文档为止。
2.7.3. HTTP协议
让我们通过实例来了解 HTTP 协议。浏览器和 Web 服务器之间交换什么?
2.7.3.1. HTTP服务器的响应
我们将在此了解 Web 服务器如何响应客户端的请求。Web 服务或 HTTP 服务通常在 80 端口上运行。它也可能在其他端口上运行。 在这种情况下,客户端浏览器必须在其发出的请求中明确指定该端口。一个请求的一般形式如下:
其中
协议 | http。浏览器还可以作为FTP、新闻、Telnet等服务的客户端。 |
主机 | 运行 Web 服务的机器名称 |
端口 | Web 服务的端口号。如果端口号是 80,则可以省略端口号。这是最常见的情况 |
路径 | 指向所请求资源的路径 |
信息 | 提供给服务器的补充信息,用于明确客户端的请求 |
当用户请求加载 URL 时,浏览器会做什么?
- 它会与 URL 文件中 machine[:port] 部分指定的机器和端口建立 TCP-IP 通信。 建立 TCP-IP 通信,即是在两台机器之间创建一条通信“管道”。一旦该管道建立,两台机器之间交换的所有信息都将通过它传输。 创建这条通道TCP-IP尚不涉及Web协议HTTP。
- TCP-IP通道建立后,客户端将向Web服务器发起请求,并通过发送HTTP格式的文本行(即命令)来完成。它将向服务器发送URL中的路径/信息部分
- 服务器将通过同一管道以相同方式进行响应
- 双方中的一方将决定关闭该通道。这取决于所使用的 HTTP 协议。 在 HTTP 1.0 协议下,服务器会在每次响应后关闭连接。这迫使需要多次请求以获取构成网页的各个文档的客户端,在每次请求时都必须建立新的连接,从而产生额外开销。 在 HTTP/1.1 协议下,客户端可以指示服务器保持连接开放,直到客户端要求关闭为止。因此,客户端可以通过单次连接获取网页的所有文档,并在获取最后一个文档后自行关闭连接。服务器将检测到此关闭操作,并随之关闭连接。
为了了解客户端与Web服务器之间的交互,我们将使用一个通用的TCP客户端。这是一个可以作为任何基于文本行通信协议的服务客户端的程序,例如HTTP协议。 这些文本行将由用户通过键盘输入。这要求用户了解其试图访问的服务所采用的通信协议。随后,服务器的响应将显示在屏幕上。该程序采用Java编写,附于本文末尾。在此,我们将其用于Windows系统的DOS窗口中,并按以下方式调用:
其中
机器 | 表示目标服务所在的主机名 |
端口 | 服务提供的端口 |
凭借这两项信息,程序将与指定的机器和端口建立名为 TCP-IP 的连接。该连接将用于客户端与 Web 服务器之间交换文本行。 客户端的文本行由用户通过键盘输入并发送至服务器。服务器作为响应返回的文本行将显示在屏幕上。因此,键盘操作的用户与Web服务器之间可以建立直接对话。让我们尝试一下之前介绍的示例。我们创建了以下静态页面 HTML:
<html>
<head>
<title>essai 1 : une page statique</title>
</head>
<body>
<center>
<h1>Une page statique...</h1>
</body>
</html>
我们在浏览器中看到的代码如下:

可见,所请求的 URL 实际为:http://localhost:81/essais/essai1.html。 因此,Web服务器的IP地址是localhost(=本地机器),端口是81。如果查看该网页的源代码(视图/源代码),会发现最初创建的文本HTML:

现在,让我们使用通用的 TCP 客户端来请求相同的 URL:
Dos>java clientTCPgenerique localhost 81
Commandes :
GET /essais/essai1.html HTTP/1.0
<-- HTTP/1.1 200 OK
<-- Date: Mon, 08 Jul 2002 08:07:46 GMT
<-- Server: Apache/1.3.24 (Win32) PHP/4.2.0
<-- Last-Modified: Mon, 08 Jul 2002 08:00:30 GMT
<-- ETag: "0-a1-3d29469e"
<-- Accept-Ranges: bytes
<-- Content-Length: 161
<-- Connection: close
<-- Content-Type: text/html
<--
<-- <html>
<-- <head>
<-- <title>essai 1 : une page statique</title>
<-- </head>
<-- <body>
<-- <center>
<-- <h1>Une page statique...</h1>
<-- </body>
<-- </html>
通过命令 <mark style="background-color: #ffe994">java clientTCPgenerique localhost 81</mark> 启动客户端时,会在程序与运行在同一台机器(localhost)81 端口的 Web 服务器之间建立连接。此时,以 HTTP 格式进行的客户端-服务器通信即可开始。需要提醒的是,这些通信包含三个组成部分:
- HTTP 报头
- 空行
- 可选数据
在本例中,客户端仅发送一个请求:
GET /essais/essai1.html HTTP/1.0
该行包含三个部分:
命令 HTTP 用于请求资源。还有其他命令: HEAD 请求资源,但仅限于服务器响应中的 HTTP 头部信息。资源本身不会被发送。 PUT 允许客户端向服务器发送文档 | |
请求的资源 | |
所使用的 HTTP 协议版本。此处为 1.0。这意味着服务器在发送响应后将立即关闭连接 |
HTTP 头部之后必须始终跟一个空行。客户端在此处已按此操作。正是通过这种方式,客户端或服务器才知晓 HTTP 部分的交换已经结束。 此时客户端已完成操作,无需发送任何文档。随后开始服务器响应,在本例中,响应由所有以 <-- 开头的行组成。服务器首先发送一系列 HTTP 头部,随后是一行空行:
<-- HTTP/1.1 200 OK
<-- Date: Mon, 08 Jul 2002 08:07:46 GMT
<-- Server: Apache/1.3.24 (Win32) PHP/4.2.0
<-- Last-Modified: Mon, 08 Jul 2002 08:00:30 GMT
<-- ETag: "0-a1-3d29469e"
<-- Accept-Ranges: bytes
<-- Content-Length: 161
<-- Connection: close
<-- Content-Type: text/html
<--
服务器表示
| |
响应的日期/时间 | |
服务器进行身份验证。此处为 Apache 服务器 | |
客户端请求的资源最后修改日期 | |
... | |
所发送数据的计量单位。此处为字节(byte) | |
在HTTP头部之后将发送的文档的字节数。该数值实际上是essai1.html文件的字节大小: | |
服务器表示文件发送完成后将关闭连接 | |
服务器表示将发送格式为 HTML(html)的文本。 |
客户端接收到这些标头 HTTP,现在知道它将接收 161 个字节,这些字节代表一个文档 HTML。服务器在表示标头结束的空行之后立即发送这 161 个字节 HTTP:
<-- <html>
<-- <head>
<-- <title>essai 1 : une page statique</title>
<-- </head>
<-- <body>
<-- <center>
<-- <h1>Une page statique...</h1>
<-- </body>
<-- </html>
这里可以看到最初生成的文件 HTML。如果我们的客户端是一个浏览器,在接收这些文本行后,它会进行解析,并在用户键盘上显示以下页面:

让我们再次使用通用客户端 TCP 请求同一资源,但这次使用 HEAD 命令,该命令仅请求响应的头部信息:
Dos>java.bat clientTCPgenerique localhost 81
Commandes :
HEAD /essais/essai1.html HTTP/1.1
Host: localhost:81
<-- HTTP/1.1 200 OK
<-- Date: Mon, 08 Jul 2002 09:07:25 GMT
<-- Server: Apache/1.3.24 (Win32) PHP/4.2.0
<-- Last-Modified: Mon, 08 Jul 2002 08:00:30 GMT
<-- ETag: "0-a1-3d29469e"
<-- Accept-Ranges: bytes
<-- Content-Length: 161
<-- Content-Type: text/html
<--
在不使用文档 HTML 的情况下,我们得到了与之前相同的结果。请注意,在请求 HEAD 中,客户端已声明其使用的是 HTTP 协议的 1.1 版本。 这要求其发送第二个 HTTP 头,明确指定客户希望查询的 machine:port 对:Host: localhost:81。
现在,让我们分别使用浏览器和通用客户端 TCP 请求一张图片。首先使用浏览器:

文件 univ01.gif 大小为 3167 字节:
现在使用通用客户端 TCP:
E:\data\serge\JAVA\SOCKETS\client générique>java clientTCPgenerique localhost 81
Commandes :
HEAD /images/univ01.gif HTTP/1.1
host: localhost:81
<-- HTTP/1.1 200 OK
<-- Date: Tue, 09 Jul 2002 13:53:24 GMT
<-- Server: Apache/1.3.24 (Win32) PHP/4.2.0
<-- Last-Modified: Fri, 14 Apr 2000 11:37:42 GMT
<-- ETag: "0-c5f-38f70306"
<-- Accept-Ranges: bytes
<-- Content-Length: 3167
<-- Content-Type: image/gif
<--
请注意服务器响应中的以下几点:
| |
| |
|
2.7.3.2. 来自客户端的请求 HTTP
现在,让我们思考以下问题:如果我们要编写一个能够与Web服务器“对话”的程序,它需要向Web服务器发送哪些命令才能获取特定资源?在之前的示例中,我们已经得到了初步的答案。我们遇到了三种命令:
| |
| |
|
还有其他命令。为了探索这些命令,我们将使用一个通用的 TCP 服务器。这是一个用 Java 编写的程序,您也可以在附录中找到它。 其启动命令为:java serveurTCPgenerique portEcoute,其中 portEcoute 是客户端需连接的端口。程序 serveurTCPgenerique
- 会在屏幕上显示客户端发送的命令
- 并将用户通过键盘输入的文本行作为响应发送给客户端。因此,该程序实际上充当了服务器。在本例中,使用键盘的用户将扮演 Web 服务的角色。
现在,让我们通过在 88 端口上启动通用服务器来模拟一个 Web 服务器:
Dos> java serveurTCPgenerique 88
Serveur générique lancé sur le port 88
现在打开浏览器,访问 URL http://localhost:88/exemple.html。 浏览器将连接到 localhost 机器的 88 端口,然后请求 /exemple.html 页面:

现在让我们查看服务器的窗口,它显示了客户端发送的内容(为简化起见,省略了与程序 serveurTCPgenerique 运行相关的某些特定行):
Dos>java serveurTCPgenerique 88
Serveur générique lancé sur le port 88
...
<-- GET /exemple.html HTTP/1.1
<-- Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/msword, */*
<-- Accept-Language: fr
<-- Accept-Encoding: gzip, deflate
<-- User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET CLR 1.0.3705; .NET CLR 1.0.2 914)
<-- Host: localhost:88
<-- Connection: Keep-Alive
<--
以 <-- 符号开头的行是客户端发送的。由此我们发现了此前未曾见过的 HTTP 头部:
| |
| |
| |
| |
|
浏览器发送的 HTTP 头部信息如预期般以空行结尾。
现在我们来为客户端构建一个响应。此时,键盘操作的用户就是真正的服务器,可以手动编写响应。回顾一下前一个示例中 Web 服务器生成的响应:
<-- HTTP/1.1 200 OK
<-- Date: Mon, 08 Jul 2002 08:07:46 GMT
<-- Server: Apache/1.3.24 (Win32) PHP/4.2.0
<-- Last-Modified: Mon, 08 Jul 2002 08:00:30 GMT
<-- ETag: "0-a1-3d29469e"
<-- Accept-Ranges: bytes
<-- Content-Length: 161
<-- Connection: close
<-- Content-Type: text/html
<--
<-- <html>
<-- <head>
<-- <title>essai 1 : une page statique</title>
<-- </head>
<-- <body>
<-- <center>
<-- <h1>Une page statique...</h1>
<-- </body>
<-- </html>
让我们尝试手动(通过键盘)编写一个类似的响应。以 --> : 开头的行将发送给客户端:
...
<-- Host: localhost:88
<-- Connection: Keep-Alive
<--
--> : HTTP/1.1 200 OK
--> : Server: serveur tcp generique
--> : Connection: close
--> : Content-Type: text/html
--> :
--> : <html>
--> : <head><title>Serveur generique</title></head>
--> : <body>
--> : <center>
--> : <h2>Reponse du serveur generique</h2>
--> : </center>
--> : </body>
--> : </html>
fin
命令 fin 专用于程序 serveurTCPgenerique 的运行。它将停止程序的执行并关闭服务器与客户端的连接。我们在回答中仅限于以下 HTTP 报头:
HTTP/1.1 200 OK
--> : Server: serveur tcp generique
--> : Connection: close
--> : Content-Type: text/html
--> :
我们并未提供即将发送的文件大小(Content-Length),而是仅告知在发送该文件后将关闭连接(Connection: close)。这对浏览器而言已足够。 当浏览器检测到连接已关闭时,便会知道服务器的响应已完成,并显示收到的页面 HTML。该页面内容如下:
--> : <html>
--> : <head><title>Serveur generique</title></head>
--> : <body>
--> : <center>
--> : <h2>Reponse du serveur generique</h2>
--> : </center>
--> : </body>
--> : </html>
浏览器随后显示以下页面:

如果在上文中执行 View/Source 以查看浏览器接收到的内容,结果如下:

即与从通用服务器发送的内容完全一致。
2.8. HTML 语言
Web浏览器可以显示各种文档,最常见的是HTML文档(HyperText标记语言)。该文档是一种使用<balise>texte</balise>形式标签进行格式化的文本。 因此,文本 <B>important</B> 将把重要内容显示为粗体。还有一些独立标签,例如标签 <hr>,它会显示一条水平线。我们不会逐一介绍 HTML 文本中可能出现的标签。 市面上有许多WYSIWYG软件,可让您无需编写任何代码HTML即可构建网页。这些工具会自动生成HTML代码,用于实现通过鼠标和预定义控件设计的版面布局。 因此,用户可以(通过鼠标)在页面中插入一个表格,然后查看软件生成的代码,从而了解在网页中定义表格应使用的标签。这并不复杂。 此外,掌握HTML语言是必不可少的,因为动态Web应用程序必须自行生成HTML代码并发送给Web客户端。该代码由程序生成,当然必须清楚需要生成什么内容,才能让客户端获得其期望的网页。
总而言之,开始进行Web编程时,完全无需掌握HTML语言的全部内容。 不过,掌握该语言是必要的,且可通过使用Word、FrontPage、DreamWeaver等数十种WYSIWYG网页构建软件来学习。 探索 HTML 语言精妙之处的另一种方法是浏览网页,并查看那些具有有趣且您尚未了解的特性的网页的源代码。
2.8.1. 一个示例
让我们来看以下示例,该示例使用 FrontPage Express 创建,这是一款随 Internet Explorer 附带的免费工具。此处已对 Frontpage 生成的代码进行了精简。该示例展示了网页文档中可能包含的若干元素,例如:
- 一个表格
- 一张图片
- 一个链接

HTML文档的一般格式如下:
整个文档由<html>...</html>标签包围。它由两部分组成:
- <head>...</head>:这是文档中不可显示的部分。它向将要显示该文档的浏览器提供信息。其中通常包含 <title>...</title> 标签,用于设定浏览器标题栏中显示的文本。此外还可能包含其他标签,特别是定义文档关键词的标签,这些关键词随后会被搜索引擎使用。 该部分还可能包含脚本,通常采用JavaScript或VBScript编写,并将由浏览器执行。
- <body 属性>...</body>:这是浏览器将显示的部分。该部分包含的标签会向浏览器指示文档的“预期”视觉呈现形式。每个浏览器都会以自己的方式解释这些标签。 因此,同一网页文档在不同浏览器中的显示效果可能存在差异。这通常是网页设计师面临的难题之一。
我们示例文档的代码如下:
<html>
<head>
<title>balises</title>
</head>
<body background="/images/standard.jpg">
<center>
<h1>Les balises HTML</h1>
<hr>
</center>
<table border="1">
<tr>
<td>cellule(1,1)</td>
<td valign="middle" align="center" width="150">cellule(1,2)</td>
<td>cellule(1,3)</td>
</tr>
<tr>
<td>cellule(2,1)</td>
<td>cellule(2,2)</td>
<td>cellule(2,3</td>
</tr>
</table>
<table border="0">
<tr>
<td>Une image</td>
<td><img border="0" src="/images/univ01.gif" width="80" height="95"></td>
</tr>
<tr>
<td>le site de l'ISTIA</td>
<td><a href="http://istia.univ-angers.fr">ici</a></td>
</tr>
</table>
</body>
</html>
代码中仅突出了我们感兴趣的部分:
标签 | 标签及示例 HTML |
<title>balises</title> balises 将显示在显示该文档的浏览器标题栏中 | |
<hr>:显示一条水平线 | |
<table 属性>....</table>:用于定义表格 <tr 属性>...</tr>:用于定义一行 <td 属性>...</td>:用于定义单元格 示例: <table border="1">...</table>:border 属性定义表格边框的粗细 <td valign="middle" align="center" width="150">单元格(1,2)</td>:定义一个内容为单元格(1,2)的单元格。该内容将垂直居中(valign="middle")并水平居中(align="center")。该单元格的宽度为150像素(width="150") | |
<img border="0" src="/images/univ01.gif" width="80" height="95">:定义了一张无边框(border="0")的图片,高度为95像素(height="95"), 宽度为80像素(width="80"),其源文件位于Web服务器上的/images/univ01.gif(src="/images/univ01.gif")。 该链接位于一个通过 URL 生成的网页文档中 http://localhost:81/html/balises.htm。 因此,浏览器将请求 URL http://localhost:81/images/univ01.gif 以获取此处引用的图片。 | |
<a href="http://istia.univ-angers.fr">此处</a>:使文本 ici 作为指向 URL http://istia.univ-angers.fr 的链接。 | |
<body background="/images/standard.jpg">:表示用作页面背景的图片位于Web服务器上的URL /images/standard.jpg。 在本例中,浏览器将请求 URL http://localhost:81/images/standard.jpg 以获取该背景图片。 |
从这个简单的例子可以看出,为了生成完整的文档,浏览器必须向服务器发送三次请求:
- http://localhost:81/html/balises.htm 以获取文档
- http://localhost:81/images/univ01.gif 用于获取图片 univ01.gif
- http://localhost:81/images/standard.jpg 获取背景图片 standard.jpg
以下示例展示了一个同样使用 FrontPage 创建的 Web 表单。

由 FrontPage 生成并稍作精简的代码如下:
<html>
<head>
<title>balises</title>
<script language="JavaScript">
function effacer(){
alert("Vous avez cliqué sur le bouton Effacer");
}//删除
</script>
</head>
<body background="/images/standard.jpg">
<form method="POST" >
<table border="0">
<tr>
<td>Etes-vous marié(e)</td>
<td>
<input type="radio" value="Oui" name="R1">Oui
<input type="radio" name="R1" value="non" checked>Non
</td>
</tr>
<tr>
<td>Cases à cocher</td>
<td>
<input type="checkbox" name="C1" value="un">1
<input type="checkbox" name="C2" value="deux" checked>2
<input type="checkbox" name="C3" value="trois">3
</td>
</tr>
<tr>
<td>Champ de saisie</td>
<td>
<input type="text" name="txtSaisie" size="20" value="qqs mots">
</td>
</tr>
<tr>
<td>Mot de passe</td>
<td>
<input type="password" name="txtMdp" size="20" value="unMotDePasse">
</td>
</tr>
<tr>
<td>Boîte de saisie</td>
<td>
<textarea rows="2" name="areaSaisie" cols="20">
ligne1
ligne2
ligne3
</textarea>
</td>
</tr>
<tr>
<td>combo</td>
<td>
<select size="1" name="cmbValeurs">
<option>choix1</option>
<option selected>choix2</option>
<option>choix3</option>
</select>
</td>
</tr>
<tr>
<td>liste à choix simple</td>
<td>
<select size="3" name="lst1">
<option selected>liste1</option>
<option>liste2</option>
<option>liste3</option>
<option>liste4</option>
<option>liste5</option>
</select>
</td>
</tr>
<tr>
<td>liste à choix multiple</td>
<td>
<select size="3" name="lst2" multiple>
<option>liste1</option>
<option>liste2</option>
<option selected>liste3</option>
<option>liste4</option>
<option>liste5</option>
</select>
</td>
</tr>
<tr>
<td>bouton</td>
<td>
<input type="button" value="Effacer" name="cmdEffacer" onclick="effacer()">
</td>
</tr>
<tr>
<td>envoyer</td>
<td>
<input type="submit" value="Envoyer" name="cmdRenvoyer">
</td>
</tr>
<tr>
<td>rétablir</td>
<td>
<input type="reset" value="Rétablir" name="cmdRétablir">
</td>
</tr>
</table>
<input type="hidden" name="secret" value="uneValeur">
</form>
</body>
</html>
视觉检查与标签 <--> HTML 的对应关系如下:
检查 | HTML 标签 |
<form method="POST" > | |
<input type="text" name="txtSaisie" size="20" value="几个字"> | |
<input type="password" name="txtMdp" size="20" value="unMotDePasse"> | |
<textarea rows="2" name="areaSaisie" cols="20"> 行1 行2 第3行 </textarea> | |
<input type="radio" value="是" name="R1">是 <input type="radio" name="R1" value="否" checked>否 | |
<input type="checkbox" name="C1" value="一">1 <input type="checkbox" name="C2" value="二" checked>2 <input type="checkbox" name="C3" value="三">3 | |
<select size="1" name="cmbValeurs"> <option>选项1</option> <option selected>选项2</option> <option>选项3</option> </select> | |
<select size="3" name="lst1"> <option selected>列表1</option> <option>列表2</option> <option>列表3</option> <option>列表4</option> <option>列表5</option> </select> | |
<select size="3" name="lst2" multiple> <option>列表1</option> <option>列表2</option> <option selected>列表3</option> <option>列表4</option> <option>列表5</option> </select> | |
<input type="submit" value="发送" name="cmdRenvoyer"> | |
<input type="reset" value="重置" name="cmdRétablir"> | |
<input type="button" value="清除" name="cmdEffacer" onclick="effacer()"> |
让我们来回顾一下这些不同的控件。
2.8.1.1. 表单
<form method="POST" > |
<form name="..." method="..." action="...">...</form> | |
name="frmexemple":表单名称 method="...":浏览器用于将表单中收集的值发送至Web服务器的传输方式 action="...":URL,表单中收集的值将发送至此处。 网页表单由 <form>...</form> 标签包围。表单可以有一个名称(name="xx")。表单中所有控件均适用此规则。如果网页文档中包含需要引用表单元素的脚本,该名称将非常有用。 表单的目的是收集用户通过键盘/鼠标输入的信息,并将这些信息发送至某个Web服务器上的URL。具体是哪个?即action="URL"属性中引用的那个。 如果缺少该属性,信息将发送至包含该表单的文档所属的Web服务器。上述示例即属于这种情况。到目前为止,我们一直将Web客户端视为向Web服务器“请求”信息,而从未将其视为向Web服务器“提供”信息。 那么,Web客户端是如何将信息(即表单中的内容)提供给Web服务器的呢?我们稍后将详细探讨。它可以使用两种不同的方法,分别称为POST和GET。 method="méthode" 属性(其 method 属性值为 GET 或 POST) 该标签中的 <form> 属性会指示浏览器使用哪种方法,将表单中收集的信息发送至由 action="URL" 属性指定的 URL。 当未指定 action="method" 属性时,默认采用 action="GET" 方法。 |
2.8.1.2. 输入字段
![]()
![]()
<input type="text" name="txtSaisie" size="20" value="几个字"> <input type="password" name="txtMdp" size="20" value="unMotDePasse"> |
<input type="..." name="..." size=".." value=".."> input 标签适用于各种控件。正是 type 这个属性,使得这些不同的控件能够相互区分。 | |
type="text":指定这是一个文本输入框 type="password":输入框中的字符将显示为星号(*)。这是它与普通输入框的唯一区别。此类控件适用于密码输入。 size="20":字段中显示的字符数——不限制输入更多字符 name="txtSaisie":控件名称 value="几句话":将在输入框中显示的文本。 |
2.8.1.3. 多行输入框
![]()
<textarea rows="2" name="areaSaisie" cols="20"> ligne1 ligne2 ligne3 </textarea> |
<textarea ...>文本</textarea> 显示一个多行输入框,初始内容为文本 | |
rows="2":行数 cols="'20":列数 name="areaSaisie":控件名称 |
2.8.1.4. 单选按钮
![]()
<input type="radio" value="是" name="R1">是 <input type="radio" name="R1" value="no" checked>否 |
<input type="radio" attribut2="valeur2" ....>文本 显示一个单选按钮,旁边带有文本。 | |
name="radio":控件名称。名称相同的单选按钮构成互斥的按钮组:只能选中其中一个。 value="值":分配给单选按钮的值。请勿将此值与单选按钮旁显示的文本混淆,后者仅用于显示。 checked:如果存在此关键字,单选按钮处于选中状态;否则未选中。 |
2.8.1.5. 复选框
<input type="checkbox" name="C1" value="一">1 <input type="checkbox" name="C2" value="二" checked>2 <input type="checkbox" name="C3" value="三">3 |
![]()
<input type="checkbox" attribut2="value2" ....>文本 显示一个复选框,旁边带有文本。 | |
name="C1":控件名称。复选框可以具有相同的名称,也可以不具有相同的名称。具有相同名称的复选框构成一组关联的复选框。 value="值":分配给复选框的值。请勿将此值与单选按钮旁显示的文本混淆,后者仅用于显示。 checked:如果存在此关键字,单选按钮处于选中状态;否则未选中。 |
2.8.1.6. 下拉列表(组合框)
<select size="1" name="cmbValeurs"> <option>choix1</option> <option selected>选项2</option> <option>choix3</option> </select> |
![]()
<select size=".." name=".."> <option [selected]>...</option> ... </select> 将位于 <option>...</option> 标签之间的文本显示为列表 | |
name="cmbValeurs":控件名称。 size="1":可见列表项的数量。size="1" 使该列表相当于一个下拉列表框。 selected:如果某个列表项包含此关键字,该项在列表中将显示为已选中。在上例中,当下拉列表首次显示时,列表项 choix2 将作为下拉列表的选中项显示。 |
2.8.1.7. 单选列表
<select size="3" name="lst1"> <option selected>列表1</option> <option>liste2</option> <option>liste3</option> <option>liste4</option> <option>liste5</option> </select> |

<select size=".." name=".."> <option [selected]>...</option> ... </select> 在列表中显示位于 <option>...</option> 标签之间的文本 | |
之间的文本,与仅显示一个项的下拉列表相同。该控件与前一个下拉列表的唯一区别在于其 size>1 属性。 |
2.8.1.8. 多选列表
<select size="3" name="lst2" multiple> <option selected>列表1</option> <option>liste2</option> <option selected>列表3</option> <option>liste4</option> <option>liste5</option> </select> |

<select size=".." name=".." multiple> <option [selected]>...</option> ... </select> 在列表中显示位于 <option>...</option> 标签之间的文本 | |
多选:允许在列表中选择多个项目。在上例中,liste1 和 liste3 这两个项目均被选中。 |
2.8.1.9. button 类型按钮
<input type="button" value="清除" name="cmdEffacer" onclick="effacer()"> |
![]()
<input type="button" value="..." name="..." onclick="effacer()" ....> | |
type="button":定义一个按钮控件。还有另外两种类型的按钮,即 submit 和 reset。 value="清除":按钮上显示的文本 onclick="函数()":用于定义用户点击按钮时要执行的函数。该函数属于显示的网页文档中定义的脚本。上述语法是 javascript 语法。如果脚本使用 vbscript 编写,则应写为 onclick="函数",不带括号。 若需向函数传递参数,语法保持不变:onclick="函数(val1, val2,...)" 在本例中,点击按钮 Effacer 将调用以下 JavaScript 函数 effacer: 函数 effacer 显示一条消息: ![]() |
2.8.1.10. 提交按钮
<input type="submit" value="发送" name="cmdRenvoyer"> |
![]()
<input type="submit" value="发送" name="cmdRenvoyer"> | |
type="submit":将该按钮定义为将表单数据发送至Web服务器的提交按钮。当用户点击该按钮时,浏览器将根据<form>标签中method属性定义的方法,将表单数据发送至该标签action属性中定义的URL。 value="提交":按钮上显示的文本 |
2.8.1.11. 重置按钮
<input type="reset" value="重置" name="cmdRétablir"> |
![]()
<input type="reset" value="重置" name="cmdRétablir"> | |
type="reset":将该按钮定义为表单重置按钮。当用户点击此按钮时,浏览器将把表单恢复到初始接收时的状态。 value="重置":按钮上显示的文本 |
2.8.1.12. 隐藏字段
<input type="hidden" name="secret" value="uneValeur"> |
<input type="hidden" name="..." value="..."> | |
type="hidden":指定这是一个隐藏字段。隐藏字段是表单的一部分,但不会显示给用户。不过,如果用户要求浏览器显示源代码,就会看到 <input type="hidden" value="..."> 标签,从而看到隐藏字段的值。 value="uneValeur":隐藏字段的值。 隐藏字段有什么用处?它可以让Web服务器在处理客户端的请求时保存信息。以一个在线购物应用为例。 客户在商品目录的首页购买了商品 art1,数量为 q1,随后跳转到目录的另一页面。 为了记住客户已购买了 q1 和 art1 这两件商品,服务器可以将这两条信息放入新页面网页表单中的隐藏字段中。 在该新页面上,客户购买了 q2 和 art2 商品。当该第二个表单的数据被提交(submit)至服务器时,服务器不仅会收到 (q2,art2),还会接收到 (q1,art1)——后者作为用户无法修改的隐藏字段,同样属于该表单的一部分。 随后,Web服务器将把信息(q1,art1)和(q2,art2)放入一个新的隐藏字段中,并发送新的目录页面。以此类推。 |
2.8.2. Web客户端向Web服务器提交表单值
我们在前一节中提到,Web客户端有两种方法可将显示的表单值发送至Web服务器:方法GET和POST。让我们通过一个示例来看看这两种方法的区别。 我们沿用前面的示例,并按以下方式进行处理:
- 浏览器向Web服务器请求示例中的URL
- 获取表单后,我们填写表单
- 在点击类型为 submit 的 Envoyer 按钮将表单值发送给 Web 服务器之前,我们停止该 Web 服务器,并将其替换为之前已使用的通用服务器 TCP。 需要提醒的是,该服务器会将Web客户端发送的文本行显示在屏幕上。因此,我们将能够看到浏览器确切发送的内容。
表单填写方式如下:

用于本文档的 URL 代码如下:

2.8.2.1. 方法 GET
文档 HTML 已配置为让浏览器使用方法 GET 将表单值发送至 Web 服务器。因此我们编写了:
我们停止 Web 服务器,并在 81 端口上启动我们的通用 TCP 服务器:
E:\data\serge\JAVA\SOCKETS\serveur générique>java serveurTCPgenerique 81
Serveur générique lancé sur le port 81
现在,我们回到浏览器中,通过按钮 Envoyer 将表单数据发送至 Web 服务器:

通用服务器 TCP 接收到的数据如下:
<-- GET /html/balises.htm?R1=Oui&C1=un&C2=deux&txtSaisie=programmation+web&txtMdp=ceciestsecret&area
Saisie=les+bases+de+la%0D%0Aprogrammation+web&cmbValeurs=choix3&lst1=liste3&lst2=liste1&lst2=liste3&
cmdRenvoyer=Envoyer&secret=uneValeur HTTP/1.1
<-- Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/msword, application/vnd
.ms-powerpoint, application/vnd.ms-excel, */*
<-- Referer: http://localhost:81/html/balises.htm
<-- Accept-Language: fr
<-- Accept-Encoding: gzip, deflate
<-- User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET CLR 1.0.3705)
<-- Host: localhost:81
<-- Connection: Keep-Alive
<--
一切都在浏览器发送的第一个标头 HTTP 中:
<-- GET /html/balises.htm?R1=Oui&C1=un&C2=deux&txtSaisie=programmation+web&txtMdp=ceciestsecret&area
Saisie=les+bases+de+la%0D%0Aprogrammation+web&cmbValeurs=choix3&lst1=liste3&lst2=liste1&lst2=liste3&
cmdRenvoyer=Envoyer&secret=uneValeur HTTP/1.1
可以看出,它比迄今为止遇到的要复杂得多。 其中包含 GET URL HTTP/1.1 的语法,但呈现为特殊形式:GET URL?param1=值1¶m2=值2&... HTTP/1.1,其中 parami 代表网页表单控件的名称,而值则是与其关联的数值。让我们进一步分析。下表包含三列:
- 第 1 列:摘自示例中控件 HTML 的定义
- 第 2 列:显示该控件在浏览器中的外观
- 第 3 列:显示浏览器为第 1 列中的控件发送给服务器的值,该值以示例中 GET 请求中的形式呈现
控件 HTML | 可视化 | 返回值 |
<input type="radio" value="是" name="R1">是 <input type="radio" name="R1" value="否" checked>否 | - 用户选中的单选按钮的 value 属性值。 | |
<input type="checkbox" name="C1" value="一">1 <input type="checkbox" name="C2" value="二" checked>2 <input type="checkbox" name="C3" value="三">3 | C1=一 C2=二 - 用户勾选的复选框的 value 属性值 | |
<input type="text" name="txtSaisie" size="20" value="几个词"> | txtSaisie=web+编程 - 用户在输入框中输入的文本。空格已被替换为 + 符号 | |
<input type="password" name="txtMdp" size="20" value="unMotDePasse"> | txtMdp=这是秘密 - 用户在输入框中输入的文本 | |
<textarea rows="2" name="areaSaisie" cols="20"> 第1行 第2行 第3行 </textarea> | areaSaisie=网页编程基础%0D%0A 网页编程 - 用户在输入框中输入的文本。%OD%OA 是换行标记。空格已被 + 符号替换 | |
<select size="1" name="cmbValeurs"> <option>选项1</option> <option selected>选项2</option> <option>选项3</option> </select> | cmbValeurs=选项3 - 用户在单选列表中选择的值 | |
<select size="3" name="lst1"> <option selected>列表1</option> <option>列表2</option> <option>列表3</option> <option>列表4</option> <option>列表5</option> </select> | ![]() | lst1=列表3 - 用户在单选列表中选择的值 |
<select size="3" name="lst2" multiple> <option selected>列表1</option> <option>列表2</option> <option selected>列表3</option> <option>列表4</option> <option>列表5</option> </select> | ![]() | lst2=列表1 lst2=列表3 - 用户在多选列表中选择的值 |
<input type="submit" value="发送" name="cmdRenvoyer"> | cmdRenvoyer=发送 - 用于将表单数据发送至服务器的按钮名称及属性 value | |
<input type="hidden" name="secret" value="uneValeur"> | secret=一个值 - 隐藏字段的属性 value |
我们再做一次同样的操作,但这次保留由 Web 服务器生成响应,看看最终结果如何。Web 服务器返回的页面如下:

这与表单填写前最初收到的内容完全相同。要理解原因,我们需要重新查看用户点击 Envoyer 按钮时,浏览器请求的 URL:
<-- GET /html/balises.htm?R1=Oui&C1=un&C2=deux&txtSaisie=programmation+web&txtMdp=ceciestsecret&area
Saisie=les+bases+de+la%0D%0Aprogrammation+web&cmbValeurs=choix3&lst1=liste3&lst2=liste1&lst2=liste3&
cmdRenvoyer=Envoyer&secret=uneValeur HTTP/1.1
请求的 URL 是 /html/balises.htm。此外,我们将表单中的值传递给该 URL。 目前,静态页面 /html/balises.htm 并未使用这些值。因此,前面的 GET 等同于
,这也是服务器再次返回初始页面的原因。值得注意的是,浏览器确实正确显示了所请求的完整 URL 页面:

2.8.2.2. 方法 POST
文档 HTML 已进行编程,使浏览器现在使用方法 POST 将表单值发送至 Web 服务器:
我们停止 Web 服务器,并在 81 端口上启动通用服务器 TCP(之前已出现过,但此次稍作修改):
E:\data\serge\JAVA\SOCKETS\serveur générique>java serveurTCPgenerique2 81
Serveur générique lancé sur le port 81
现在,我们回到浏览器中,通过“提交”按钮将表单数据发送至Web服务器:

那么,通用服务器收到的数据如下:
<-- POST /html/balises.htm HTTP/1.1
<-- Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/msword, application/vnd
.ms-powerpoint, application/vnd.ms-excel, */*
<-- Referer: http://localhost:81/html/balises.htm
<-- Accept-Language: fr
<-- Content-Type: application/x-www-form-urlencoded
<-- Accept-Encoding: gzip, deflate
<-- User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET CLR 1.0.3705)
<-- Host: localhost:81
<-- Content-Length: 210
<-- Connection: Keep-Alive
<-- Cache-Control: no-cache
<--
<-- R1=Oui&C1=un&C2=deux&txtSaisie=programmation+web&txtMdp=ceciestsecret&areaSaisie=les+bases+de+la%0D%0Aprogrammation+web&cmbValeurs=choix3&lst1=liste3&lst2=liste1&lst2=liste3&cmdRenvoyer=Envoyer&secret=uneValeur
与我们已知的相比,我们在浏览器请求中注意到以下变化:
- 最初的 HTTP 标头已不再是 GET,而是 POST。 语法为 POST URL HTTP/1.1,其中 URL 是浏览器请求的 URL。 同时,POST 表示浏览器有数据需要传输给服务器。
- Content-Type: application/x-www-form-urlencoded 这一行指明了浏览器将发送何种类型的数据。这些是经过 URL 编码的表单数据(x-www-form)。这种编码会将传输数据中的某些字符进行转换,以避免服务器出现解析错误。 因此,空格被替换为 +,换行符被替换为 %OD%OA,... 通常,数据中所有可能被服务器误解的字符(如 &、+、% 等)都会被转换为 %XX,其中 XX 代表其十六进制编码。
- Content-Length: 210 这一行告知服务器,在完成 HTTP 头部后,客户端将发送多少个字符,c.a.d。紧随其后的是一个空行,标志着头部的结束。
- 数据(210个字符):R1=是&C1=一&C2=二&txtSaisie=Web+编程&txtMdp=这是秘密&areaSaisie=基础知识%0D%0A网页编程&cmbValeurs=选择3&lst1=列表3&lst2=列表1&lst2=列表3&cmdRenvoyer=发送&secret=uneValeur
可以发现,POST传输的数据格式与GET传输的数据格式相同。
这两种方法哪一种更好?我们看到,如果表单的值由浏览器通过 GET 方法发送, 浏览器会在其 Adresse 字段中以 URL 的形式显示所请求的 URL?param1=val1¶m2=val2&.... 这既可视为优点,也可视为缺点:
- 若希望允许用户将此带参数的 URL 添加到收藏夹,则视为优点
- 若不希望用户访问表单中的某些信息(例如隐藏字段),则属于缺点
此后,我们在表单中将几乎完全使用 POST 方法。
2.8.2.3. 获取 Web 表单的值
由客户端请求的静态页面,即使通过 POST 或 GET 发送了参数,也无法以任何方式获取这些参数。 只有程序才能做到这一点,而程序将负责向客户端生成响应,该响应是动态的,通常取决于接收到的参数。这是Web编程的领域,我们将在下一章中通过介绍Java Web编程技术——Servlet和JSP页面——对此进行更详细的探讨。









