荔园在线

荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀

[回到开始] [上一篇][下一篇]


发信人: zzt (少年仲永), 信区: Java
标  题: 用servlets建立联网的“白板”
发信站: BBS 荔园晨风站 (Fri Apr  9 15:51:36 1999), 转信

用servlets建立联网的“白板”
---- Servlets 是Java 里 自 带的 扩 展 服 务 器, 一 般 用 于 网 络 服 务
。 包括Apache, Java Web Server (JWS), O'Reilly's WebSite, 以 及Netscape 在
内 的 很 多 网 络 服 务 器 支 持 它。

---- Servlets 的 特 点 是 不为 每 个 请 求 启 动 一 个 新 进 程, 这 使
它的 速 度 高 于 一 些CGI. 下 的 网 络 服 务 器,如JWS。 因 为servlets 没
有 进 程 任 务 的 切 换,它 甚 至 比Fast-CGI 还 快;servlets 本 身 象 一 个
服 务 进 程 一 样。

---- 由 于servlets 用HTTP 方式( 如GET 和 POST) 应 答, 基 于servlets 的
 通 讯可 以 绕 过 防 火 墙( 防 火 墙 把sockets 和 RMI 视为 多 余 物)。
实 现 在 支 持SSL 的 客 户 端(如Netscape) 和 运 行 在 支 持SSL 的 网 络
服 务 器上 的servlets 之 间 的 安
全 通 信 是 很 简 单的。SSL(Secure Sockets Layer ) 是 一 个 保 护 互 联
网 上 的 数 据 不 被 偷 听 的 协 议。

Servlets 的 结 构 和 生 命 周期
---- 从 更 高 层 上 看来,servlets 只 是 一 个Applet, 所 不 同 的 是 它
运 行 在 服 务 器 环 境 下 而 不 是 浏 览 器下。象 Applet 一 样, 它 有 一
个 受 环 境 控 制的 生 命 周 期。 不 同 的 是, 对 每 个 网 络 服务 器 它
只 创 建 一 个servlets 的 实
例。 每 个对servlets 的URL 的 请 求 被 翻 译 为 相 同 的 servlets 实 例。


---- 从 客 户 端 获 得 的Servlets 象 一 个CGI 的 脚 本。 例 如: 一 个HTT
P GET 向URL 发 出 类 似 于http://www.mycom.com/servlet/CurrentTime 的请 求
, 会 返 回HTML 格 式 的 文 件 包 含 服 务器 的 当 前 时 间。

---- 一 旦init () 方 法 完成,servlet 就 可 以 通 过 service () 方 法 处
理 请求。 环 境 把 每 个 请 求 归 入 它 自 己 的 线程, 每 个 线 程 调 用s
ervlet 的service () 方 法。

---- servlet 持 续 处 理 请求 直 到 卸 载。 卸 载( 如 关 机 时) 时 将
调用destroy () 方 法。

servlet API 的 简 要 回 顾
---- javax.servlet 和 javax.servlet.http 包 中 都 提 供 了servlet API。ja
vax.servlet 是 基 本 的 servlet API, 它 包 括Servlet 界 面 及 其从 属 的Ge
nericServlet。 这 个 包 中 还 包 含 了ServletRequest 和 ServletResponse 界
 面。

---- GenericServlet 中 最 重要 的 方 法 是service (), 他 是 请 求 线 程
的 入口。 个 别 的GenericServlet 象HttpServlet 处 理 特 定的 类 协 议 请
求 是 则 不 用 这 个 方 法。

---- javax.servlet.http 列 举这 些 类 和 应 用 界 面 来 处 理HTTP 请 求。
 在下 面 的 应 用 中 我 们 将 进 一 步 看 到HTTP servlet 类 的 请 求。

白 板 的servlet
---- 这 个 白 板 使 用 了一 个 叫 做ObservableList 的 数 据 类 型 来 保
存 显示 的 元 素。 分 布 式 的 应 用 使 用sockets 和 RMI 的 类subclassed Ob
servableList 并 在 此 基 础 上 联网。 这 时, 服 务 器 提 供 了 中 央IDLis
t 来 保存 共 享 元 素。

---- 现 在 我 们 要 做 同样 的 事 情, 不 同 的 是 要 扩 展ObservableList
 使它 提 供 基 于servlet 的 应 用, 起 名 为ServletList 。 基 于servlet 的
 应 用 有 两 个 功 能: 一 是 我们 上 次 提 到 的RMI 方 法 的 联 网 模 式
