Você está na página 1de 15

Sistemas Distribuidos

Estrutura de dados
Tipo abstrato de dados
Sockets
(TAD)
Sockets
1. Introdução

Para estabelecermos a comunicação entre processos em um ambiente


distribuído e para permitirmos que processos se comuniquem na troca de
dados ou acessos a recursos ou serviços em processadores remotos, é
necessária a utilização de um mecanismo de serviço de transporte. O
mecanismo mais utilizado é o Socket (ALVES, 2008).
Sockets é a maneira mais popular de utilizar as funcionalidades de
comunicação TCP/IP (KUROSE, 2006). Todos os mecanismos Sockets são
gerenciados pela camada de transporte e há diversas APIs Sockets
(Application Program Interface) que podem ser utilizadas em linguagens de
programação, sendo que as mais populares são do ambiente Unix e a
WinSock do Windows.

SAIBA MAIS

Para maiores informações sobre API Sockets, leiam:


- Sockets Unix: Disponível em: <http://olinux.uol.com.br/artigos/370/1.html>.
- Sockets Windows: Disponível em: <http://msdn.microsoft.com/en-
us/library/windows/desktop/ms740673(v=vs.85).aspx>.

Um Socket é um ponto final (endpoint) de um canal bidirecional de


comunicação entre dois programas executados em uma rede, sendo que
para definir um endereço de Socket é necessário:
 Endereço local (número da porta) que se refere ao endereço da
porta de comunicação para camada de transporte;
 Endereço global (nome do host) que se refere ao endereço do
computador (host) na rede.

A Figura 1 mostra uma analogia de um Socket com uma linha


telefônica. Ambos servem para a comunicação, sendo que um Socket realiza
a comunicação de processos e a linha telefônica realiza a comunicação entre
pessoas.

Sistemas distribuidos – Sockets 2


Figura 1 – Analogia de um Socket

O primeiro passo em um Socket é a sua criação, sendo que é definida


uma porta que o Socket utilizará para a comunicação. Na linha telefônica,
define-se o número do telefone. Após isso, no Socket, definimos quantas
conexões conseguimos atender, o colocamos em modo de espera e ele fica
esperando pedidos de conexão de outros processos. Já na linha telefônica,
conectamos a linha telefônica ao aparelho e o deixamos no gancho a espera
que outras pessoas liguem para o número do telefone.
Assim que um processo faz a conexão com o Socket, cria-se o canal
de comunicação bidirecional para que os processos troquem mensagens. Na
linha telefônica, assim que o telefone toca e a pessoa atende, elas podem
conversar. Ao final, ambos fecham a conexão / desligam.

Sistemas distribuidos – Sockets 3


Os próximos tópicos abordam como é a conexão, a comunicação e um
exemplo de Sockets em Java.

2. Conexão

Na comunicação Cliente-Servidor, o servidor é a entidade passiva e


fica apenas “ouvindo” o Socket, na porta informada, aguardando um pedido
de conexão do cliente, sendo que de alguma forma o cliente sabe o nome do
host (computador) e qual porta está associada à aplicação servidora.
Assim que o servidor aceitar a conexão, este cria um novo Socket (e
consequentemente o associa a uma nova porta) e pode ficar esperando
novas conexões no Socket original enquanto atende às requisições do cliente
pelo novo Socket. A Figura 2 ilustra as portas utilizadas na conexão e na
comunicação.

Request
Ouve

Cliente Servidor
Comunica

Figura 2 – Conexão com Sockets

O servidor sempre fica em modo de espera, aguardando pedidos de


conexão em uma porta pré-definida, como por exemplo, a porta http que é a
8080. Assim que um cliente consegue se conectar no servidor, este cria uma
nova porta específica para a comunicação com o cliente que efetuou a
conexão e a porta pré-definida é liberada para aguardar pedidos de conexão
de novos clientes.
Com relação aos protocolos na camada de transporte, podemos
utilizar o TCP e o UDP. Ambos utilizam a camada IP como camada de Rede.
Nestes protocolos há as seguintes diferenças:
 Protocolo TCP
