Localizando sistemas de host do servidor

Um sistema de host de servidor é o computador que executa o programa de servidor do aplicativo distribuído. Pode haver um ou muitos sistemas de host de servidor em uma rede. A forma como o programa cliente encontra um servidor para se conectar depende das necessidades do programa.

Há dois métodos de localização de sistemas de host de servidor:

  • Usando informações armazenadas em cadeias de caracteres no código-fonte do cliente, variáveis de ambiente ou arquivos de configuração específicos do aplicativo. Seu aplicativo cliente pode usar os dados na cadeia de caracteres para compor uma associação entre o cliente e o servidor.
  • Consultando um banco de dados de serviço de nome para o local de um programa de servidor.

Esta seção apresenta informações sobre ambas as técnicas nos seguintes tópicos:

Usando associações de cadeia de caracteres

Os aplicativos podem criar associações a partir de informações armazenadas em cadeias de caracteres. Seu aplicativo cliente compõe essas informações como uma cadeia de caracteres e, em seguida, chama a função RpcBindingFromStringBinding . O cliente deve fornecer as seguintes informações para identificar o servidor:

(As informações do objeto UUID e do ponto de extremidade são opcionais.)

Nos exemplos a seguir, o parâmetro pszNetworkAddress e outros parâmetros incluem cílios inseridos. A barra invertida é um caractere de escape na linguagem de programação C. Duas barras invertidas são necessárias para representar cada caractere de barra invertida literal. A estrutura de associação de cadeia de caracteres deve conter quatro caracteres de barra invertida para representar os dois caracteres de barra invertida literal que precedem o nome do servidor.

O exemplo a seguir mostra que o nome do servidor deve ser precedido por oito barras invertidas para que quatro caracteres de barra invertida literal apareçam na estrutura de dados de associação de cadeia de caracteres após a função sprintf_s processar a cadeia de caracteres.

/* 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);

No exemplo a seguir, a associação de cadeia de caracteres aparece como:

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

Em seguida, o cliente chama RpcBindingFromStringBinding para obter o identificador de associação:

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

Uma função de conveniência, rpcStringBindingCompose monta o objeto UUID, sequência de protocolo, endereço de rede e ponto de extremidade na sintaxe correta para a chamada para RpcBindingFromStringBinding. Você não precisa se preocupar em colocar a ampersand, dois-pontos e os vários componentes para cada sequência de protocolo no lugar certo; basta fornecer as cadeias de caracteres como parâmetros para a função. A biblioteca em tempo de execução ainda aloca a memória necessária para a associação de cadeia de caracteres.

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

Outra função de conveniência, RpcBindingToStringBinding, usa um identificador de associação como entrada e produz a associação de cadeia de caracteres correspondente.

Importando de bancos de dados de serviço de nome

Repositório de bancos de dados de serviço de nome, entre outras coisas, identificadores de associação e UUIDs. Seu aplicativo cliente pode pesquisar por ambos quando precisar ser associado ao servidor. Para obter uma discussão sobre as informações que um serviço de nome armazena e o formato de armazenamento, consulte o Banco de Dados de Serviço de Nome RPC.

A biblioteca RPC fornece dois conjuntos de funções que seu programa cliente pode usar para pesquisar o banco de dados do serviço de nome. Os nomes de um conjunto começam com RpcNsBindingImport. Os nomes do outro conjunto começam com RpcNsBindingLookup. A diferença entre os dois grupos de funções é que as funções RpcNsBindingImport retornam um único identificador de associação por chamada e as funções RpcNsBindingLookup retornam grupos de identificadores por chamada.

Para iniciar uma pesquisa com as funções RpcNsBindingImport, primeiro chame RpcNsBindingImportBegin, conforme mostrado no fragmento de código a seguir.

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

Quando as funções RPC pesquisam o banco de dados de serviço de nome, elas precisam de um local para iniciar a pesquisa. Na terminologia RPC, isso é chamado de nome de entrada. Seu programa cliente passa o nome de entrada como o segundo parâmetro para RpcNsBindingImportBegin. Esse parâmetro pode ser NULL se você quiser pesquisar todo o banco de dados de serviço de nome. Como alternativa, você pode pesquisar a entrada do servidor passando um nome de entrada de servidor ou pesquisar a entrada do grupo passando um nome de entrada de grupo. Passar um nome de entrada restringe a pesquisa ao conteúdo dessa entrada.

No exemplo anterior, o valor RPC_C_NS_SYNTAX_DEFAULT é passado como o primeiro parâmetro para RpcNsBindingImportBegin. Isso seleciona a sintaxe de nome de entrada padrão. Atualmente, essa é a única sintaxe de nome de entrada com suporte.

Seu aplicativo cliente pode pesquisar no banco de dados de serviço de nome um nome de interface, uma UUID ou ambos. Se você quiser que ele pesquise uma interface por nome, passe a variável de interface global que o compilador MIDL gera do arquivo IDL como o terceiro parâmetro para RpcNsBindingImportBegin. Você encontrará sua declaração no arquivo de cabeçalho que o compilador MIDL gerou quando gerou o stub do cliente. Se você quiser que seu programa cliente pesquise apenas por UUID, defina o terceiro parâmetro como NULL.

Ao pesquisar um UUID no banco de dados de serviço de nome, defina o quarto parâmetro de RpcNsBindingImportBegin para a UUID que você deseja pesquisar. Se você não estiver procurando uma UUID, defina esse parâmetro como NULL.

A função RpcNsBindingImportBegin passa o endereço de um identificador de contexto de pesquisa de serviço de nome por meio de seu quinto parâmetro. Você passa esse parâmetro para outras funções RpcNsBindingImport.

Em particular, a próxima função que seu aplicativo cliente chamaria é RpcNsBindingImportNext. Os programas cliente usam essa função para recuperar identificadores de associação compatíveis do banco de dados de serviço de nome. O fragmento de código a seguir demonstra como essa função pode ser chamada:

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);

Depois de chamar a função RpcNsBindingImportNext para obter um identificador de associação, seu aplicativo cliente poderá determinar se o identificador recebido é aceitável. Caso contrário, o programa cliente poderá executar um loop e chamar RpcNsBindingImportNext novamente para ver se o serviço de nome contém um identificador mais apropriado. Para cada chamada para RpcNsBindingImportNext, deve haver uma chamada correspondente para RpcNsBindingFree. Quando a pesquisa for concluída, chame a função RpcNsBindingImportDone para liberar o contexto de pesquisa.

Depois que o aplicativo cliente tiver um identificador de associação aceitável, ele deverá verificar se o aplicativo de servidor está em execução. Há dois métodos que seu cliente pode usar para executar essa verificação. A primeira é chamar uma função na interface do cliente. Se o programa de servidor estiver em execução, a chamada será concluída. Caso contrário, a chamada falhará. Uma maneira melhor de verificar se o servidor está em execução é invocar RpcEpResolveBinding, seguido por uma chamada para RpcMgmtIsServerListening. Para obter mais informações sobre o banco de dados de serviço de nome, consulte o Banco de Dados do Serviço de Nome RPC.