, 即 客户 端 定 期 轮 询 服 务
器 以 便 知 道 它 的 变化; 另 一 个 是 向 服 务 器 的 应 用 对 象 流传
递“ 消 息 对 象”, 即sockets 方 法。

---- 客 户 端会 使 用URL 和URLConnection 类 来 传 送 消 息 对 象用HTTP PO
ST 协 议, 这 个 协 议 允 许 网 络 服 务器 传 送 有POST 申 请 书 的 二 进
制 代 码。 应答 信 息 也 必 须 是 二 进 制 代 码, 所 以 对象 流 要 被 编
码 成 应 答 对 象。

ServletList 类
---- ServletList 扩 展 了ObservableList 并 使 用 了 基 于socket 的 应 用
中 的 消 息 包 来与 服 务 器 通 讯。 一 个 新 的 消 息 类 型UpdateMsg 使
客 户 端 可以 通 知 服 务 器 它 需 要 做 修 改 了。 服 务器 回 答 一 个Init
Msg , 其 中 包 含 了 改 动。
让我 们 看 一 下:

package shoffner.step.jan;

import java.io.*;

import java.util.*;

import java.net.*;

import org.merlin.step.dec.*;

import org.merlin.step.dec.socket.*;

public class ServletList extends ObservableList implements Runnable {

public static final int UPDATE_DELAY = 10 * 1000;

URL server;

IDList list;

Thread processor;

public ServletList (String host, String loc) {

try {

server = new URL ("http://" + host + "/" + loc);

} catch (MalformedURLException ex) {

ex.printStackTrace ();

}

list = new IDList ();

update ();

processor = new Thread (this);

processor.start ();

}

---- 因 为 我 们 要 用 到上 次 的 一 些 数 据 结 构 和 插 口 的 消 息类,
 我 们 从dec 和 dec.socket 中 引 用 了 一 些类。 开 始 时 在 宿 主 上 安
装 一 个URL , 并 用这 个 类 把ServletList 实 例 化。

public Enumeration elements () {

return list.elements ();

}

public void addElement (Object element) {

Object id = queryServer (new AddElementMsg (element));

list.addElementWithID (id, element);

}

public void replaceElementAtEnd (Object oldE, Object newE) {

Object oldID = list.getIDOfElement (oldE);

Object id = queryServer (new ReplaceElementMsg (oldID, newE));

if (id != null)

list.replaceElementWithID (oldID, id, newE);

else

System.out.println ("Unknown id:" + oldID);

}

---- 这 三 个 方 法(elements (), addElement (), and replaceElementAtEnd
()) 是ServletList 的 公 共 接 口。 他 们 的 核 心 是 调 用queryServer ()
, 他 从 服 务 器 向 调 用 他 的 方 法 返 回 一个 对 象。 注 意 我 们 把 请
 求 打 包 成 对 象是 为 了 向
服 务 器 表 明 他 们 的 类 型。

public void run () {

while (true) {

try {

Thread.sleep (UPDATE_DELAY);

update ();

}

catch (Exception ignored) {

ignored.printStackTrace ();

}

}

}

public void stop () {

if (processor != null) {

processor.stop ();

processor = null;

}

}

void update () {

InitMsg i = (InitMsg) queryServer (new UpdateMsg (list.getUpdateCount ())
);

IDList initList = i.getList ();

if (initList != null) {

list = initList;

fireUpdate ();

}

}

---- 这 里 我 们 看 到 了修 改 线 程 定 期 轮 询 服 务 器。 如 果 服 务器
 的IDList 与 列 表 保 存 的 不 同, 他 将 返 回新 的IDList 。 我 们 用 新
的IDList 代 替 本 地 的IDList 并 调 用fireUpdate ()。 显 然, 我 们 可 以
用 一个 更 复 杂 的 修 改 机
制, 但 这 个 简 单 的方 法 已 经 够 用 了。

---- 当 且 仅 当 客 户 端退 出 或 停 止 修 改 的 轮 询 时 才 可 以 退 出r
un ()。 这 里run () 是 一 个 循 环, 如 果 客 户 端轮 询 时 服 务 器 关 机
 将 发 生 异 常。 下 面是 客 户 端 应 用 中 重 要 的 部 分:

Object queryServer (Object arg) {

URLConnection con;

ObjectOutputStream req = null;

ObjectInputStream res = null;

Object result = null;

try {

con = server.openConnection ();

con.setDoOutput (true);

req = new ObjectOutputStream (new BufferedOutputStream

(con.getOutputStream ()));

req.writeObject (arg);

req.flush ();

req.close ();

res = new ObjectInputStream (new BufferedInputStream

(con.getInputStream ()));

result = res.readObject ();

} catch (IOException ignored) {

ignored.printStackTrace ();

} catch (ClassNotFoundException ex) {

ex.printStackTrace ();

} finally {

try {

res.close ();

} catch (IOException ex) {}

}

return result;

}

}

