一、 引 言---- 近 年 来, 利 用Internet 进 行 网 际 间 通 讯, 在WWW 浏 览、FTP、Gopher 这 些 常 规 服 务, 以 及 在 网 络 电 话、 多 媒 体 会 议 等 这 些 对 实 时 性 要 求 严 格 的 应 用 中 成 为 研 究 的 热 点, 而 且 已 经 是 必 需 的 了。Windows 环 境 下 进 行 通 讯 程 序 设 计 的 最 基 本 方 法 是 应 用Windows Sockets 实 现 进 程 间 的 通 讯, 为 此 微 软 提 供 了 大 量 基 于Windows Sockets 的 通 讯API, 如WinSock API、WinInet API 和ISAPI, 并 一 直 致 力 于 开 发 更 快、 更 容 易 的 通 讯API, 将 其 和MFC 集 成 在 一 起 以 使 通 讯 编 程 越 来 越 容 易。 ---- MFC 是VC 编 程 环 境 最 重 要 的 组 成 部 分, 它 为 用 户 提 供 了 一 大 批 预 先 定 义 的 类 和 成 员 函 数, 封 装 了 大 量 的Windows API。 同 时VC 环 境 提 供 了 与MFC 对 象 和 代 码 一 起 工 作 的 专 用 工 具:AppStudio 源 程 序 编 辑 器、AppWizard 和Class Wizard。 应 用MFC, 可 以 使Windows 程 序 员 用 较 少 的 时 间 和 精 力 开 发 出 复 杂 的 通 讯 应 用 程 序。
---- 本 文 根 据 笔 者 自 己 在 开 发 实 时 网 络 音 频 工 具FreeTalk 过 程 中 的 一 些 经 验, 介 绍Windows 环 境 下 的 常 用API 和 封 装 它 们 的MFC 类, 重 点 介 绍 使 用MFC 的CAsyncsocket 和CSocket 类 编 写 网 络 通 讯 程 序 的 方 法, 这 两 个 类 封 装 了WinSock API, 并 使 他 们 更 容 易 使 用 和 更 适 应 于MFC 编 程 环 境。
二、Windows 环 境 下 的 通 讯API 和 相 应 的MFC 类---- 1. Windows Sockets(WinSock)API ---- Windows Sockets 定 义 了Windows 的 网 络 编 程 接 口, 它 基 于 加 利 福 尼 亚 大 学 伯 克 利 分 校 的 伯 克 利Unix Sockets。Windows Sockets 既 包 括BSD 风 格 的 例 程, 还 加 入 了Windows 的 扩 展 部 分, 例 如 用 于 消 息 驱 动 的 扩 展 函 数。Windows Sockets 可 以 运 行 在 许 多 网 络 协 议 之 上, 包 括TCP/IP、XNS、DECNet、IPX/SPX 等。 在Win32 环 境 下,Windows Sockets 提 供 线 程 安 全。 通 过 微 软 与 标 准 组 织 的 努 力, 为WinSock 定 义 了 应 用 程 序 设 计 接 口(WinSock API), 可 以 非 常 方 便 地 利 用 下 层 的 网 络 协 议( 如TCP/IP) 进 行 网 络 通 讯。
---- 通 过 提 供 两 个 类CAsyncSocket 和CSocket,MFC 支 持 使 用WinSock API 通 讯 程 序 设 计。MFC 把 复 杂 的WinSock API 封 装 到 类 里, 这 使 得 编 写 应 用 程 序 更 容 易。CAsyncSocket 类 逐 个 封 装 了WinSock API, 为 高 级 网 络 程 序 员 提 供 了 更 加 有 力 而 灵 活 的 方 法。 这 个 类 基 于 程 序 员 了 解 网 络 通 讯 的 假 设, 目 的 是 为 了 在MFC 中 使 用WinSock, 程 序 员 有 责 任 处 理 诸 如 阻 塞、 字 节 顺 序 和 在Unicode 与MBCS 间 转 换 字 符 的 任 务。 为 了 给 程 序 员 提 供 更 方 便 的 接 口 以 自 动 处 理 这 些 任 务,MFC 给 出 了CSocket 类, 这 个 类 是 由CAsyncSocket 类 继 承 下 来 的, 它 提 供 了 比CAsyncSocket 更 高 层 的WinSock API 接 口。Csocket 类 和CsocketFile 类 与Carchive 类 一 起 合 作 来 管 理 发 送 和 接 收 的 数 据, 这 使 管 理 数 据 收 发 更 加 便 利。CSocket 对 象 提 供 阻 塞 模 式, 这 对 于Carchive 的 同 步 操 作 是 至 关 重 要 的。 阻 塞 函 数[ 比 如Receive()、Send()、ReceiveFrom()、SendTo() 和Accept()] 直 到 操 作 完 成 后 才 返 回 控 制 权, 因 此 如 果 需 要 低 层 控 制 和 高 效 率, 就 使 用CasyncSock 类; 如 果 需 要 方 便, 则 可 使 用Csocket 类。 2.Win32 Internet(WinInet)API
---- 微 软 公 布 了 一 些 使Internet 应 用 程 序 的 设 计 比 以 前 更 快、 更 容 易 的API:WinInet API, 它 提 供 了 中 高 层 通 信 函 数, 这 使 访 问 主 要 的Internet 协 议 变 得 相 当 容 易。 这 些 函 数 在 程 序 员 和WinSock 驱 动 之 间 提 供 了 隔 离 层。 有4 类WinInet API 函 数: 通 用WinInet 函 数、WinInet 文 件 传 输 协 议(FTP) 函 数、WinInet Gopher 函 数、WinInet 超 文 本 传 输 协 议(HTTP) 函 数。
---- 事 实 上,MFC 把WinInet API 和ActiveX 技 术 封 装 进 类, 使Internet 编 程 更 加 容 易, 这 些 类 包 括CInternetSession、CInternetConnection、CInternetFile、CHttpConnection、CHttpFile、CGopherFile、CFtpConnection、CGopherConnection、CFileFind、CFtpFileFind、CGopherFileFind、CGopherLocator 和CInternetException。
---- 3.Internet 服 务 器API(ISAPI)
---- 微 软 的IIS 是 惟 一 与Windows NT Server 操 作 系 统 紧 密 集 成 的WWW 服 务 器, 它 作 为Internet/Intranet 服 务 器 应 用 范 围 很 广。IIS 允 许 扩 展 功 能, 这 是 通 过ISAPI 来 实 现 的,ISAPI 描 述 了 与Internet 服 务 器 之 间 的 接 口。 用ISAPI 提 供 的 工 具, 可 建 立 高 性 能、 高 效 率、 满 足 商 业 安 全 及 符 合 新 的IIS 标 准 的Internet 服 务 器。 同 样,ISAPI 在MFC 中 由 典 型 的 类 所 封 装, 包 括CHttpFilter、CHttpFilterContext、CHttpServer、CHttpServerContext、Related Classes 和CHtmlStream。
三、WinSock API 的MFC 封 装 类---- 一 些 网 络 应 用 程 序( 如 网 络 电 话、 多 媒 体 会 议 工 具) 实 时 性 要 求 非 常 强, 要 求 能 够 直 接 应 用WinSock 发 送 和 接 收 数 据。 这 时 设 计 者 应 该 选 择 直 接 应 用WinSock API 或 者 由MFC 封 装 的WinSock API。 新 开 发 的 应 用 程 序 中, 为 了 充 分 利 用MFC 的 优 势, 首 选 方 案 应 当 是MFC 中 的CAsyncSocket 类 和CSocket 类, 这 两 个 类 完 全 封 装 了WinSock API, 并 提 供 更 多 的 便 利。 本 文 介 绍 应 用 这 两 个 类 的 编 程 模 型, 并 引 出 相 关 的 成 员 函 数 与 一 些 概 念 的 解 释。 ---- 1.CAsyncSocket 类 和CSocket 类 简 述
附 图 CAsyncSocket 类 和CSocket 类 的 继 承 关 系---- CAsyncSocket 类 和CSocket 类 的 继 承 关 系 由 附 图 给 出。CSocket 类 是 由CAsyncSocket 继 承 而 来 的, 事 实 上, 在MFC 中CAsyncSocket 逐 个 封 装 了WinSock API, 每 个CAsyncSocket 对 象 代 表 一 个Windows Socket, 使 用CAsyncSocket 类 要 求 程 序 员 对 网 络 编 程 较 为 熟 悉。 相 比 起 来,CSocket 类 是CAsyncSocket 的 派 生 类, 继 承 了 它 封 装 的WinSock API。 一 个CSocket 对 象 代 表 了 一 个 比CAsyncSocket 对 象 更 高 层 次 的Windows Socket 抽 象,CSocket 类 与CSocketFile 类 和CArchive 类 一 起 工 作 来 发 送 和 接 收 数 据, 因 此 使 用 它 更 加 容 易。CSocket 对 象 提 供 阻 塞 模 式, 因 为 阻 塞 功 能 对 于CArchive 的 同 步 操 作 是 至 关 重 要 的。 在 这 里 有 必 要 对 阻 塞 的 概 念 作 一 解 释: 一 个socket 可 以 处 于“ 阻 塞 模 式” 或“ 非 阻 塞 模 式”, 当 一 个 套 接 字 处 于 阻 塞 模 式( 即 同 步 操 作) 时, 它 的 阻 塞 函 数 直 到 操 作 完 成 才 会 返 回 控 制 权, 之 所 以 称 为 阻 塞 是 因 为 此 套 接 字 的 阻 塞 函 数 在 完 成 操 作 返 回 之 前 什 么 也 不 能 做。 如 果 一 个socket 处 于 非 阻 塞 模 式( 即 异 步 操 作), 则 会 被 调 用 函 数 立 即 返 回。 在CAsyncSocket 类 中 可 以 用GetLastError 成 员 函 数 查 询 最 后 的 错 误, 如 果 错 误 是WSAEWOULDBLOCK 则 说 明 有 阻 塞, 而CSocket 绝 不 会 返 回WSAEWOULDBLOCK, 因 为 它 自 己 管 理 阻 塞。 微 软 建 议 尽 量 使 用 非 阻 塞 模 式, 通 过 网 络 事 件 的 发 生 而 通 知 应 用 程 序 进 行 相 应 的 处 理。 但 在CSocket 类 中, 为 了 利 用CArchive 处 理 通 讯 中 的 许 多 问 题 和 简 化 编 程, 它 的 一 些 成 员 函 数 总 是 具 有 阻 塞 性 质 的, 这 是 因 为CArchive 类 需 要 同 步 的 操 作。 在Win32 环 境 下, 如 果 要 使 用 具 有 阻 塞 性 质 的 套 接 字, 应 该 放 在 独 立 的 工 人 线 程 中 处 理, 利 用 多 线 程 的 方 法 使 阻 塞 不 至 于 干 扰 其 他 线 程, 也 不 会 把CPU 时 间 浪 费 在 阻 塞 上。 多 线 程 的 方 法 既 可 以 使 程 序 员 享 受CSocket 带 来 的 简 化 编 程 的 便 利, 也 不 会 影 响 用 户 界 面 对 用 户 的 反 应。
---- 2.CAsyncsocket 类 编 程 模 型
---- 在 一 个MFC 应 用 程 序 中, 要 想 轻 松 处 理 多 个 网 络 协 议, 而 又 不 牺 牲 灵 活 性 时, 可 以 考 虑 使 用CAsyncSocket 类, 它 的 效 率 比CSocket 类 要 高。CAsyncSocket 类 针 对 字 节 流 型 套 接 字 的 编 程 模 型 简 述 如 下:
---- (1) 构 造 一 个CAsyncSocket 对 象, 并 用 这 个 对 象 的Create 成 员 函 数 产 生 一 个Socket 句 柄。 可 以 按 如 下 两 种 方 法 构 造:
CAsyncSocket sock;
Sock.Create();
// 使 用 默 认 参 数 产 生 一 个 字 节 流 套 接 字
或
CAsyncSocket*pSocket=new CAsyncSocket;
int nPort=27;
pSocket->Create(nPort, SOCK-DGRAM);
// 指 定 端 口 号 产 生 一 个 数 据 报 套 接 字
---- 第 一 种 方 法 在 栈 上 产 生 一 个CAsyncSocket 对 象, 而 第 二 种 方 法 在 堆 上 产 生CAsyncSocket 对 象。 第 一 种Create 成 员 函 数 用 缺 省 参 数 产 生 一 个 字 节 流 套 接 字, 第 二 种Create 成 员 函 数 用 指 定 的 端 口 和 地 址 产 生 一 个 数 字 报 套 接 字。Create 的 参 数 有: ---- ① 端 口,UINT 类 型。 注 意: 如 果 是 服 务 方, 则 使 用 一 个 众 所 周 知 的 端 口 供 服 务 方 连 接; 如 果 是 客 户 方, 典 型 做 法 是 接 受 默 认 参 数, 使 套 接 字 可 以 自 主 选 择 一 个 可 用 端 口;
---- ②socket 类 型。SOCK-STREAM( 默 认 值) 或SOCK-DGRAM;
---- ③socket 地 址。 例 如“ftp.gliet.edu.cn” 或“202.193.64.33”。
---- (2) 如 是 客 户 方 程 序, 用CAsyncSocket ∷Connect 成 员 函 数 连 接 到 服 务 方; 如 是 服 务 方 程 序, 用CAsyncSocket ∷Listen 成 员 函 数 开 始 监 听, 一 旦 收 到 连 接 请 求, 则 调 用CAsyncSocket ∷Accept 成 员 函 数 开 始 接 收。 注 意:CAsyncSocket ∷Accept 成 员 函 数 要 用 一 个 新 的 并 且 是 空 的CSocket 对 象 作 为 它 的 参 数, 这 里 所 说 的“ 空 的” 指 的 是 这 个 新 对 象 还 没 有 调 用Create 成 员 函 数。
---- (3) 调 用 其 他 的CAsyncSocket 类 成 员 函 数 进 行 通 讯 管 理。
---- (4) 通 讯 结 束 后, 销 毁CAsyncSocket 对 象。 如 果 是 在 栈 上 产 生 的CAsyncSocket 对 象, 则 对 象 超 出 定 义 的 范 围 时 自 动 被 析 构; 如 果 是 在 堆 上 产 生, 也 就 是 用 了new 这 个 操 作 符, 则 必 须 使 用delete 操 作 符 销 毁CAsyncSocket 对 象。
---- 3.CSocket 类 编 程 模 型
---- 使 用CSocket 对 象 涉 及CArchive 和CSocketFile 类 对 象。 以 下 介 绍 的 针 对 字 节 流 型 套 接 字 的 操 作 步 骤 中, 只 有 第3 步 对 于 客 户 方 和 服 务 方 操 作 是 不 同 的, 其 他 步 骤 都 相 同。
---- (1) 构 造 一 个CSocket 对 象。
---- (2) 使 用 这 个 对 象 的Create 成 员 函 数 产 生 一 个socket 句 柄。 在 客 户 方 程 序 中, 除 非 需 要 数 据 报 套 接 字,Create 一 般 情 况 下 应 该 使 用 默 认 参 数。 而 对 于 服 务 方 程 序, 必 须 在 调 用Create 时 指 定 一 个 端 口。 注 意:CArchive 不 能 与 数 据 报(UDP) 套 接 字 一 起 工 作, 因 此 对 于 数 据 报 套 接 字,CAsyncSocket 和CSocket 的 使 用 方 法 是 一 样 的。
---- (3) 如 果 是 客 户 方 套 接 字, 则 调 用CAsyncSocket ∷Connect 与 服 务 方 套 接 字 连 接; 如 果 是 服 务 方 套 接 字, 则 调 用CAsyncSocket ∷Listen 开 始 监 听 来 自 客 户 方 的 连 接 请 求, 收 到 连 接 请 求 后, 调 用CAsyncSocket ∷Accept 接 受 请 求, 建 立 连 接。 注 意:Accept 成 员 函 数 需 要 一 个 新 的 并 且 为 空 的CSocket 对 象 作 为 它 的 参 数, 解 释 同 上。
---- (4) 产 生 一 个CSocketFile 对 象, 并 把 它 与CSocket 对 象 关 联 起 来。
---- (5) 为 接 收 和 发 送 数 据 各 产 生 一 个CArchive 对 象, 把 它 们 与CSocketFile 对 象 关 联 起 来。 切 记CArchive 是 不 能 和 数 据 报 套 接 字 一 起 工 作 的。
---- (6) 使 用CArchive 对 象 在 客 户 与 服 务 方 传 送 数 据。 (7) 通 讯 完 毕 后, 销 毁CArchive、CSocketFile 和CSocket 对 象。
---- ( 作 者 地 址: 桂 林 电 子 工 业 学 院97 研,541004; 收 稿 日 期:1999 年7 月) -
-