客户/服务器远程数据传输处理技巧

---- 在 实 际 的MIS 系 统 中, 远 程 数 据 库 访 问 大 多 通 过Modem 连 接, 出 于 通 信 费 用 及 速 度 方 面 的 考 虑, 往 往 采 用 先 将 数 据 保 存 在 本 地, 然 后 集 中 传 送 到 远 端 的 办 法。 远 程 数 据 传 送 可 以 有 多 种 方 案, 最 常 见 的 是 先 将 要 传 送 的 数 据 打 包 成 文 件, 在 利 用 文 件 传 输 形 式 传 送 到 目 的 地, 在 目 的 地 对 数 据 恢 复 后 添 加 到 本 地 数 据 库 中。 这 种 方 法 普 遍 地 应 用 于 证 券 交 易 系 统, 其 优 点 是 速 度 快, 并 且 可 事 先 对 数 据 压 缩, 更 大 限 度 地 节 约 传 送 时 间 及 费 用。 但 这 种 方 案 也 有 其 不 足 之 处: 由 于 利 用 文 件 传 输 机 制, 无 法 利 用 数 据 库 本 身 的 特 性 如 完 整 性 约 束、 数 据 一 致 性、 回 滚 机 制 等, 因 此 在 比 较 复 杂 的 数 据 库 系 统 中 较 少 采 用。 另 一 种 方 法 是 直 接 将 两 端 处 理 成" 客 户/ 服 务 器" 模 式, 将 数 据 传 送 看 成 是 向Server 提 交 数 据。 由 于 这 种 方 案 充 分 利 用 了 数 据 库 服 务 器 的 特 性, 并 且 实 际 操 作 基 本 与 局 域 网 方 式 一 致, 因 此 本 文 将 详 细 介 绍 这 种 方 案。 另 外 本 文 的 部 分 内 容 是 基 于Delphi/CBuilder 的。

---- 由 于 传 输 速 度 的 原 因, 当 传 送 大 量 数 据 时 绝 对 不 赞 成 逐 条 记 录 地 向 服 务 器 提 交 数 据, 而 应 批 量 地 向Server 提 交,Delphi/CBuilder 中 提 供 了 一 个TBatchMove 控 件 专 门 用 于 批 量 传 送 数 据, 利 用 它 可 极 大 减 少 网 络 负 担, 提 高 传 送 速 度。 遗 憾 的 是,TBatchMove 控 件 只 提 供 了 简 单 的 错 误 控 制 功 能, 没 有 提 供 显 示 传 送 进 度、 用 户 终 止 传 送 等 重 要 功 能。 然 而TBatchMove 所 依 赖 的BDE 却 提 供 了 一 种" 回 调 机 制" 可 以 完 成 上 述 两 个 功 能。 所 谓" 回 调" 过 程 是 这 样 的: 当BDE 执 行 某 种 操 作 时, 比 如 从 一 张 表 向 另 一 张 表 拷 贝 大 量 数 据 的 过 程 中, 每 过 一 段 时 间( 如 需 要 显 示 拷 贝 进 度 时),BDE 会 调 用 一 段 你 自 己 写 的 函 数( 回 调 函 数), 以 帮 助 你 更 完 全 地 控 制 程 序。 这 种 做 法 有 点 想DLPHI 中 的Event( 事 件) 及 事 件 处 理 函 数-- 某 个 具 体 的 操 作 动 作 会 让VCL 触 发 某 个 事 件, 从 而 调 用 一 段 你 写 好 的 事 件 处 理 函 数, 不 同 的 事 件 会 触 发 不 同 的 处 理 函 数。

---- 为 了 让BDE 能 正 确 地 与 你 的 函 数 协 同 工 作, 你 必 须 事 先" 注 册" 你 的 函 数, 让BDE 知 道 某 个 事 件 发 生 时 应 调 用( 回 调) 你 的 某 段 代 码。BDE 提 供 了 一 个DbiRegisterCallBack 注 册 函 数, 不 幸 的 是,BDE 的 联 机 帮 助 中 的 说 明 不 能 适 合 于Delphi/CBuilder, 按 照 该 说 明 编 写 的 程 序 根 本 不 能 通 过 编 译 ! 笔 者 通 过 实 践 找 到 了 正 确 使 用BDE 回 调 函 数 的 方 法, 下 面 将 详 细 介 绍 该 机 制 的 使 用。 BDE 回 调 机 制 包 含 以 下 几 个 步 骤:

---- 1) 按BDE 的 预 定 格 式 编 写 你 的 回 调 函 数

---- 2) 调 用DbiRegisterCallBack 函 数 注 册 你 的 回 调 函 数, 这 样 当 你 执 行 相 关 数 据 库 操 作 时 就 自 然 地 触 发 你 的 回 调 函 数。

---- 3) 执 行 相 关 数 据 库 操 作, 比 如BatchMove1- >Exectue();