---- queryServer () 方 法 把一 个 叫 做arg 的 对 象 送 到servlet, 使 用H
TTP POST 方 法 得 到 servlet 的 URL。 在 这 个 对 象 被 送出 之 前 必 须
连 续 使 用ObjectOutputStream. 服 务器 的 应 答 也 将 从 一 个ObjectInputSt
ream 读 进 来并 用 这 个 方 法
返 回。

---- 有 趣 的 是, 在 另一 端queryServer () 方 法 并 不 需 要servlet 。
任 何可 以 明 白POST 并 且 可 以 连 续 地 接 受 和 返回Java 对 象 的 网 络
服 务 器 终 端 或CGI 都 可以 在 没 有Servlet 的 情 况 下 正 常 工 作。 所
以, 我 们 可 以 修
改queryServer () 方 法, 使 它除 了 可 以 被 那 些 处 理 连 续 的Java 对
象 服务 器 接 受 外, 还 可 以 被 更 多 中 间 消 息生 成 服 务 器 接 受。

---- ServletListServlet 类

import javax.servlet.*;

import javax.servlet.http.*;

import java.io.*;

import org.merlin.step.dec.IDList;

import org.merlin.step.dec.socket.*;

import shoffner.step.jan.*;

public class ServletListServlet extends HttpServlet {

IDList list;

public void init (ServletConfig c) throws ServletException {

super.init (c);

list = new IDList ();

log ("ServletListServlet initialized.");

System.out.println ("ServletListServlet initialized.");

}

---- 我 们 首 先 引 用 必要 的 类 并 声 明 我 们 的servlet 扩 充 了HttpSe
rvlet , 以 便 它 可 以 使 用doPost () 方 法。 下 一 步是 修 改 缺 省 的in
it () 方 法。 指 令init () 把 启动 消 息 传 给 网 络 服 务 器 日 志 文 件
,println () 方 法 把 它 在 网
络 服 务 器 启 动 的 终 端上 显 示 出 来( 假 设 终 端 可 用)。

---- 我 们 还 可 以 修 改一 般 的destroy () 方 法 以 确 定 在servlet 被
卸 载之 前 结 束 了 所 有 的 连 接。 这 个 方 法 对那 些‘ 连 接 死 机’ 敏
 感 的 应 用 来 说 是 有益 的。 我 们 将 改 成 让 客 户 端 处 理 这 种情
况( 指servlet 被 卸 载)。( 网
络 服 务 器也 可 能 意 外 死 机, 但 这 是 很 罕 见 的)。

public void doPost (HttpServletRequest httpReq, HttpServletResponse httpR
es)

throws ServletException, IOException {

// handle request

ServletInputStream sis = httpReq.getInputStream ();

BufferedInputStream bis = new BufferedInputStream (sis);

ObjectInputStream ois = new ObjectInputStream (bis);

DListMsg msg = null;

Object response = null;

try {

msg = (DListMsg) ois.readObject ();

} catch (ClassNotFoundException ex) {

ex.printStackTrace ();

}

if (msg instanceof AddElementMsg) {

Object element = ((AddElementMsg) msg).getElement ();

list.addElement (element);

response = list.getIDOfElement (element);

} else if (msg instanceof ReplaceElementMsg) {

Object oldID = ((ReplaceElementMsg) msg).getID ();

Object element = ((ReplaceElementMsg) msg).getElement ();

response = list.replaceElement (oldID, element);

} else if (msg instanceof UpdateMsg) {

if (list.getUpdateCount () == ((UpdateMsg) msg).getUpdateCount ())

response = new InitMsg (null);

else

response = new InitMsg ((IDList) list.clone ());

} else {

System.out.println ("Unknown message:" + msg);

}

System.out.println (msg);

---- Servlet 中 真 正 起 作用 的 是doPost ()。 其 作 用 是 接 受 进 来 的
POST 并 生 成 应 答。 请 求 和 响 应 通 过HttpServletRequest 和 HttpServle
tResponse 里 的 环 境 调 用doPost () 方 法时 分 别 传 给 它。

---- 当msg 对 象 从 输 入流 被 取 出 后,logic 消 息 接 管 它。

---- 由 于servlet 的 一 个服 务 方 法 里 要 执 行 许 多 请 求 线 程, 并
发 性 显 得 非 常 重 要。IDList 方 法 已 经 是 并发 的 了, 所 以 我 们 不
必 担 心 这 一 点。

// send response

httpRes.setStatus (HttpServletResponse.SC_OK);

ServletOutputStream sos = httpRes.getOutputStream ();

BufferedOutputStream bos = new BufferedOutputStream (sos);

ObjectOutputStream oos = new ObjectOutputStream (bos);

oos.writeObject (response);

oos.flush ();

}

}

