CAPÍTULO 6
Programação visual com Software Livre
EDIT LIN EDITORIAL S.L,  dos autores  Daniel Campos Fernández e José Luis Redrejo.  Prólogo de Benoit Minisini

<< Anterior Próximo >>


6.2 Criando um servidor TCP

Nossa primeira tarefa consistirá em escrever o código de um servidor TCP: será um servidor que aceita conexões remotas, lerá os dados enviados e devolverá um eco aos clientes. Criaremos um programa de console chamado MeuServidor. Este programa terá um único módulo chamado  ModMain. E nas propriedades do projeto selecionaremos o componente gb.net.

Dentro do módulo ModMain teremos uma referência a um objeto ServerSocket. Estes objetos comportam-se como servidores de socketes, quer dizer, encontram-se a escuta de petições de clientes remotos em um porta determinada. As portam são numeradas de 1 a 65535 e cada programa que atua como servidor dentro do sistema utiliza uma delas.

zFigura 1
Figura 1.Programa MeuServidor.

As portas com número de 1 a 1024,  se consideram reservadas para serviços conhecidos (HTTP, POP FTP, IMAP...) e podem ser utilizados por programas cujo usuário é diferente de root em sistemas GNU/Linux. Tratar, por tanto, de abrir, por exemplo, a porta 523 a partir de um programa executado por um usuário normal, dará um erro.

Os serviços mais conhecidos como HTTP ou FTP já tem uma porta designada (por exemplo, 80 no caso de HTTP) de forma padrão, se bem que não há nenhuma razão técnica pela qual um servidor web não possa atender, por exemplo, a porta 9854. Esto se deve unicamente a um acordo internacional para que qualquer cliente que queira realizar uma petição web, saiba que, salvo instruções especificas ao contrário, terá que conectar-se a porta 80 da maquina servidora.

Em sistema GNU/Linux podemos encontrar uma lista dos serviços mais comuns e suas portas oficiais dentro do arquivo de texto /etc/services

Em nosso caso vamos utilizar a porta 3152. Na função Main do programa,deveremos criar o objeto ServerSocket, especificar que seu tipo será internet (isso se for um socket TCP e não UNIX), a porta que deve atender e indicar-lhe que comece com as petições.

PRIVATE Servidor AS ServerSocket


PUBLIC SUB Main()

 
Servidor = NEW ServerSocket AS "Servidor"

Servidor.Type = Net.Internet

Servidor.Port = 3152

TRY Servidor.Listen()

 
IF ERROR THEN PRINT "Erro: oo sistema não permite atender a porta especificada"

 
END

Observamos que na hora de criar o objeto servidor, indicamos que o gestor de eventos será "Servidor",  para poder escrever as rotinas nas quais tratemos as petições que chega dos clientes.

Na hora de indicar ao servidor que ponha-se a escutar com o método listen, o fazemos protegendo o código com uma instrução TRY, para gestionar o erro se o sistema não permitir atender a porta indicada (por exemplo, se outro serviço já o estiver utilizando). Já podemos executar o programa.

Como podemos observar, o programa passa a função Main e segue executando a espera da conexão de um.  Um programa normal de console, ao terminar esta função, teria finalizado sem mais, mas neste caso o interpretador Gambas está atendendo as novidades que podem aparecer no socket que foi criado e, por tanto, o programa segue funcionando, esperando petições de clientes.

Qualquer programa Gambas que está vigiando um descritor, ou seja, um arquivo, um processo ou um socket, não finaliza enquanto esse descritor se encontre aberto. Esta técnica permite criar programa de console que não ficam em um loop continuo a espera de novidades (seja uma modificação em um arquivo, uma entrada de um cliente em um socket ou a leitura de dados de um processo filho), poupando recursos e melhorando a eficácia do processo.
 
O programa servidor já está funcionando, enquanto não haja nada realmente útil. Pra fazer, se testarmos ao conectarmos a partir do console com o programa telnet em nosso próprio servidor, observaremos que conecta-se e imediatamente desconecta-se do servidor.
Figura 2
Figura 2. Teste de conexão.

O funcionamento do objeto ServerSocket é precisamente este: recebe uma conexão de um cliente e, se não especificarmos ao programa que desejamos atende-la, a fecha.

Quando um objeto servidor recebe uma petição de um cliente, dispara o evento Connection. Dentro desse evento, e só dentro dele, podemos utilizar o método Accept. Esse método devolve um objeto Socket que representa uma conexão com esse cliente. Na hora de receber e enviar dados a esse cliente, faremos através desse objeto, desse modo, se diferencia a cada cliente conectado com um servidor de forma única, evitando, por exemplo, que enviamos o resultado de um calculo a um cliente equivocado.

