Usando Sockets

Propaganda
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.
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.
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
02
03
04
05
06
07
08
09
10
import
import
import
import
import
java.io.BufferedReader;
java.io.IOException;
java.io.InputStreamReader;
java.net.ServerSocket;
java.net.Socket;
public class Servidor {
public static void main(String[] args) {
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ív
el
23
serv = new ServerSocket(7000);
24
25
//Aguarda uma conexão na porta especificada e cria reto
rna o socket que irá comunicar com o cliente
26
s = serv.accept();
27
28
//Cria um BufferedReader para o canal da stream de entr
ada de dados do socket s
29
entrada = new BufferedReader(new InputStreamReader(s.ge
tInputStream()));
30
31
//Aguarda por algum dado e imprime a linha recebida qua
ndo recebe
32
System.out.println(entrada.readLine());
33
34
//trata possíveis excessões de input/output. Note que as ex
cessõ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 alg
o errado.
38
System.out.println("Algum problema ocorreu para criar o
u 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
2
3
4
5
6
7
8
//Declaro o ServerSocket
ServerSocket serv=null;
//Declaro o Socket de comunicação
Socket s= null;
//Declaro o leitor para a entrada de dados
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
2
3
4
5
//Encerro o socket de comunicação
s.close();
//Encerro o ServerSocket
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.
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 especif
icada
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
38
39
40
41
42
43
44 }
s.close();
}catch(IOException e){}
}
}
Nas linhas
1
2
3
4
5
//Declaro o socket cliente
Socket s = null;
//Declaro a Stream de saida de dados
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.
Download