o Protocolo orientado à conexão, ou seja, é obrigatório que
um canal de comunicação seja estabelecido antes da
troca de mensagens entre os processos.
o Para haver a transmissão dos dados, uma fase de
conexão entre as duas entidades que se comunicam
precisa ser feita. Isso torna a comunicação confiável.
o Há a Fase de Conexão, a Fase de Transmissão dos

Sistemas distribuidos – Sockets 4


Dados e a Fase de Desconexão.
o Por ter a confiabilidade, é um protocolo mais lento, já que
há mais informações que trafegam com a mensagem
para garantir o seu recebimento ou o motivo pelo qual a
mensagem não foi recebida.
o Exemplos: TELNET e Web Browser. A maioria das
aplicações distribuídas utiliza o protocolo TCP.

 Protocolo UDP
o Protocolo não orientado à conexão. Não é necessário
efetuar o pedido de conexão antes de enviar a
mensagem.
o Não há garantia de entrega dos dados (não há
mensagens de confirmação).
o Perdas durante as transmissões não são tratadas por
este protocolo.
o Utilizado em redes com alta confiabilidade, onde as taxas
de perda são baixas.
o Exemplos: Videoconferência, transmissão de áudio e
vídeo.

SAIBA MAIS

Para maiores informações sobre protocolos TCP e UDP, leiam: - TCP:


Disponível em:

<http://www.inf.ufes.br/~zegonc/material/Redes%20de%20Computadores%2
02011-2/1%20-%20Aulas/2012-1/O%20Protocolo%20TCP.pdf>.
- UDP: Disponível em: <http://www.cbpf.br/~sun/pdf/udp.pdf>.

3. Comunicação

Para facilitar a explicação de como ocorre a comunicação, a Figura 3


mostra os blocos com as funções de Sockets utilizadas para a criação,
conexão e comunicação entre processos cliente e servidor.

Sistemas distribuidos – Sockets 5


Figura 3 – Comunicação Sockets

Com relação aos blocos do servidor, eles têm as seguintes


funcionalidades:

 socket: cria um Socket e retorna um descritor, sendo que o


descritor é a referência para que as outras funções utilizem o
Socket criado.
 bind: provê o número da porta que o servidor espera contato.
Função utilizada apenas pelo servidor, uma vez que associa um
determinado endereço IP e porta TCP ou UDP para o processo
servidor.
 listen: indica ao sistema operacional para colocar o Socket em
modo de espera (passivo) para aguardar conexões de clientes.
 accept: cria um novo Socket a partir do estabelecimento de uma
conexão para iniciar a comunicação (leitura e escrita),
associando-o a uma nova porta. Quando o cliente realizar a
conexão, esta função será responsável por aceitar e
estabelecer a conexão.
 read/receive: lê o conteúdo do buffer associado ao Socket, já
que quando a mensagem é recebida, ela é armazenada em um
buffer. Conforme mostra a figura 3, o receive tem que “estar
casado” com o send do outro processo.
 write/send: escreve os dados em um buffer associado ao socket

Sistemas distribuidos – Sockets 6


para que este buffer seja enviado. Esta função também precisa
“estar casada” com o receive do outro processo.
 close: informa ao sistema operacional para terminar o uso de
um Socket.

Com relação aos blocos do cliente, eles têm as seguintes


funcionalidades:

 socket: cria um Socket e retorna um descritor, sendo que o


descritor é a referência para que as outras funções utilizem o
Socket criado.
 accept: função que o cliente utiliza para se conectar ao Socket
de um servidor. Assim que o cliente faz o pedido de conexão, o
Socket do servidor precisa estar bloqueado na função accept
para que este pedido seja aceito.
 write/send: funcionalidade similar ao bloco write/send do
servidor.
 read/receive: funcionalidade similar ao bloco read/receive do
servidor.
 close: informa ao sistema operacional para terminar o uso de
um Socket.

