Tópicos em Bancos de Dados: Sistemas de Informação
Inteligentes 1997/2
Implementação de clientes
A implementação de sistemas cliente / servidor usando
RPC requer a definição e o registro de uma interface específica.
Veja a definição
da interface deste servidor para mais detalhes. Cada interface registrada
é acessada pelo cliente especificando o nome e a versão do
servidor que se deseja utilizar, além do nome da máquina
em que o servidor está sendo executado.
Neste trabalho, definimos um servidor de nome TREC_SERVER com
apenas uma versão, de nome TBD97_2. O servidor está
executando na máquina solimoes.dcc.ufmg.br.
Como utilizar o servidor
O servidor fica executando eternamente na máquina servidora aguardando
solicitações de serviços por parte dos clientes. Quando
o servidor recebe um pedido, o processo cliente fica bloqueado aguardando
a resposta, ou uma mensagem de erro caso haja alguma falha com servidor.
O servidor processa o pedido e envia a resposta de volta, quando o cliente
volta ao processamento normal, já com os dados de resposta.
A seguir apresentamos os nomes, os parâmetros e os tipos de dados
retornados pelos serviços implementados. Observe atentamente que
todos os serviços retornam ponteiros e nunca estruturas nem dados.
lista_inv *lista_invertida_1(
texto *p, CLIENT *cl) |
Retorna o identificador do termo correspondente à palavra passada
como parâmetro, e a lista invertida deste termo. |
lista_term_freq *termos_documento_1(
documento *d, CLIENT *cl) |
Retorna uma lista de tuplas contendo todos os termos de um documento,
passado como parâmetro, e suas respectivas frequências dentro
daquele documento. |
texto *fonte_documento_1(
documento *d, CLIENT *cl) |
Retorna um string que é o texto em linguagem natural
de um documento, passado como parâmetro. |
lista_nor *norma_documentos_1(
lista_doc *ld, CLIENT *cl) |
Rertorna uma lista contendo as normas dos documentos contidos numa
lista de documentos, passada como parâmetro. A ordem da listas é
a mesma. |
lista_idf *idf_termos_1(
lista_ter *lt, CLIENT *cl) |
Retorna uma lista contendo os idfs dos termos da lista de termos
passada como parâmetro. A ordem das listas também é
a mesma. |
Note que todas as funções terminam com o sufixo (_1)
corresponde ao número da versão implementada.
Código de acesso ao servidor
Vamos descrever o código básico para se utilizar um serviço
remoto qualqer. O primeiro passo é criar uma referência para
chamadas de procedimentos remotos:
CLIENT *cl;
...
if (!(cl = clnt_create(servidor, TREC_SERVER, TBD97_2, "tcp"))){
printf("nao consegui acessar o servico em %s\n", servidor);
clnt_pcreateerror(servidor); /* exibe uma mensagem descrevendo
o erro */
exit(1);
}
A variável servidor no código acima contém
o nome da máquina em que o processo está executando. Repare
que além do programa e de sua versão, especificamos que a
comunicação com o servidor deve ser usando o protocolo TCP/IP.
Isto é importante por questões de confiabilidade. No nosso
caso é imprescindível o uso de TCP/IP por que o volume de
dados trocado é muito grande.
Observe que o resultado da chamada é armazenado numa variável
ponteiro (cl). Esta variável será utilizada posteriormente
quando da chamada efetiva do procedimento remoto. No exemplo a seguir,
fazemos uma chamada ao procedimento remoto que retorna o texto fonte de
um documento. No nosso exemplo, do documento 365799:
texto *resposta;
int param;
...
param = 365799;
resposta = fonte_documento_1(¶m, cl);
Note que tanto o parâmetro passado ao procedimento quanto o valor
retornado são referenciados através de ponteiros e não
através de referências diretas. Isto é uma peculiaridade
do protocolo RPC.
A variável resposta é local, portanto podemos
utilizá-la normalmente sem nenhum procedimento especial. O trecho
a seguir imprime o conteúdo da resposta (note que resposta
é um ponteiro para um texto).
printf("\ndocumento %d\n%s\n",param, *resposta);
Finalizando o cliente
Após utilizar o servidor, o cliente deve liberar a memória
alocada para cada resposta aos serviços e destruir o identificador
de cliente criado. Ainda sobre o exemplo descrito acima, temos:
xdr_free(xdr_texto, (char *) resposta); /*libera a memória
alocada */
clnt_destroy(cl); /*elimina o identificador de cliente cl*/
Observe que fazemos a conversão (casting) do ponteiro resposta
para o tipo char.
Naturalmente este código deve ser inserido no final do programa
cliente.
Compilando o cliente
Nesta seção vamos discutir o processo de compilação
de um cliente para o servidor TREC. Além do próprio código
fonte do programa precisamos de três arquivos gerados por um compilador
específico para aplicações que usam RPC. Estes arquivos
são gerados apenas uma vez e compilados usando um compilador C normal,
portanto os clientes não precisam se ater ao compilador de RPC.
Veja como pegar e utilizar estes e outros arquivos.
O arquivo de interface (trec.x) é compilado e gera quatro
novos arquivos:
trec.h |
contém definições de tipos de uso geral para o
cliente e para o servidor |
trec_clnt.c |
contém código e definições de comunicação
para o lado cliente da aplicação |
trec_svc.c |
contém código e definições de comunicação
para o lado servidor da aplicação |
trec_xdr.c |
contém definições para conversão de dados
entre os processos |
Vale salientar que precisamos gerar o código binário a partir
do arquivo trec_xdr.c antes de usá-lo. O comando a seguir
faz isso:
Este comando vai gerar um arquivo binário compilado de nome trec_xdr.o
que será usado na compilação efetiva do cliente. Este
comando só precisa ser executado uma vez.
Um programa cliente é um programa normal em C, com a mesma estrutura
de um programa qualqer. As únicas diferenças são que
o arquivo trec.h precisa ser incluído no programa e os
comandos especiais de RPC que já foram citados acima. No momento
da compilação precisamos incluir o "lado cliente da aplicação
gerada pelo sistema", através do seguinte comando:
gcc cliente.c trec_clnt.c trec_xdr.o -lsocket -lnsl -o cliente
O resultado do comando acima será um arquivo executável de
nome cliente. Evidentemente que outras opções de
compilação podem e devem ser incluídas nesta linha
de comando.