SOCKETS O modelo mais utilizado para aplicações distribuídas em

Propaganda
SOCKETS
O modelo mais utilizado para aplicações distribuídas em redes de
computadores é chamado cliente/servidor, no qual, a comunicação costuma se
dar através de uma mensagem de solicitação do cliente enviada para o
Servidor, pedindo para que alguma tarefa seja executada. Em seguida, o
servidor executa a tarefa e envia a resposta. O mecanismo mais utilizado
atualmente para esse modelo que possibilita a comunicação entre aplicações é
chamado de Socket.
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. (DE CAMARGO).
Através de um socket é possível realizar diversas operações, como
exemplo:

Estabelecer conexões entre máquinas.

Enviar e receber dados.

Encerrar conexões.

Esperar por conexões em determinada porta.
Socket é um endpoint de uma comunicação de mão dupla unidos entre
dois programas executados na rede. Uma Socket é ligado a um número da
porta de forma que a camada de TCP pode identificar a aplicação que vai
destinar os dados para serem enviados. (ALL ABOUT SOCKETS).
Um endpoint é uma combinação de um endereço de IP e um número da
porta. Toda conexão de TCP pode ser identificada exclusivamente por seu dois
endpoints. Neste modo pode haverá conexões múltiplas entre seu host e o
servidor. (ALL ABOUT SOCKETS).
A tecnologia socket é utilizada no desenvolvimento de aplicações do
modelo cliente/servidor, para realizar a comunicação em rede. Cada socket tem
um número que consiste no IP do host mais um numero de 16 bits local para
este host, denominado porta. Para que a comunicação funcione é necessário
que uma conexão seja estabelecida entre um socket da máquina transmissora
e um socket da máquina receptora.
Normalmente, um servidor é executado em um computador específico e
tem um socket que é ligada a um número da porta específico. O servidor fica
espera, escutando o socket do cliente para fazer uma solicitação de conexão.
A
figura
4
mostra
basicamente
uma
requisição
de
conexão
cliente/servidor.
Figura 4: Requisição de conexão cliente/servidor.
O cliente sabe qual a máquina servidora na qual o servidor está
executando e o número da porta no qual o servidor está aguardando. Para
solicitar uma conexão, o cliente tenta se comunicar com o servidor da máquina
servidora e a porta. O cliente também precisa se identificar ao servidor para
que a conexão com a porta local seja efetuada. Isto normalmente é
determinado pelo sistema.
Se tudo ocorrer certo, o servidor aceita a conexão, e ao aceitar, o
servidor gera um novo socket à mesma porta local e também com seu endpoint
remoto definido como o endereço e porta do cliente. A figura 5 mostra
basicamente uma conexão entre cliente/servidor.
Figura 5: Conexão entre cliente/servidor.
No lado do cliente, se a conexão for aceita, um socket é criado
prosperamente e o cliente pode usar o socket para comunicar com o servidor.
Modos de utilização de Sockets
Os seguintes modos de utilização de sockets que Java oferece é o modo
orientado a conexão, que funciona sobre o protocolo TCP (Transmission
Control Protocol, ou protocolo de controle de transmissão), e o modo orientado
a datagrama, que funciona sobre o protocolo UDP (User Datagram Protocol, ou
protocolo de datagrama de usuários). Os dois modos funcionam sobre o
protocolo IP (Internet Protocol).
Modo orientado a conexão TCP/IP
Para que os computadores de uma rede possam trocar informações
entre si é necessário que todos os computadores adotem as mesmas regras
para o envio e o recebimento de informações. Este conjunto de regras é
conhecido como Protocolo de comunicação. Falando de outra maneira
podemos afirmar: “Para que os computadores de uma rede possam trocar
informações entre si é necessário que todos estejam utilizando o mesmo
protocolo de comunicação”. No protocolo de comunicação estão definidas
todas as regras necessárias para que o computador de destino, “entenda” as
informações no formato que foram enviadas pelo computador de origem. Dois
computadores com diferentes protocolos instalados, não serão capazes de
estabelecer uma comunicação e nem serão capazes de trocar informações.
(BATTISTI, 2003).
O processo de comunicação no modo orientado à conexão ocorre
quando o servidor escolhe uma determinada porta e fica aguardando conexões
nesta porta. O cliente deve saber previamente qual a máquina servidora (host)
e a porta que o servidor está aguardando conexões. Então o cliente solicita
uma conexão em um host/porta, de acordo com a figura 6.
Figura 6: Requisição de conexão.
Se tudo ocorrer conforme o esperado, ou seja, se não existir nenhum
problema, o servidor aceita a conexão gerando um socket em uma porta
qualquer do lado servidor, criando assim um canal de comunicação entre o
cliente e o servidor, como demonstrado na figura 7.
Figura 7: Comunicação entre Servidor e Cliente.
Tipicamente o comportamento do servidor é ficar em um loop
aguardando novas conexões e gerando sockets para atender as solicitações de
clientes.
Características do TCP