Em nosso programa adicionaremos uma matriz de objetos onde guardaremos uma referencia a cada cliente, e nessa matriz iremos adicionando os clientes que iram conectar-se.

PRIVATE Servidor AS ServerSocket

PRIVATE Clientes AS NEW Object[]


PUBLIC SUB Servidor_Connection(RemoteHostIP AS String)

DIM Cliente AS Socket


Cliente = Servidor.Accept()

Clientes.Add(Cliente)


END


PUBLIC SUB Main()


Servidor = NEW ServerSocket AS "Servidor"

Servidor.Type = Net.Internet

Servidor.Port = 3152

TRY Servidor.Listen()


IF ERROR THEN PRINT "Erro: O sistema não permite atender a porta especificada"


END

Em nosso gestor de eventos Connection utilizamos o método Accept, para que sempre aceitemos as conexões de entrada. No entanto, se o omitimos em casos determinados, a tentativa de conexão do cliente encerra-se automaticamente. Observamos, por exemplo, que Accept nos informa do endereço IP do cliente que trata de conectar-se através do parâmetro RemoteHostIP.  Se desejarmos, por exemplo, aceitar só conexões a partir de nosso próprio equipamento (que sempre  tem endereço 127.0.0.1), poderíamos modificar o código para que recuse as conexões de outros endereços IP.

PUBLIC SUB Servidor_Connection(RemoteHostIP AS String)

DIM Cliente AS Socket


IF RemoteHostIP = "127.0.0.1" THEN

Cliente = Servidor.Accept()

Clientes.Add(Cliente)

END IF


END

Este objeto socket, por padrão recebem um gestor de eventos chamado Socket, pelo qual os eventos dos clientes de socket se entendem no programa por um função chamada Socket_ + Nome do evento.

Em nosso caso utilizamos os eventos Read produzido quando se recebe dados vindo dos clientes, e o evento Close, que ocorre quando quando o cliente encerra a conexão.

No caso do evento READ, nos valemos da palavra chave LAST, que mantem uma referência ao último objeto que disparou um evento (neste caso, o cliente de socket) para tratar os dados que provem dele.

Para isso, leremos o socket como se fosse um arquivo, com a instrução READ, no qual indicamos a cadeia que recebe os dados; leremos o comprimento dos dados disponíveis no sockt, determinada pela função Lof() e, depois escreveremos esses mesmos dados no socket com a instrução WRITE, de forma que o cliente receba um eco dos dados enviados, mas convertidos em maiúsculas.

PUBLIC SUB Socket_Read()
DIM sCad AS String
 
TRY READ #LAST, sCad, Lof(LAST)
sCad = UCase(sCad)
TRY WRITE #LAST, sCad, Len(sCad)
  
END

Para Close, buscamos o objeto Socket dentro de nossa matriz e o eliminamos para que desapareçam as referências a esse cliente dentro de nosso programa servidor.

PUBLIC SUB Socket_Close()

DIM Ind AS Integer


Ind = Clientes.Find(LAST)

IF Ind >= 0 THEN Clientes.Remove(Ind)


END

Já dispomos de um programa servidor de socket com a capacidade para atender a múltiplos clientes: podemos abrir várias janelas de terminal, executando nelas telnet 127.0.0.1 352, e enviar e receber dados do servidor. Cada vez que escrevemos uma cadeia e a enviamos pressionando Return, receberemos a cadeia convertida em maiúsculas como resposta do servidor.

Ainda que, o programa telnet foi criado pára controlar um equipamento de forma remota, também é um cliente universal que pode servir para comprovar o funcionamento de qualquer servidor que estejamos construindo, antes de dispor de um cliente real.
 
Figura 3
Figura 3.Programa servidor de socket.







<< Anterior Próximo >>

HOME
 


Cópia literal

Extraído do livro “GAMBAS, programação visual com software Livre”, da editora EDIT LIN EDITORIAL S.L,  dos autores  Daniel Campos Fernández e José Luis Redrejo. Prólogo de Benoit Minisini

LICENSA DESTE DOCUMENTO


É permitido a cópia e distribuição da totalidade ou parte desta obra sem fins lucrativo. Toda cópia total ou parcial devera expressar o nome do autor e da editora e incluir esta mesma licença, adicionando se é uma cópia literal “Cópia literal”. Se é autorizada a modificação e tradução da obra  sem fins lucrativo sempre se deve constar na obra resultante  a modificação o nome da obra original o autor da obra original e o nome da editora e a obra resultante também deverá ser livremente reproduzida, distribuída, ao publico e transformada em termos similares ao exposto nesta licença.

Tradução

Cientista
 (Antonio Sousa)