---- 4) 注 销 该 回 调 函 数

---- 其 中 最 关 键 的 是 正 确 注 册 你 的 回 调 函 数, 因 此 先 介 绍 第 二 步。( 注 册 与 注 销 都 调 用 同 一 函 数, 只 是 最 后 一 个 参 数 略 有 不 同)

---- 首 先 你 应 知 道 在 哪 类" 事 件" 发 生 时 调 用 你 的 回 调 函 数, 其 次 你 应 明 白 与 该 事 件 相 关 的 参 数 及 数 据 结 构-- 这 一 切 都 发 生 在 调 用DbiRegisterCallBack 函 数 注 册 时, 所 以 下 面 先 介 绍DbiRegisterCallBack 的 正 确 用 法 及 说 明:

---- 在 原BDE 帮 助 中 该 函 数 的 原 形(C) 是 这 样 的
DBIResult DBIFN DbiRegisterCallBack (hCursor,
ecbType, iClientData, iCbBufLen, pCbBuf, pfCb);

---- 要 使 用 该 函 数 必 须include 头 文 件, 问 题 是Delphi/CBuilder 中 根 本 没 有 提 供 该 文 件, 取 而 代 之 的 是"BDE.HPP", 但 是 在 包 含 进 该 文 件 后 程 序 仍 然 不 能 编 译 通 过, 因 为 该 文 件 中 没 有DBIFN 等 的 说 明。 一 个 简 单 的 方 法 是 在 代 码 中 去 掉DBIFN。 函 数 中 各 参 数 解 释 如 下:hCursor 是 一 个BDE 中 对 象 的 句 柄, 如 果 这 个 参 数 为NULL, 则 表 示 注 册 的 回 调 函 数 适 合 于 所 有BDE 任 务; 第 二 个 参 数ecbType 是 指 回 调 函 数 的 触 发 条 件 的 类 别, 有 很 多 种 类 型 可 以 选 择, 其 中cbGENPROGRESS 表 示 当 需 要 显 示 一 个 长 操 作 的 进 度 时 触 发 这 个 回 调 函 数; 第 三 个 参 数iClientData 是 传 递 给 回 调 函 数 的 某 个 数 据 结 构 的 指 针, 在 我 们 的 例 子 中 为NULL; 第 四 个 参 数iCbBufLen 是 指 回 调Buffer 的 大 小, 该 大 小 随 第 二 个 参 数 的 不 同 而 不 同, 比 如sizeof(CBPROGRESSDesc); 第 五 个 参 数pCbBuf 是 回 调Buffer 的 指 针, 该 指 针 类 型 随 第 二 个 参 数 变 化, 比 如cbGENPROGRESS 的 数 据 结 构 是CBPROGRESSDesc; 最 后 一 个 参 数 是 回 调 函 数 的 地 址 指 针, 当 该 参 数 为NULL 时 表 示 注 销 该 类 型 的 回 调 函 数。 关 于 回 调 函 数 将 在 稍 后 详 细 介 绍。 下 面 是 注 册 执 行 长 操 作 时 显 示 进 度 的 回 调 函 数 的 格 式:

int rst= DbiRegisterCallBack (NULL,
//适合于任何进程
cbGENPROGRESS, //回调类型:显示长操作的进度
NULL, //没有数据
sizeof(CBPROGRESSDesc), //数据结构的大小
&aCBBuf, //数据的内存地址
ApiCallBackFun //回调函数的地址
);

---- 接 下 来 就 应 该 完 成 第 一 步: 编 写 回 调 函 数

---- 在C 中, 回 调 函 数 应 如 下 声 明:

CBRType __stdcall ApiCallBackFun(
CBType ecbType, //回调类型
int iClientData, //回调数据(指针)
void * pCbInfo //回调数据结构指针
)

---- 第 一 个 参 数 是 回 调 类 型; 第 二 个 参 数 是 回 调 数 据, 其 解 释 同DbiRegisterCallBack 的 第 三 个 参 数; 第 三 个 是 回 调 数 据 的 指 针, 该 数 据 的 结 构 随 回 调 类 型 的 不 同 而 不 同。 比 如 进 度 指 示cbGENPROGRESS 的 数 据 结 构 是CBPROGRESSDesc, 其 定 义 如 下:

struct CBPROGRESSDesc {
short iPercentDone; //进度的百分比
char szMsg[128]; //进度的文本信息
};

---- 该 结 构 的 两 个 域 同 时 只 有 一 个 起 作 用, 第 一 个 表 示 操 作 的 进 度 百 分 比, 当 其 为-1 时 表 示 第 二 个 域 起 作 用。 第 二 个 域 用 字 符 串 表 示 进 度 信 息, 其 格 式 为< String >< : >< Value >, 比 如:Records Copied : 125

