Usando Sockets

Leandro de Camargo Araujo Lima

Aprendendo a criar uma aplicação cliente/servidor.



Sockets. O que são, pra que servem e como funcionam


A estrutura Socket foi uma inovação apresentada pelo sistema Berkeley Unix. Através desta estrutura, o programador por ler e gravar bytes como uma stream qualquer de dados.
Além disto, ?esconde? os detalhes de baixo nível das redes tais como tipo de transmissão, tamanho de pacote, retransmissão e etc.

Através de um socket podemos realizar várias operações, como exemplo:

  • Estabelecer conexões entre máquinas
  • Enviar e receber dados
  • Encerrar conexões
  • Esperar por conexões em determinada porta

    O socket é na verdade um elemento de software que provê uma interface de rede para a aplicação.

    Vamos tratar dos sockets TCP, porém Java permite a utilização de sockets UDP e fornece meios para que você possa utilizar outros tipos não definidos através da classe SocketImpl e da interface SocketImplFactory.

    Onde estão? Quem são?
    Os sockets estão localizados no pacote java.net. Basicamente precisamos das classes Socket e ServerSocket para conseguir implementar uma aplicação básica.

    A classe Socket implementa o socket cliente. Para construir um socket precisamos saber qual é o IP que desejamos conectar e a porta de conexão (que varia de 0 a 65535).

    A classe ServerSocket fornece a interface de rede necessária para que a aplicação possa funcionar como um servidor TCP. Para criar um ServerSocket precisamos saber qual é a porta que será utilizada. Comumente utiliza-se portas acima de 1000 pois as inferiores são utilizadas pelo sistema operacional.





    Mãos à obra
    Para ilustrar o uso dos sockets, iremos construir uma aplicação bastante simples para comunicação de dois computadores. Um computador ficará aguardando alguma conexão e irá exibir em tela o que foi recebido.

    Veremos então a classe Servidor e a classe Cliente.

    01 import java.io.BufferedReader;
    02 import java.io.IOException;
    03 import java.io.InputStreamReader;
    04 import java.net.ServerSocket;
    05 import java.net.Socket;
    06 
    07 public class Servidor {
    08 
    09     public static void main(String[] args) {
    10         
    11         //Declaro o ServerSocket
    12         ServerSocket serv=null
    13         
    14         //Declaro o Socket de comunicação
    15         Socket s= null;
    16         
    17         //Declaro o leitor para a entrada de dados
    18         BufferedReader entrada=null;
    19                 
    20         try{
    21             
    22             //Cria o ServerSocket na porta 7000 se estiver disponível
    23             serv = new ServerSocket(7000);
    24         
    25             //Aguarda uma conexão na porta especificada e cria retorna o socket que irá comunicar com o cliente
    26             s = serv.accept();
    27             
    28             //Cria um BufferedReader para o canal da stream de entrada de dados do socket s
    29             entrada = new BufferedReader(new InputStreamReader(s.getInputStream()));
    30             
    31             //Aguarda por algum dado e imprime a linha recebida quando recebe
    32             System.out.println(entrada.readLine());    
    33             
    34         //trata possíveis excessões de input/output. Note que as excessões são as mesmas utilizadas para as classes de java.io    
    35         }catch(IOException e){
    36         
    37             //Imprime uma notificação na saída padrão caso haja algo errado.
    38             System.out.println("Algum problema ocorreu para criar ou receber o socket.");
    39         
    40         }finally{
    41             
    42             try{
    43                 
    44                 //Encerro o socket de comunicação
    45                 s.close();
    46                 
    47                 //Encerro o ServerSocket
    48                 serv.close();
    49                 
    50             }catch(IOException e){
    51             }
    52         }
    53     
    54         
    55         
    56         
    57         
    58     }
    59 }


    Vamos explicar os pontos importantes da classe acima.

    No techo

    1 //Declaro o ServerSocket
    2 ServerSocket serv=null
    3 
    4 //Declaro o Socket de comunicação
    5 Socket s= null;
    6 
    7 //Declaro o leitor para a entrada de dados
    8 BufferedReader entrada=null;


    apenas declaramos as variáveis que iremos utilizar.

    Na trecho

    1 //Cria o ServerSocket na porta 7000 se estiver disponível
    2 serv = new ServerSocket(7000);


    , criamos o server socket na porta 7000.


    No trecho

    1 //Aguarda uma conexão na porta especificada e cria retorna o socket que irá comunicar com o cliente
    2 s = serv.accept();


    utilizamos o método accept() que espera por uma conexão e continua somente quando recebe uma. Então retorna um socket para comunicar com o cliente que acaba de se conectar.
    O método accept() do ServerSocket, quando invocado, faz com que a Thread atual seja "paralisada" até que uma conexão seja recebida.
    É comum, em ambientes reais, lançarmos uma Thread a cada conexão recebida pelo ServerSocket. Isto é feito para que possamos tratar vários clientes conectados simultaneamente. Veremos como fazer isto em um outro tutorial.

    No trecho

    1 //Cria um BufferedReader para o canal da stream de entrada de dados do socket s
    2 entrada = new BufferedReader(new InputStreamReader(s.getInputStream()));


    criamos um leitor dos dados de entrada baseado no canal de entrada de dados do socket.
    O BufferedReader lê texto de uma text-input stream e os deixa em buffer para leitura eficiente das informações.
    O InputStreamReader lê os bytes que estão chegando e os transforma em caracteres para que o BufferedReader possa entender.

    No trecho

    1 //Aguarda por algum dado e imprime a linha recebida quando recebe
    2 System.out.println(entrada.readLine());    


    chamamos o método readline() da classe BufferedReader. Através deste método, o programa aguarda a chegada de algum dado no canal de entrada e lê uma linha. Após linha ser recebida ela é impressa na saída padrão do sistema com o popular System.out.println().

    Nas linhas

    1 //Encerro o socket de comunicação
    2 s.close();
    3 
    4 //Encerro o ServerSocket
    5 serv.close();


    , fechamos os sockets. Note que fizemos isto dentro de um bloco do tipo finally pois isto garante que os recursos serão liberados. É muito importante liberar este tipo de recurso sempre que não forem mais necessários, pois são finitos e representam um custo considerável de manutenção.



    Mãos à Obra - Continuando...
    Agora vejamos a classe Cliente.

    01 
    02 import java.io.IOException;
    03 import java.io.PrintStream;
    04 import java.net.Socket;
    05 
    06 public class Cliente {
    07 
    08     public static void main(String[] args) {
    09         
    10         //Declaro o socket cliente
    11         Socket s = null;
    12         
    13         //Declaro a Stream de saida de dados
    14         PrintStream ps = null;
    15         
    16         try{
    17             
    18             //Cria o socket com o recurso desejado na porta especificada
    19             s = new Socket("127.0.0.1",7000);
    20             
    21             //Cria a Stream de saida de dados
    22             ps = new PrintStream(s.getOutputStream());
    23             
    24             //Imprime uma linha para a stream de saída de dados
    25             ps.println("Estou enviando dados para o servidor");
    26             
    27         //Trata possíveis exceções
    28         }catch(IOException e){
    29             
    30             System.out.println("Algum problema ocorreu ao criar ou enviar dados pelo socket.");
    31         
    32         }finally{
    33             
    34             try{
    35                 
    36                 //Encerra o socket cliente
    37                 s.close();
    38                 
    39             }catch(IOException e){}
    40         
    41         }
    42 
    43     }
    44 }


    Nas linhas

    1 //Declaro o socket cliente
    2 Socket s = null;
    3 
    4 //Declaro a Stream de saida de dados
    5 PrintStream ps = null;


    apenas declaramos as variáveis que iremos utilizar.

    Na linha

    1 //Cria o socket com o recurso desejado na porta especificada
    2 s = new Socket("127.0.0.1",7000);


    criamos o socket. De acordo com o construtor que utilizamos, criamos um socket para comunicação com o IP 127.0.0.1 (IP LoopBack*) na porta 7000 (que a porta que nosso servidor irá ?escutar?.

    Na linha

    1 //Cria a Stream de saida de dados
    2 ps = new PrintStream(s.getOutputStream());


    criamos um objeto do tipo PrintStream para poder imprimir dados para o canal de saída do socket.

    Na linha

    1 //Imprime uma linha para a stream de saída de dados
    2 ps.println("Estou enviando dados para o servidor");


    utilizamos o método println() da classe PrintStream para imprimir uma String que será enviada através do socket para o Servidor.

    O método println() da classe PrintStream converte os caracteres digitados para o formato adequado de envio através do socket. Verifique a especificação da API para verificar todos os tipos de dados que podem ser impressos por este método.

    Na linha

    1 //Encerra o socket cliente
    2 s.close();


    fechamos o socket dentro do bloco finally.

    Agora, que entendemos o código podemos executá-los

    Copie os arquivos do tutorial para uma pasta e compile-os da forma comum.

    javac Servidor.java
    javac Cliente.java

    Agora, abra 2 janelas de prompt. Vamos executar cada classe em um prompt diferente (para testar em um mesmo equipamento).

    Em uma das janelas digite java Servidor e na outra digite java Cliente.

    Você deverá perceber que ao executar a classe Cliente, o texto ?Estou enviando dados para o servidor? será exibido na janela da classe Servidor.

    Pronto, esta é a nossa aplicação funcionando em rede de forma simples.

    Faça testes, verifique o que acontece quando o servidor não está rodando, altere os Ips caso esteja em rede, ou se estiver conectado a internet teste com seus amigos, leia a especificação das APIs e, quem sabe, adicionar recursos...

    Demos o passo inicial, agora é com você!

    *IP LoopBack ? Termo utilizado para representar uma forma de comunicação com o próprio equipamento (geralmente utilizado para efetuar testes). Para acessá-lo, utilizamos 127.0.0.1 ou simplesmente localhost.





  • Copyright © 2002-2006 GUJ | Todas as marcas e marcas registradas que aparecem no GUJ são de propriedade de seus respectivos donos