---- 一 旦logic 消 息 通过, 就 要 回 送 一 个 响 应。 我 们 要 做 的 第
一 件 事 是 把HttpServletResponse 的 状 态 置 为SC_OK。下 一 步, 创 建 一
个ObjectOutputStream , 根 据 服务 器 的 响 应 写 一 个 给POST 的 响 应,
然 后取 消ObjectOutputStream 。
注 意 我 们 并 没 有 关 闭这 个 流, 因 为 环 境 自 动 做 了。

启 动servlets
---- 使 用servlets 的 第 一步 是 从JavaSoft 中 得 到servlet 开 发 工 具
箱(JSDK) 并 安 装 它( 工 具 箱 中 指 明 了 安 装 步骤)。

---- 为 了 使servlets 的Apache 能 运 行, 请 使 用 最 新 版 本 并 安 装se
rvlet 模组。JavaSoft 在JSDK 中 提 供mod_servlet.c , 但 为 了使 用Apache 1
.2.x , 你 需 要 贴 上 它。 每 周 出版 的Apache 用 户 指 南Apache Week 里
提 供“ 贴” 上 它 的 工
具。Apache 组 里 还 有 一 个servlet 模组。

---- 安 装 了servlet 的 模组 后, 你 需 要 配 置 它。 关 于 配 置 的 说
明在 安 装 指 南 中。 这 将 包 括servlet 指 南 的 别名 和 存 储 的 物 理 位
 置。 这 很 象CGI 的 安装。

代 码 的 使 用
---- 本 文 涉 及 到 的 源代 码 都 在 来 源 部 分 列 出。 编 译 代 码 后,
你 必 须 把ServletListServlet 类 放 在servlet 的"bin" 目 录 下( 安 装 时
已 经 建 立 了) 你 可 以 把客 户HTML 放 在 任 何 地 方。

---- 在 编 译 之 前, 你一 定 要 把ServletWB.java 的"content = ..." 行的
第 二 个 参 数 改 为 把ServletList 配 置 到 你的servlet 别 名 下。 按 以
下 步 骤 编 译:

下 载 全 部 代 码, 解 压 缩。
改 变src 路 径
用 以 下 命 令 编 译:
---- javac ServletListServlet.java org\merlin\step\dec\*.java org\merlin\
step\dec\socket\*.java shoffner\step\jan\*.java

---- ( 如 果 你 使 用Unix , 用/ 代 替\)

---- 因 为servlet 和 客 户端 在 不 同 的 目 录 下, 就 有 必 要 两 端 都
有org 和shoffner 层 的 备 份。 对 客 户 端 来 说,这 一 层 与 含 有ServletW
BLaunche 的 应 用 标 签 的HTML 文 件 在 同 一 路 径 下。对servlet 来 说,
这 一 层 在 你 安 装 时 定 义
的servlet 的"bin" 路 径 下。

---- 另 外 客 户 端 的 网络 服 务 器 必 须 与servlet 端 的 一 样, 否 则
会遇 到 排 斥。

小 结
---- Servlets 允 许 程 序 员使 用Java 来 扩 展 网 络 服 务。 这 使Java 开
 发者 的 工 具 箱 变 大 了, 他 们 可 以 利 用HTTP 来 实 现 客 户 端 和 服
 务 器 的 消 息 传 递。客 户 端 不 必 成 为 一 个Java 应 用; 实 际 上,se
rvlets 做 到 了 所 有CGI 程 序
做 到 的, 包 括 向 网 络浏 览 器 返 回HTML 文 件。

---- 从 白 板 的 意 义 上讲,servlets 是 代 替RMI 和sockets 的 有 益 的
工具, 特 别 是 当 客 户 端 是 一 个applet 时。 因为applet 的 安 全 政 策
要 求applet 与 网 络 服 务器 联 结,servlet 可 以 代 替 单 独 的、 需 要 特
殊 启 动 的 服 务 进 程。


 --
日出东方,唯我不败;     天上地下,唯我独尊。

※ 来源:.BBS 荔园晨风站 bbs.szu.edu.cn.[FROM: 192.168.0.101]


[回到开始] [上一篇][下一篇]

荔园在线首页 友情链接:深圳大学 深大招生 荔园晨风BBS S-Term软件 网络书店