Orientado à conexão: A aplicação envia um pedido de conexão
para o destino e usa a "conexão" para transferir dados.

Ponto a ponto: uma conexão TCP é estabelecida entre dois pontos.

Confiabilidade: Existem várias técnicas que o TCP usa para
proporcionar uma entrega confiável dos pacotes de dados, que é a
grande vantagem que tem em relação ao UDP, no qual, é o motivo
do seu uso extensivo nas redes de computadores. O TCP permite a
recuperação de pacotes perdidos, a recuperação de dados
corrompidos, a eliminação de pacotes duplicados, e pode recuperar
a ligação em caso de problemas no sistema e na rede.
(TRANSMISSION CONTROL PROTOCOL).

Full duplex: É possível a transferência simultânea em ambas
direções (cliente-servidor) durante toda a sessão.

Handshake: Mecanismo de estabelecimento e finalização de
conexão a três e quatro tempos, respectivamente, o que permite a
autenticação e encerramento de uma sessão completa. O TCP
garante que, no final da conexão, todos os pacotes foram bem
recebidos. (TRANSMISSION CONTROL PROTOCOL).

Entrega ordenada: A aplicação faz a entrega ao TCP de blocos de
dados com um tamanho arbitrário num fluxo (ou stream) de dados,
caracteristicamente em byte. O TCP parte estes dados em
segmentos de tamanho especificado pelo valor MTU, que se refere
ao tamanho do maior datagrama que uma camada de um protocolo
de comunicação pode transmitir, no qual, o datagrama é a estrutura
de dados unitária de transmissão em uma rede. Porém, a circulação
dos pacotes ao longo da rede (utilizando um protocolo de
encaminhamento, na camada inferior, como o IP) pode fazer com
que os pacotes não cheguem ordenados. O TCP garante a
reconstrução do stream no destinatário mediante os números de
seqüência. (TRANSMISSION CONTROL PROTOCOL).

Controle de fluxo: O TCP usa o campo janela ou window para
controlar o fluxo. O receptor, à medida que recebe os dados, envia
mensagens ACK (=Acknowledgement), confirmando a recepção de
um segmento; como funcionalidade extra, estas mensagens podem
especificar o tamanho máximo do buffer no campo (janela) do
segmento TCP, determinando a quantidade máxima de bytes aceite
pelo receptor. O transmissor pode transmitir segmentos com um
número de bytes que deverá estar confinado ao tamanho da janela
permitido: o menor valor entre sua capacidade de envio e a
capacidade informada pelo receptor. (TRANSMISSION CONTROL
PROTOCOL).
Modo orientado a datagrama UDP/IP
O UDP é um acrónimo do termo inglês User Datagram Protocol que
significa protocolo de datagramas de utilizador (ou usuário) é um protocolo de
transporte que presta um serviço de comunicação não orientado a conexão e
sem garantia de entrega. As aplicações que usam o UDP devem tomar medias
para garantir a recuperação dos dados perdidos, a organização dos dados
recebidos fora de ordem, a eliminação dos dados recebidos em duplicidade e o
controle de fluxo. (ALBUQUERQUE, 2001, p.11).
Sockets UDP/IP são muito mais rápidos e mais simples que sockets
TCP/IP, porém menos confiáveis. Em UDP não existe o estabelecimento de
conexão, sendo que a comunicação ocorre apenas com o envio de
mensagens.
Uma mensagem é um datagrama, pelo qual é composto de um
remetente (sender), um destinatário ou receptor (receiver), e a mensagem
(content). Em UDP, caso o destinatário não esteja aguardando uma
mensagem, ela é perdida.
A figura 8 apresenta o envio de um datagrama de uma suposta
Máquina1 para outra Máquina2 em uma rede.
Figura 8: Envio de um datagrama.
Característica da UDP

Simplicidade: O UDP é mais simples que o TCP, ou seja, ele não
se preocupa com a conexão e a chegada correta dos dados no
destino.