4. Sockets em Java
Para criarmos uma aplicação em Java (DEITEL, 2010), que utiliza
Sockets, há pacotes que podemos utilizar. Os pacotes dependem do
protocolo a ser utilizado. Caso seja TCP, utilizamos as classes Socket e
ServerSocket que são do tipo StreamSocket. Se for UDP, utilizamos a classe
DatagramSocket.
O StreamSocket é orientado a conexão (o servidor precisa aceitar o
pedido de conexão do cliente) e o DatagramSocket é não orientado a
conexão.
Neste tópico, nós vamos criar um exemplo utilizando o protocolo TCP.
O StreamSocket utiliza as classes que estão nos seguintes pacotes:

 java.net.Socket : utilizado em cada lado do canal de


comunicação bidirecional. O cliente e o servidor precisam se um
objeto desta classe, já que será através dele que as mensagens
serão enviadas e recebidas.
 java.net.ServerSocket : responsável por ficar aguardando
pedidos de conexão dos clientes. Somente o servidor precisa

Sistemas distribuidos – Sockets 7


utilizar um objeto desta classe. As funções que associam uma
porta ao socket o colocam em modo de espera e aceitam os
pedidos de conexão são utilizadas no objeto ServerSocket.

SAIBA MAIS

Para maiores informações sobre DatagramSocket, leiam:


Disponível em:
<http://pages.cs.wisc.edu/~hasti/cs368/JavaTutorial/jdk1.2/api/java/net/Datagr
amSocket.html>.
Disponível em: <http://javafree.uol.com.br/artigo/2910/Programando-em-
sockets-com-java-parte-02.html>.

Na comunicação, os Sockets enviam e recebem dados na forma de


bytes. Cada conjunto de bytes é chamado stream. Para enviar os dados,
utilizamos o método getOutputStream e para recebê-los, utilizamos o método
getInputStream.
A Figura 4 mostra os passos necessários para enviar uma mensagem,
utilizando Sockets em Java. Nesta figura, estamos enviando uma mensagem
em que o conteúdo é uma String. Antes do envio, os dados são convertidos
em bytes, pois toda mensagem trafega na forma de bytes.

// Envio de uma mensagem

1 String txt; // string da mensagem a ser enviada


2 OutputStream out; // objeto para escrita no socket
3
4 try {
5 out = socket.getOutputStream(); // habilita a escrita
6 out.write(txt.getBytes()); // escreve (envia) mensagem
7 }
8 catch (Exception e)
9 {
10 e.printStackTrace();
11 }

Figura 4 – Envio de uma Mensagem

Nesta figura reparamos que um objeto da classe OutputStream foi

Sistemas distribuidos – Sockets 8


utilizado para o envio da mensagem. Sempre que quisermos enviar uma
mensagem em que o conteúdo é uma informação de tipos primitivos ou de
String, utilizamos a classe OutputStream. Caso queiramos enviar um objeto
de uma classe específica, devemos utilizar a classe ObjectOutputStream.
Para o envio da mensagem, precisamos habilitar a escrita no objeto da
classe OutputStream. Isso é feito na linha 5, no qual Socket é um objeto da
classe Socket e este objeto é utilizado para a comunicação entre o cliente e o
servidor.
A linha 6 realiza o envio da mensagem. Pra isso, a String txt é
convertida em bytes e o seu envio é realizado. Caso aconteça algum erro, o
bloco catch captura o erro e imprimimos o que aconteceu.
A Figura 5 mostra os passos necessários para receber uma
mensagem, utilizando Sockets em Java. Nesta figura, estamos recebendo
uma mensagem em um vetor de bytes e depois a convertendo para uma
String.

// Recebimento de uma mensagem

1 InputStream in; // objeto do tipo InputStream


2 byte btxt[]; // array de bytes
3 int bt // número de bytes lidos
4 String saida; // string que armazena o conteúdo da mensagem
5 try
6{
7 in = socket.getInputStream(); // habilito a leitura
8 bt = in.read(btxt); // leio a cadeia de bytes (recebe a mensagem)
9 saida = new String(btxt);
10 }
11 catch(Exception e)
12 {
13 e.printStackTrace();
14 }
Figura 5 – Recebimento de uma Mensagem

Nesta figura reparamos que um objeto da classe InputStream foi


utilizado para o recebimento da mensagem. Sempre que quisermos receber
uma mensagem em que o conteúdo é uma informação de tipos primitivos ou
de String, utilizamos a classe InputStream. Caso queiramos receber um
objeto de uma classe específica, devemos utilizar a classe

