查找服务器主机系统

服务器主机系统是执行分布式应用程序的服务器程序的计算机。 网络上可能有一个或多个服务器主机系统。 客户端程序如何查找要连接到的服务器取决于程序的需求。

可通过两种方法查找服务器主机系统:

  • 使用存储在客户端源代码、环境变量或特定于应用程序的配置文件中的字符串中的信息。 客户端应用程序可以使用字符串中的数据在客户端和服务器之间撰写绑定。
  • 查询名称服务数据库以获取服务器程序的位置。

本部分介绍以下主题中有关这两种技术的信息:

使用字符串绑定

应用程序可以根据存储在字符串中的信息创建绑定。 客户端应用程序将此信息编写为字符串,然后调用 RpcBindingFromStringBinding 函数。 客户端必须提供以下信息才能标识服务器:

(对象 UUID 和终结点信息是可选的。)

在以下示例中, pszNetworkAddress 参数和其他参数包括嵌入的反斜杠。 反斜杠是 C 编程语言中的转义字符。 需要两个反斜杠来表示每个文本反斜杠字符。 字符串绑定结构必须包含四个反斜杠字符,以表示服务器名称前面的两个文本反斜杠字符。

以下示例显示服务器名称前面必须有八个反斜杠,以便在 sprintf_s 函数处理字符串后,字符串绑定数据结构中将出现四个文本反斜杠字符。

/* client application */

char * pszUuid = "6B29FC40-CA47-1067-B31D-00DD010662DA";
char * pszProtocol = "ncacn_np";
char * pszNetworkAddress = "\\\\\\\\servername";
char * pszEndpoint = "\\\\pipe\\\\pipename";
char * pszString;
 
int len = 0;
 
len  = sprintf_s(pszString, strlen(pszUuid), "%s", pszUuid);
len += sprintf_s(pszString + len, strlen(pszProtocolSequence) + 2, "@%s:",
    pszProtocolSequence);
if (pszNetworkAddress != NULL)
    len += sprintf_s(pszString + len, strlen(pszNetworkAddress), "%s",
    pszNetworkAddress);
len += sprintf_s(pszString + len, strlen(pszEndpoint) + 2, "[%s]", pszEndpoint);

在以下示例中,字符串绑定显示为:

6B29FC40-CA47-1067-B31D-00DD010662DA@ncacn_np:\\\\servername[\\pipe\\pipe\\pipename]

然后,客户端调用 RpcBindingFromStringBinding 以获取绑定句柄:

RPC_BINDING_HANDLE hBinding;
 
status = RpcBindingFromStringBinding(pszString, &hBinding);
//...

RpcStringBindingCompose 是一个方便的函数,它以正确的语法组合对象 UUID、协议序列、网络地址和终结点,以便调用 RpcBindingFromStringBinding。 不必担心将每个协议序列的与号、冒号和各种组件放在正确的位置;只需将字符串作为参数提供给函数即可。 运行时库甚至会分配字符串绑定所需的内存。

char * pszNetworkAddress = "\\\\server";
char * pszEndpoint = "\\pipe\\pipename";
status = RpcStringBindingCompose(
            pszUuid,
            pszProtocolSequence,
            pszNetworkAddress,
            pszEndpoint,
            pszOptions,
            &pszString);
//...
status = RpcBindingFromStringBinding(
            pszString,
            &hBinding);
//...

另一个便捷函数 RpcBindingToStringBinding 采用绑定句柄作为输入,并生成相应的字符串绑定。

从名称服务数据库导入

除其他事项外,名称服务数据库存储绑定句柄和 UUID。 客户端应用程序需要绑定到服务器时,可以搜索其中一个或两者。 有关名称服务存储的信息和存储格式的讨论,请参阅 RPC 名称服务数据库

RPC 库提供两组函数,客户端程序可以使用这些函数来搜索名称服务数据库。 一个集的名称以 RpcNsBindingImport 开头。 另一个集的名称以 RpcNsBindingLookup 开头。 这两组函数的区别在于,RpcNsBindingImport 函数每次调用返回一个绑定句柄,而 RpcNsBindingLookup 函数则返回每个调用的句柄组。

若要开始使用 RpcNsBindingImport 函数进行搜索,请首先调用 RpcNsBindingImportBegin,如以下代码片段所示。

RPC_STATUS status;
RPC_NS_HANDLE hNameServiceHandle;
 
status = RpcNsBindingImportBegin(
    RPC_C_NS_SYNTAX_DEFAULT,
    NULL,
    MyInterface_v1_0_c_ifspec,
    NULL,
    &hNameServiceHandle);

当 RPC 函数搜索名称服务数据库时,它们需要一个开始搜索的位置。 在 RPC 术语中,这称为条目名称。 客户端程序将条目名称作为第二个参数传递给 RpcNsBindingImportBegin。 如果要搜索整个名称服务数据库,此参数可以为 NULL 。 或者,可以通过传递服务器条目名称来搜索服务器条目,或通过传递组条目名称来搜索组条目。 传递条目名称会将搜索限制为该条目的内容。

在前面的示例中,值 RPC_C_NS_SYNTAX_DEFAULT 作为第一个参数传递给 RpcNsBindingImportBegin。 这会选择默认条目名称语法。 目前,这是唯一支持的条目名称语法。

客户端应用程序可以在名称服务数据库中搜索接口名称、UUID 或两者。 如果要让其按名称搜索接口,请将 MIDL 编译器从 IDL 文件生成的全局接口变量作为第三个参数传递给 RpcNsBindingImportBegin。 可以在 MIDL 编译器在生成客户端存根时生成的头文件中找到其声明。 如果希望客户端程序仅按 UUID 进行搜索,请将第三个参数设置为 NULL

在名称服务数据库中搜索 UUID 时,请将 RpcNsBindingImportBegin 的第四个参数设置为要搜索的 UUID。 如果不搜索 UUID,请将此参数设置为 NULL

RpcNsBindingImportBegin 函数通过其第五个参数传递名称服务搜索上下文句柄的地址。 将此参数传递给其他 RpcNsBindingImport 函数。

具体而言,客户端应用程序将调用的下一个函数是 RpcNsBindingImportNext。 客户端程序使用此函数从名称服务数据库中检索兼容的绑定句柄。 以下代码片段演示如何调用此函数:

RPC_STATUS status;
RPC_BINDING_HANDLE hBindingHandle;
// The variable hNameServiceHandle is a valid name service search 
// context handle obtained from the RpcNsBindingBegin function.
 
status = RpcNsBindingImportNext(hNameServiceHandle, &hBindingHandle);

调用 RpcNsBindingImportNext 函数以获取绑定句柄后,客户端应用程序可以确定它收到的句柄是否可接受。 如果没有,客户端程序可以执行循环并再次调用 RpcNsBindingImportNext ,以查看名称服务是否包含更合适的句柄。 每次调用 RpcNsBindingImportNext 时,都必须有对 RpcNsBindingFree 的相应调用。 搜索完成后,调用 RpcNsBindingImportDone 函数以释放查找上下文。

客户端应用程序具有可接受的绑定句柄后,应检查以确保服务器应用程序正在运行。 客户端可以使用两种方法来执行此验证。 第一种是在客户端接口中调用函数。 如果服务器程序正在运行,则调用将完成。 否则,调用将失败。 验证服务器是否正在运行的更好方法是调用 RpcEpResolveBinding,然后调用 RpcMgmtIsServerListening。 有关名称服务数据库的详细信息,请参阅 RPC 名称服务数据库