到 目 前 为 止, 相 信 大 家 对 客 户 端Java 已 经 比 较 熟 悉 了, 甚 至 可 能 已 经
写 过 许 多 出 色 的Applets 小 程 序。 但 是, 大 家 可 能 不 太 熟 悉 服 务 器 端Java,也 没 写 过 服 务 器 端Applet。 本 文 将 带 领 读 者 初 窥 服 务 器 端Java 的 门 径。
服 务 器 端Java(SSJ,Server Side Java) 是 公 共 网 关 接 口(CGI) 和 低 级 服 务 器API
程 序 设 计( 例 如Netscape 的NSAPI 与Microsoft 的ISAPI) 的 混 合 体。 有 时 我 们 称SSJ
为Servlets 或 服 务 器 端Applet。
本 文 向 大 家 介 绍 服 务 器 端Java 及 引 导 大 家 一 步 步 实 现 服 务 器 端Java 的
执 行 程 序,Netscape 称 之 为 服 务 器 端Applets(SSA,Server Side Applet)。
SSA 像CGI 的 脚 本 程 序 一 样, 接 收“get” 和“post” 请 求, 并 返 回 一个Web 页 面( 通 常 以Html 形 式)。 但 是SSJ 如 同NSAPI/ISAPI 一 样 是 动 态 地 装 入服 务 器 的, 这 就 消 除 了CGI 启 动 时 的 延 迟, 也 使SSJ 能 在 两 次 执 行 中 维 持它 自 身 的 状 态, 例 如 保 持 与 数 据 库 开 放 的 连 接。
SSA 在 他 们 自 己 的“ 沙 箱”(“sandbox”) 中 执 行, 这 样 可 以 保 证 安 全。
例 如, 一 个 毁 灭 性 的Applet 不 会 像NSAPI/ISAPI 程 序 设 计 那 样 摧 毁 整 个 服 务器, 这 种 安 全 性 允 许 上 载 执 行Applet, 就 像 客 户 机 端Java 的 下 载 执 行Applet一 样。SSA 最 重 要 的 优 点 是Java 的 平 台 无 关 性 和 面 向 对 象 的 特 性。
本 文 以Netscape 服 务 器Enterprise 2.0 和Fast Track 2.0 为 例 来 介 绍 服 务 器
端Java 应 用 的 开 发。 准 备 服 务 器 在 编 写 服 务 器 端Applet 前, 需 要 准 备 相 应
的 服 务 器。 首 先 打 开 服 务 器 的Java 解 释 器。 在 服 务 器 管 理 器 的“Programs
-Java” 栏 下 点 击“yes” 按 钮 启 动Java 解 释 器, 服 务 器 管 理 器 就 会 提 示 输
入“Java applet Directory”, 以 存 放SSA 类 文 件。 服 务 器 提 供 有 缺 省 地 址。
在Unix 机 上, 缺 省 值 是/usr/ns home/plugins/java/applets。 在Windows 机 上, 缺 省
值 为c:\Program Files\Netscape\Server\plugins\Java\applets( 注: 在 这 个 输 入栏,Netscape 常 常 对 其\ 和 / 不 加 区 别。 不 必 担 心,Netscape 对 这 两 种 类 型 斜 杠的 处 理 方 式 是 相 同 的。) 可 能 的 话 最 好 使 用 缺 省 目 录。 如 果 习 惯 于 自 己 的目 录, 只 要 该 目 录 是 在 服 务 器 的 根 目 录 下 面, 就 可 把 缺 省 目 录 下 的 所 有文 件 拷 贝 到 自 己 的 工 作 目 录 下, 保 存 这 些 改 动, 关 闭 服 务 器, 再 重 新 启动 该 服 务 器。
实 验
下 面, 我 们 利 用Netscape 提 供 的Applet 进 行 实 验。 把 浏 览 器 指
向http://servername/server java/Connect, 它 加 载 并 显
示http://www.meer.net/barn/index.html。Connect applet 为 取 出 此 页 面 建 立 一 个socket
通 讯, 如 果 服 务 器 在 一 个 防 火 墙 后 面, 这 可 能 会 导 致 服 务 器 错 误。 下
面, 让 我 们 假 定 防 火 墙 阻 塞 了socket, 我 们 将 编 辑Connect applet 代 码, 使 它
能 在 不 同 的Web 服 务 器 中 访 问 不 同 页 面。
在“Java applet 目 录” 中 可 得 到Connect.java 文 件。 它 包 含 开 发 服 务 器
端Applet 的 基 本 类, 其 中 最 重 要 的 类 是HttpApplet, 这 是 所 有 服 务 器 端Applet
的 超 级 类。 实 现 服 务 器 端Applet 所 需 的 唯 一 方 法 是run 方 法,Applet 每 接 收 一次“hit” 就 要 执 行 一 次 该 方 法。 在 使 输 出 转 到 客 户 机 之 前,Connect 的run 方法 向“host” 打 开 一 个socket 并 取 出“request”。 通 过 设 置 变 量“host”, 可 以访 问 任 一 台 机 器; 同 样 设 置 变 量“request”, 可 以 访 问 某“host” 上 的 任 一个 页 面。
import netscape.server.applet. *;
import java.io.PrintStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.DataInputStream;
import java.net.Socket;
class Connect extends HttpApplet {
public void run() throws Exception {
String host = “www.meer.net";
//读者可以根据自己的环境修改此主机名
int port = 80;
String request = “GET /barn/index.html HTTP/1.0\n";
//同样可以修改
Socket s = new Socket(host, port);
OutputStream os = s.getOutputStream();
PrintStream op = new PrintStream(os);
op.println(request);
InputStream is = sam();
DataInputStream di = new DataInputStream(is);
String line;
if (returnNormalResponse(“text/html")) {
PrintStream out = getOutputStream();
out.println(“&lth1&gtData on "+host+“port "+port +“</h1>");
out.println(“request: "+request+“<hr>");
while ((line = di.readLine()) != null)
out.println(line);
}
}
}
设 置 变 量“host” 或“request” 后, 下 一 步 就 是 重 新 编 译Connect。
在Windows 系 统 下, 把 标 准javac 编 译 器 的classpath 设 置 成 包 括serv2
0.zip.javac classpath \classes\serv2 0.zip Connect.java。
在Unix 系 统 下,Netscape 在Java applet 目 录 的 上 级 目 录 中 提 供 一 个Java 编
译 器(javac)。 这 个javac 用 来 编 译 的 实 际 上 是 一 个 叫 做java sun.tools.javac.Main 的
脚 本 程 序。 在 某 些 系 统 中,sun.tools.javac.Main 编 译 器 使 用 新 版JDK11 中 的 方法, 例 如java.lang.Character.isJavaLetterOrDigit(), 如 果 没 有JDK11, 可 能 会 遇 到 很
多 麻 烦。 最 好 选 择 常 用 的 标 准javac 编 译 器, 如javac classpath. /classes/serv2
0.zip Connect.java。 如 果 要 使 用 服 务 器 提 供 的javac 脚 本 程 序, 只 需
用“../javac” 来 替 换“javac”。
在 这 个 编 译 过 程 中, 可 能 会 出 现 这 样 一 个 错 误:
Connect.java:1:Package netscape.server.applet not found in import.
import netscape.server.applet. *;
^
1 error
在import 语 句 中 删 掉wildcards, 就 可 避 免 这 个 错 误。
在Unix 系 统 下,Netscape 在Java applet 目 录 中 提 供 一 个makefile 文 件, 以 控
制Applet 的 编 译。 可 惜, 这 个makefile 文 件 使 用‘ %’wildcard, 它 是mk/nmake 的扩 展, 有 时 不 可 获 取 其 代 码。 问 题 代 码 如 下:
%.class; %.java
../javac classpath ../classes/serv2 0.zip $ *.java
解 决 办 法 是 使 用suffixes 规 则, 编 辑makefile 文 件 的 第 一 行, 如:SUFFIXES
:java.class 然 后 执 行:
javac classpath ../classes/serv2 0 zip $
即 删 去 了../, 这 样makefile 文 件 就 可 调 用 标 准 的javac 编 译 器。 测 试 这 个新 的makefile 文 件, 重 新 保 存Connect.java 文 件, 然 后 执 行“make”。
Methods
下 面 介 绍 几 个 有 用 的 方 法(Methods)。
1.PrintStream getOutputStream() throws IOException;
返 回 一 个PrintStream, 把 响 应 输 出 到 客 户 机 上, 此 方 法 代 替了System.out。
2.Hashtable getFormData() throws IOException;
返 回 一 个Hashtable( 哈 希 表), 其 中 存 储 有HTPP 请 求 的name value
对,value 串 是 从 加 密 的URL 形 式 解 密 后 得 到 的。 若 没 有form data, 则 显 示I/O
异 常。
3.String getFormField(String fieldName)throws IOException;
利 用getFormField 方 法 能 找 到 一 个field, 且 仅 仅 一 个。 若 没 有form data, 也
显 示I/O 异 常。
4.boolean retrunNormalResponse(String contentType)throws IOException;
根 据 在 参 数 中 指 定 的 内 容 类 型 启 动HTTP 响 应。 若 是“get” 或“post” 请
求 则 返 回 值 为true, 若 是“head” 请 求 则 返 回false。
5.public boolean returnErrorResponse(String contentType,int status,String reason) throws
IOException;
启 动 响 应 报 错 。 它 带 有 三 个 参 数: 内 容 类 型、 状 态( 例 如HttpApplet
BAD REQUEST, 其 标 准 错 误 代 码 号 为400), 以 及 可 缺 省 的 用 来 说 明 错 误
原 因 的 字 符 串。
Netscape 在 它 的 服 务 器 中 安 装 了 一 个API 入 门 手 册, 其 中 有 许 多 开 发 服
务 器 端Java 经 常 调 用 的 方 法。 在Unix 系 统 下, 通 过/usr/ns
home/bin/httpd/admin/html/manual/pg/javapi.htm, 可