Sistemas distribuidos – Sockets 9


ObjectInputStream.
Para o recebimento da mensagem, precisamos habilitar a leitura no
objeto da classe InputStream. Isso é feito na linha 7, no qual Socket é um
objeto da classe Socket e este objeto é utilizado para a comunicação entre o
cliente e o servidor.
A linha 8 realiza o recebimento da mensagem. Pra isso, ele armazena
a mensagem recebida no vetor de bytes btxt. A variável bt armazena a
quantidade de bytes lidos. Na linha 9 o vetor de bytes é convertido em uma
String e o seu conteúdo é armazenado na variável saída. Caso aconteça
algum erro, o bloco catch captura o erro e imprimimos o que aconteceu.
Pronto, agora que já vimos como a comunicação pode ser realizada
entre dois processos utilizando Sockets em Java, vamos apresentar um
código de uma aplicação cliente e servidor, na qual o cliente envia 10
mensagens para o servidor e, a cada mensagem, ele recebe uma resposta
do servidor e a imprime.
Criamos uma classe conexão, sendo que ela possui dois métodos: um
para o envio de mensagens e outro para o recebimento. A Figura 6 mostra o
código desta classe.

Sistemas distribuidos – Sockets 10


// Classe Conexao

1 import java.net.*;
2 import java.io.*;
3 public class Conexao {
4
5 public static void send(Socket socket,String txt)
6 {
7 OutputStream out;
8 try {
9 out = socket.getOutputStream();
10 out.write(txt.getBytes());
11 }catch (Exception e){
12 System.out.println(”Exceção no OutputStream");
13 }
14 }
15
16 public static String receive(Socket socket)
17 {
18 InputStream in;
19 int bt;
20 byte btxt[] = new byte[79];
21 String txt="";
22 try{
23 in = socket.getInputStream();
24 bt = in.read(btxt);
25 if (bt > 0) txt = new String(btxt);
26 } catch(Exception e){
27 System.out.println("Excecao no InputStream: "+e);
28 }return txt;
31 }
32 }

Figura 6 – Classe Conexão

Sistemas distribuidos – Sockets 11


Na classe, o código dos dois métodos já foi explicado nas figuras
anteriores. No método do envio (send), passamos como argumento o objeto
da classe Socket utilizado para a comunicação e a string a ser enviada. O
método não retorna nenhum valor.
No método de recebimento (receive), passamos como argumento o
objeto da classe Socket e o método retorna uma com o conteúdo da
mensagem recebida.
No servidor, além da classe Conexão, há mais uma classe. A Figura 7
mostra a classe Servidor, que é a classe que contém a lógica da aplicação do
Servidor.

// Classe Servidor
1 public class Servidor
2 {
3 static ServerSocket serversocket;
4 static Socket client_socket;
5 static Conexao c;
6 static String msg;
7 public Servidor() {
8 try {
9 serversocket = new ServerSocket(9600);
10 }catch (Exception e) {
11 System.out.println("Nao criei o server socket..."); }
12 }
13
14 public static void main(String args[]) {
15 String texto;
16 new Servidor();
17 c = new Conexao();
18 try {
19 client_socket = serversocket.accept(); // fase de
conexão
20 for(int i=0; i<10; i++){
21 texto = c.receive(client_socket); // fase de
dados
22 System.out.println(texto);
23 c.send(client_socket,”Servidor Envia: Olá
Cliente");
24 }
25 7 – Classe Servidor client_socket.close(); serversocket.close();}
Figura //
desconexao
26 }catch (Exception e){
A aplicação do Servidor é composta pelas classes: Servidor e
27 e.printStackTrace();
28 }
Sistemas29distribuidos} } – Sockets 12
Conexão. Assim que a aplicação é executada, a classe Servidor chama o
construtor da sua classe e, nela vemos que a linha 9 realiza a criação de um
objeto do tipo ServerSocket. Ele é o responsável por criar o Socket do lado
do servidor que será responsável por aceitar os pedidos de conexão do
cliente. Na execução da linha 9, nós também definimos a porta que o Socket
do servidor ficará escutando os pedidos de conexão. No caso, definimos que
será a porá 9600.
Após isso, o Socket do servidor (serversocket) ficará em estado de
espera na linha 19, bloqueado no método accept. Ele sairá desta linha
somente quando o cliente fizer o pedido de conexão. Após ele executar o
método accept, retornamos um objeto da classe Socket (cliente_socket). Este
objeto retornado será o responsável pela comunicação com o cliente,
devendo ser utilizado para o envio e recebimento de mensagens.
Na fase de comunicação, o servidor fica em um loop de 10 iterações,
sendo que a cada iteração, o servidor recebe uma mensagem, imprime o
conteúdo da mensagem recebida e envia uma mensagem ao cliente com o
conteúdo “Servidor envia: Olá Cliente!”. Ao final das 10 iterações, o servidor
fecha a conexão do Socket e finaliza a sua execução.
Para o envio e o recebimento das mensagens, utilizamos os métodos
da classe Conexão.
Agora que já explicamos o funcionamento do Servidor, chegou o
momento de explicarmos o funcionamento do Cliente. A Figura 8 mostra a
classe Cliente que contém a lógica da aplicação cliente.
A aplicação do Cliente é composta pelas classes: Cliente e Conexão.
Assim que a aplicação é executada, a classe Cliente chama o construtor da
sua classe e, nela vemos que a linha 7 realiza a criação de um objeto do tipo
Socket. Ele é o responsável por criar o Socket do lado do cliente que será o
responsável por se conectar com o servidor e para realizar a troca de
mensagens. Na execução da linha 7, além da criação do objeto, nós também
realizamos a conexão com o servidor. Para isso, passamos como argumento
o ip e a porta que o servidor está escutando pedidos de conexão. No caso o
ip é 200.18.98.106 e a porta é a 9600.
Assim que esta linha é executada, o servidor aceita o pedido de
conexão, caso esteja bloqueado no método accept, e a troca de mensagens
entre o cliente e o servidor esteja liberada, com o canal de comunicação
estabelecido.

Sistemas distribuidos – Sockets 13


// Classe Cliente
1 public class Cliente {
2 static Conexao c;
3 static Socket socket;
4 int i;
5 public Cliente(){
6 try {
7 socket = new Socket("200.18.98.106",9600); } //
fase de conexão
8 catch (Exception e) {
9 System.out.println("Nao consegui resolver o host...");}
10 }
11
12 public static void main(String args[]){
13 String msg = "Cliente envia : Olá Servidor";
14 String texto;
15 new Cliente();
16 for(i=0;i<10;i++){
17 c.send(socket,msg); // fase de dados
18 texto = c.receive(socket);
19 System.out.println(texto);
20 }
21 try{ socket.close(); }catch(Exception e){
e.printStackTrace();} // fase de desconexão
22 }
Figura 7 – Classe Cliente
23 }

Na fase de comunicação, o cliente fica em um loop de 10 iterações,


sendo que a cada iteração, o cliente envia uma mensagem com o conteúdo
“Cliente envia: Olá Servidor”, recebe uma mensagem do servidor e imprime o
conteúdo da mensagem recebida. Ao final das 10 iterações, o servidor fecha
a conexão do Socket e finaliza a sua execução. Para o envio e o recebimento
das mensagens, utilizamos os métodos da classe Conexão.
Pronto, já temos um exemplo de Sockets em Java!!!

Sistemas distribuidos – Sockets 14


LEITURA COMPLEMENTAR
Leitura complementar de Sockets
Disponível em: <http://www.caelum.com.br/apostila-java-orientacao-
objetos/apendice-sockets/>.
Disponível em: <http://www2.unoeste.br/~chico/comunicacao_socket/>.

5. Referências

ALVES, Maicon M. Sockets Linux. São Paulo: Brasport, 2008.

DEITEL, Paul; DEITEL, Harvey. Java, Como Programar. 8ª ed. São Paulo:
Pearson Prentice Hall Brasil, 2010.

KUROSE, J. F.; ROSS, K. W. Redes de Computadores e a Internet. 3. ed.


São Paulo: Pearson, 2006.

Sistemas distribuidos – Sockets 15

Você também pode gostar