---- 本 文 主 要 在 回 调 函 数 中 完 成 两 个 工 作:
---- 1) 显 示 数 据 拷 贝(BatchMove) 进 度
---- 2) 提 供 让 用 户 终 止 长 时 间 拷 贝 的 机 制

---- 显 示 拷 贝 进 度 的 代 码 如 下:

CBRType __stdcall ApiCallBackFun(
CBType ecbType, // Callback type
int iClientData, // Client callback data
void * pCbInfo // Call back info/Client)
{ AnsiString str;
if(ecbType==cbGENPROGRESS)
{
int j= StrToInt( ((CBPROGRESSDesc*)
pCbInfo)- >iPercentDone);
if(j< 0)
//如果iPercentDone为-1,则分析szMsg的信息
{
str=((CBPROGRESSDesc*)pCbInfo)- >szMsg;
int pos=str.AnsiPos(":")+1;
//提取出拷贝的记录数
//下面的代码用来在一个Form中显示拷贝进度及拷贝数量
Form1- >Label2- >Caption= str.SubString(pos,100);
Form1- >Label2- >Update();
Form1- >ProgressBar1- >Position=
int((str.SubString(pos,100).
ToDouble()/Form1- >TransNum)*100);
Form1- >ProgressBar1- >Update();
}
else
{Form1- >ProgressBar1- >Position=j;
Form1- >ProgressBar1- >Update();
}
return cbrCONTINUE;
//必须返回cbrCONTINUE以便让BatchMove继续
//若返回cbrABORT则终止拷贝
}

---- 一 切 完 成 以 后, 每 当 调 用 长 时 间BDE 操 作( 比 如BatchMove1->Exectue()) 时 都 会 触 发 该 回 调 函 数, 注 意 在 不 需 要 时 应" 注 销" 这 个 回 调 函 数。

---- 如 果 批 量 传 送 数 据 时 间 很 长, 则 必 须 为 用 户 提 供 终 止 该 操 作 的 机 会, 前 面 提 到, 若 回 调 函 数 返 回cbrABORT, 则BatchMove 过 程 立 即 终 止。 可 以 在Form 上 加 上 一 个" 停 止" 按 钮 和 一 个 全 局 布 尔 变 量isContinue, 当 开 始 拷 贝 时 设 该 变 量 为true, 当 按 钮 按 下 后, 设 该 变 量 为false, 每 次 调 用 回 调 函 数 时 检 查isContinue 的 值, 若 为true 则 回 调 函 数 返 回cbrCONTINUE 让 拷 贝 继 续, 否 则 返 回cbrABORT 终 止 拷 贝。 但 是 问 题 在 于 一 旦 拷 贝 过 程 开 始, 该 进 程 内 所 有 消 息 将 被 阻 塞, 应 用 程 序 在 拷 贝 结 束 之 前 没 有 机 会 响 应 键 盘、 鼠 标 等 一 切 消 息, 连 屏 幕 刷 新 都 不 能 完 成, 因 此 必 须 找 到 一 种 避 免 消 息 阻 塞 的 方 法。

---- 大 家 知 道,Windows 是 靠 事 件( 消 息) 驱 动 的, 在WIN32 系 统 中 有 两 种 消 息 队 列: 系 统 队 列 和 应 用 程 序 队 列, 当 一 个 程 序 进 行 一 个 长 时 间 操 作 时, 系 统 分 配 给 该 程 序 的 时 间 片 将 完 全 用 于 处 理 该 操 作, 换 句 话 说, 应 用 程 序 没 有 从 它 的 应 用 程 序 队 列 中 取 出 消 息 并 处 理 的 机 会, 这 样 该 程 序 将 停 止 一 切 对 外 部 事 件 的 响 应 直 到 该 操 作 完 成 为 止。 具 体 到 本 文 中 就 是 程 序 必 须 等 到BatchMove1- >Execute() 执 行 完 毕 后 才 能 响 应 用 户 操 作, 因 此 用 户 将 完 全 没 有 机 会 终 止 拷 贝 过 程。

---- 解 决 的 办 法 是: 在 回 调 函 数 中 取 出 消 息 队 列 中 的 消 息, 并 后 台 处 理 它 们, 这 样 用 户 将 有 机 会 按 下 终 止 按 钮。 实 现 的 代 码 很 简 单, 在 回 调 函 数 中 最 后 加 入 以 下 代 码 即 可

CBRType __stdcall ApiCallBackFun(…)
{
……
MSG amsg;
while(PeekMessage(&amsg,NULL,0,0,PM_REMOVE))
//从队列中取消息
{
TranslateMessage(&amsg); //翻译消息
DispatchMessage(&amsg); //分发消息
}
if (isContinue)
return cbrCONTINUE;
else
return cbrABORT;
}

---- 以 上 的 代 码 虽 然 都 用CBuilder 编 写, 但 是 其 原 理 同 样 适 用 于DELPHI。

 

转自:大家论坛

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值