Mensagens desordenada: Não ordena as mensagens, ou seja,
elas vão sendo agrupadas conforme vão chegando.

Otimização na transmissão e recepção dos dados: O UDP, por
ser um protocolo pequeno, proporcionar um ganho de velocidade na
transmissão e recepção de dados.

Inconfiabilidade: O protocolo UDP não garante se os dados
chegaram ao destino correspondente.
Exemplo de duas aplicações em Sockets
Neste trabalho serão mostrado dois exemplos de uma aplicação em
Sockets utilizando a linguagem Java, uma no modo orientado a conexão
TCP/IP e outra no modo orientado a Datagrama UDP/IP no qual os dois
funcionam sobre o protocolo IP.
Exemplo de uma aplicação em Socket no modo TCP/IP
No exemplo a seguir serão apresentadas as ações necessárias para
implementar comunicação sobre TCP através de um socket cliente e um socket
servidor, ou seja, será implementado uma classe Servidor e uma classe
Cliente.
Para implementar a classe Servidor, o primeiro objetivo é criar o server
socket na porta 7000, depois, deverá ser utilizado 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 solicitado, faz com que a
Thread atual seja "paralisada" até que uma conexão seja recebida.
É comum, em ambientes reais, lançar uma Thread a cada conexão recebida
pelo ServerSocket, pois isto é feito para que possa tratar vários clientes
conectados simultaneamente. (DE CAMARGO).
Um leitor dos dados de entrada baseado no canal de entrada de dados
do socket deverá ser criado que ira trabalhar com o BufferedReader que lê
texto de uma text-input stream e os deixa em buffer para leitura eficiente das
informações e o InputStreamReader lê os bytes que estão chegando e os
transforma em caracteres para que o BufferedReader possa entender.
O método readline() da classe BufferedReader deverá ser chamado,
pois 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 um comando popular System.out.println().
Depois é só fechar os sockets que precisará ser feito dentro de um bloco
do tipo finally, pois isto garante que os recursos serão liberados.
Então, a aplicação devera ser feito da seguinte maneira, como mostra a
listagem 1, no qual representa a classe Servidor.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
public class Servidor
{
public static void main(String[ ] args) {
// Declara o ServerSocket
ServerSocket serv=null;
// Declara o Socket de comunicação
Socket s= null;
// Declara o leitor para a entrada de dados
BufferedReader entrada=null;
try{
// Cria o ServerSocket na porta 7000 se estiver disponível
serv = new ServerSocket(7000);
// Aguarda uma conexão na porta especificada e cria retorna o
// socket que irá comunicar com o cliente
s = serv.accept();
// Cria um BufferedReader para o canal da stream de entrada de
// dados do sockets
entrada = new BufferedReader(new InputStreamReader
(s.getInputStream()));
// Aguarda por algum dado e imprime a linha recebida quando
recebe
System.out.println(entrada.readLine());
}
// trata possíveis excessões de input/output. Note que as
// excessões são as mesmas utilizadas para as classes de java.io
catch(IOException e){
// Imprime uma notificação na saída padrão caso haja algo
// errado.
System.out.println(“Algum problema ocorreu para criar ou receber
o socket.”);
}
finally{
try{
// Encerro o socket de comunicação
s.close();
// Encerro o ServerSocket
serv.close();
}
catch(IOException e){ }
}
}
}
Listagem 1: Classe Servidor utilizando Sockets TCP/IP.
Agora a classe Cliente será implementada, no qual, será criado primeiro
o socket, de acordo com o construtor que foi utilizado, para a comunicação com
o IP 127.0.0.1 (IP LoopBack) na porta 7000 (que a porta que do servidor irá
“escutar”.
IP LoopBack é um termo usado para representar uma forma de
comunicação com o próprio computador (geralmente utilizado para efetuar
testes), que para acessá-lo, é utilizado 127.0.0.1 ou simplesmente localhost.
(DE CAMARGO).
Um objeto do tipo PrintStream será criado para poder imprimir dados
para o canal de saída do socket.
O método println() da classe PrintStream será utilizado para imprimir
uma String que será enviada através do socket para o Servidor, no qual, esse
método converte os caracteres digitados para o formato adequado de envio
através do socket. Depois é só fechar o socket dentro de um bloco finally.
Para que possa entender melhor a classe Cliente, a listagem 2, descreve
como deve ser realizado.
import java.io.IOException;
import java.io.PrintStream;
import java.net.Socket;
public class Cliente {
public static void main(String[] args) {
// Declara o socket cliente
Socket s = null;
// Declara a Stream de saida de dados
PrintStream ps = null;
try{
// Cria o socket com o recurso desejado na porta especificada
s = new Socket("127.0.0.1",7000);
// Cria a Stream de saida de dados
ps = new PrintStream(s.getOutputStream());
// Imprime uma linha para a stream de saída de dados
ps.println("Olá Servidor...!");
// Trata possíveis exceções
}
catch(IOException e){
System.out.println("Algum
problema
dados pelo socket.");
}
finally{
try{
// Encerra o socket cliente
ocorreu
ao
criar
ou
enviar
s.close();
}
catch(IOException e){}
}
}
}
Listagem 2: Classe Cliente utilizando Sockets TCP/IP.
Agora para que possa entender melhor o aplicativo, deverá seguir os
seguintes passos:
1) Compile as classes Servidor e Cliente:
Javac Servidor.java
Javac Cliente.java
2) Execute a classe Servidor.
start java Servidor.
3) Execute a classe Cliente.
java Cliente.
O resultado será “Olá Servidor...!” na janela de prompt onde foi
executado a classe Servidor.
Exemplo de uma aplicação em Socket no modo UDP/IP
No próximo exemplo a seguir serão apresentadas as ações necessárias
para implementar a comunicação sobre UDP através de um socket cliente e um
socket servidor, como no exemplo anterior, ou seja, será implementado uma
classe Servidor e uma classe Cliente.
Para implementar a classe Servidor, o primeiro objetivo é criar o server
socket na porta 7000, depois, cria o datagrama para receber uma mensagem
passando um buffer e o tamanho do buffer para receber a mensagem, caso o
conteúdo da mensagem recebida for maior que o buffer a mensagem será
perdido.
Depois deverá ser utiliza o comando serverSocket.receive() que terá a
responsabilidade de fica aguardando o recebimento de uma mensagem e
então é só fechar o servidor.
Então, a aplicação devera ser feito da seguinte maneira, como mostra a
listagem 3, no qual representa a classe Servidor.
import java.io.*;
import java.net.*;
public class Servidor{
public static void main(String args[])
{
try{
// Passo 1 - Crindo um socket servidor:
// Escutando na porta 7000.
DatagramSocket
serverSocket
=
new
DatagramSocket(7000);
// Passo 2 - Recebendo um datagrama:
DatagramPacket
content
=
new
DatagramPacket(new
byte[512], 512);
// Aguarda até o recebimento de uma mensagem.
serverSocket.receive(content);
String inContent = new String(content.getData());
System.out.println(inContent);
// Passo 3 - Fechando o servidor:
serverSocket.close();
}catch(IOException e){
System.out.println("Erro: "+ e);
}
}
}
Listagem 3: Classe Servidor utilizando Sockets UDP/IP.
Agora a classe Cliente será implementada, no qual um socket deverá ser
criado sem utilizar uma porta em especial, depois cria e envia o datagrama ao
destinatário na porta 7000 e logo em seguida fecha o socket, como mostra a
listagem 4 que representa a classe Cliente.
import java.io.*;
import java.net.*;
public class Cliente{
public static void main(String args[])
{
try{
// Passo 1 - Criar o socket:
// Não precisa de uma porta em especial.
DatagramSocket clientSocket = new DatagramSocket();
// Passo2 - Criando e enviando o datagrama:
InetAddress addr = InetAddress.getByName("127.0.0.1");
String content = "Ola Servidor...!";
byte[] buffer = content.getBytes();
// Enviar datagram para destinatário na porta 7000.
DatagramPacket
question
=
new
DatagramPacket(buffer,
buffer.length, addr, 7000);
// Envia o datagrama.
clientSocket.send(question);
// Passo 3 - Fechando o socket:
clientSocket.close();
}catch(IOException e){
System.out.println("Erro: "+ e);
}
}
}
Listagem 4: Classe Cliente utilizando Sockets UDP/IP.
Os passos para executar a aplicação ocorrerá da mesmas maneira do
modo orientado a conexão TCP/IP, como mostrado logo abaixo:
1) Compile as classes Servidor e Cliente:
Javac Servidor.java
Javac Cliente.java
2) Execute a classe Servidor.
start java Servidor.
3) Execute a classe Cliente.
java Cliente.
O resultado será “Olá Servidor...!”.
Download