Trabalhando com redes no Java com o pacote java.net

Neste artigo, traduzido do siteJava Examples, será explanado os fundamentos da forma como Java trabalha com redes (local e Internet). Este artigo serve como continuação do artigo 5 maneiras de armazenar dados em aplicações Android.

INTRODUÇÃO:

O  ambiente de execução do Java é projetado para que as aplicacões possam ser facilmente escritas para se comunicar eficientemente e compartilhar dados com sistemas remotos. Muitas dessas funcionalidades são fornecidas pela API padrão do Java no pacote java.net.

Protocolos TCP/IP:

Três protocolos são normalmente bastante usados dentre o esquema TCP/IP e uma investigação detalhada de suas propriedades é garantida. Entender como esses três protocolor (IP, TCP e UDP) interagem é critico para o desenvolvimento de aplicações que trabalhem em rede.

Internet Protocol (IP):

O IP é a chave mestra da suite TCP/IP. Todos os dados da Internet são encapsulados em pacotes IP, a unidade básica de transmissões IP. O IP é considerado um protocolo independente de conexão e não confiável. Por ser independente de conexão, ele não faz uso de informações de controle antes de transmitir dados para um sistema remoto – pacotes são meramente enviados ao destino na expectativa de que eles sejam tratados adequadamente. E é não confiável porque não retransmite pacotes perdidos ou detecta dados corrompidos. Essas tarefas precisam ser implementadas por protocolos de alto nível, como o TCP.

O IP define um esquema de endereçamento universal chamado endereço IP. Um endereço IP é um número de 32 bits e cada endereço padrão é único na Internet. Dado um pacote IP, a informação pode ser roteada para o destino baseado apenas no endereço IP definido no cabeçalho do pacote. O endereço IP é geralmente escrito como 4 números, entre 0 e 255, separados por pontos (por exemplo: 124.148.157.6).

Enquanto um número de 32 bits é uma maneira apropriada para endereçar sistema para computadores, humanos tem dificuldade de lembrar esses números. Por causa disso, um sistema chamado Domain Name System (DNS) foi desenvolvido para mapear os endereços IP para identificadores mais intuitivos e vice-versa. Você pode usar www.netscape.org no lugar de 128.148.157.6.

É importante observar que esses nomes de domínios não são usados nem entendidos pelo IP. Quando uma aplicação quer transmitir dados para outra máquina da Internet, precisa primeiro traduzir o nome do domínio para um endereço IP usando o DNS. Uma aplicação que recebe dados pode executar a tradução inversa, usando o DNS para retornar o nome do domínio a partir de um endereço IP. Não existe uma correspondência direta entre endereços IP e nomes de domínio: um nome pode ser mapeado para múltiplos IP, e múltiplos endereços IP podem ser mapeados para um único nome de domínio

O Java fornece uma classe para trabalhar com endereços IP, InetAddress.

A CLASSE INETADDRESS

Essa classe representa uma endereço IP. As aplicações devem usar os metódos getLocalHost, getByName ou getAllByName para criar uma nova instância da classe.

Transmission Control Protocol (TCP)

Muitas aplicações da Internet usam TCP para implementar a camada de transporte. O TCP nos fornece um protocolo confiável, orientado a conexão e de fluxo continuo. As implicações dessas características são:

  • Confiabilidade: Quando segmentos TCP, a menor unidade de transmissões TCP, são perdidas ou corrompidas, a implementação TCP detectará e retransmitirá os segmentos necessários.
  • Orientação a conexão: O TCP configura uma conexão com um sistema remoto pela transmissão de informações de controle, frequentemente conhecida como “handshake”, antes do inicio da comunicação. No final, uma mensagem similar finaliza a transmissão.
  • Fluxo continuo: O TCP fornece um meio de comunicação que permite que um conjunto arbitrário de bytes sejam enviados e recebidos suavemente; uma vez que a conexão seja estabelecida, o segmentos TCP fornece à camada de aplicação o aspecto de fluxo continuo de dados.

Por causa dessas características, é fácil ver porque o TCP é usado por muitas aplicações da Internet. O TCP torna fácil criar aplicações baseadas em Internet, liberando o desenvolvedor de preocupações de como os dados são quebrados ou rotinas de correção de erro. Porém, o TCP requer uma quantidade significativa de dados extras e talvez você possa querer codificar rotinas que sejam mais eficientes no fornecimento de transmissões confiáveis. Além disso, retransmissáo de dados perdiso pode ser inapropriado para sua aplicação, já que essa informação pode ter se tornado inútel por ter perdido a validade.

Um importante esquema de endereçamento definido pelo TCP são as portas. As portas separam vários fluxos de comunicação do TCP que estejam rodando simultenamente no mesmo sistema. Para aplicações de servidor, que esperam por clientes TCP para iniciar po contato, uma porta específica pode ser estabelecida de onde a comunicação será originada. Esses conceitos estão reunidos em uma abstração de programação chamada “socket”.

User Datagram Protocol (UDP) :

O UDP é uma alternativa mais leve ao TCP para comunicação host-a-host. Em contrate ao TCP, o UDP tem as seguintes características:

  • Não confiável: O UDP não tem nenhuma mecanismo para detectar erros nem retransmitir dados perdidos ou corrompidos.
  • Independente de conexão: o UDP não negocia uma conexão antes de transmitir dados. A informação é  enviada com a presunção de que o cliente esteja escutando.
  • Orientada a mensagem: O UDP permite que as aplicações enviem mensagens junto com os datagramas UDP, a unidade de transmissão do UDP.  A aplicação precisa empacotar toda a informação dentro de datagramas individuais.

Para algumas aplicações, o USP é mais apropriado que o TCP. Por exemplo, com o NTP – Protocolo de Tempo da Rede, dados perdidos indicam uma hora que será inválida quando for retransmitido. Em um ambiente LAN, o NFS – Sistema de arquivos de rede pode ser mais eficiente em fornecer confiabilidade na camada de aplicação e por isso usa UDP.

Como o TCP, o UDP fornece o mesmo esquema de endereçamento de portas, permitindo que várias aplicações enviem e recebam datagramas ao mesmo tempo. As portas UDP são diferentes das portas TCP. Por exemplo, uma aplicação pode responder a porta UDP número 512 enquanto outra pode responder na porta TCP 512.

Uniform Resource Locator (URL):

Enquanto endereços IP identificam sistema de maneira única na Internet, e portas identificam serviços TCP ou UDP em um sistema, URLs fornecem um esquema de identificação universal no nível de aplicação. Qualquer um que já usou um navegador Web já está familiar com URLs, mesmo que a sintaxe completa não seja evidente de imediato. As URLs foram desenvolvidas para criar um formato comum de identificar recursos na Web, mas foram desenhadas para serem suficientemente gerais para acomodar aplicações Web por decadas. De modo similar, a sintaxe da URL é flexivel o bastante para acomodar protocolos futuros.

Sintaxe da URL:

A classificação primária das URLs é o esquema, que usualmente corresponde a um protocolo de aplicação. Os esquemas incluem http, ftp, telnet e gopher. A sintaxe restante da URL está num formato que depende do esquema. Essas duas partes são separadas por dois pontos, que nos dá a seguinte sintaxe:

scheme-name:scheme-info

Assim, enquanto mailto:dwb@netscape.org indica “envie um e-mail para o usuário dwb da máquina netscape.org”, ftp://dwb@netscape.org/ significa “abra uma conexão FTP com a máquina netscape.org e loge-se com o usuário dwb”.

Formato geral da URL:

Muitas URLs seguem um formato geral que segue o padrão abaixo:

scheme-name://host:port/file-info#internal-reference

O Scheme-name pe o esquema da URL como HTTP, FTP ou Gopher. O Host é o nome do domínio ou endereço IP do sistema remoto. A porta é o número da porta no qual o serviço está escutando; já que muitas aplicações definem uma porta padrão, a menos que uma porta não padrão esteja sendo usada, a porta e os dois pontos que a delimitam do nome do host pode ser omitida. File-info é o recurso requisitado pelo sistema remoto, que frequentemente é um arquivo. Porém, a parte do arquivo pode as vezes executar um programa do servidor e normalmente inclui um caminho para um arquivo especifico do sistema. O campo internal-reference é normalmente o identificador de um nome de âncora dentro de uma página HTML. Um nome de ãncora permite fazer uma ligação como um ponto específico da página HTML. Normalmente não é usado, e pode ser omitido junto com o #.

Java e URLs:

O Java fornece um mecanismo bastante poderoso e elegante para criar aplicações cliente de rede permitindo que você use poucas sentenças para obter recursos da Internet. O pacote java.net contém os fontes desses recursos, as classes URL e URLConnection.

A CLASSE URL:

A classe URL representa uma “Uniform Resource Locatior”, um ponteiro para um recursos da Web. Um recursos pode ser desde um simples arquivo ou um diretório, ou pode ser uma referência para um objeto mais complicado, como uma consulta a um banco de dados ou um mecanismo de busca.

Em geral, uma URL pode ser quebrada em várias partes. O exemplo anterior de URL indica que o protocolo usado é o http e a informação está em uma máquina alvo chamada www.ncsa.uiuc.edu. A informação da máquina alvo é chamada demoweb/url-primer.html. O significado exato desse nome na máquina alvo é tanto dependência de protocolo e de host. A informação normalmente está em um arquivo, mas pode ser gerado em tempo real. Esse componente da URL é chamado componente arquivo, mesmo que a informação não esteja necessariamente em um arquivo.

Um URL pode opcionalmente especificar uma porta, que é o número da porta na qual a conexão TCP é estabelecida na máquina remota. Se a porta não for especificada, a porta padrão do protocolo é usada. Por exemplo, a porta padrão para o http é 80. Uma porta alternativa pode ser especificada dessa forma:

http://www.ncsa.uiuc.edu:8080/demoweb/url-primer.html.

Um URL pode vir anexa  como uma âncora, também conhecida como ref ou referência. A âncora é indicada por um caractere de marcação # seguido de mais caracteres. Por exemplo,

http://java.sun.com/index.html#chapter1

Essa mâncora não é tecnicamente parte da URL. Ao invés disso, indica que após acessar o recursos especificado, a aplicação está especialmente interessada na parte do documento indentificada pela tag “chapter1”. O significado de uma tag é um recurso específico.

Uma aplicação pode também especificar uma “URL relativa”, que contém apenas a informação suficiente para alcançar o recursos relativo a outra URL. URL relativas são frequentamente usadas dentro de páginas HTML.

Por exemplo, se o conteúdo da URL for:
http://java.sun.com/index.html

contendo internamente a URL relativa:
FAQ.html

seria um atalho para:
http://java.sun.com/FAQ.html

A IRL relativa não precisa especificar todos os componentes da URL. Se o protocolo, nome do alvo ou porta estiver faltando, o valor epga esses dados da URL especificada. O componente arquivo precisa ser especificado. A âncora não é utilizada.

Exemplo para URL:

GetURLApp.java

import java.net.URL;
import java.net.MalformedURLException;
import java.io.*;

public class GetURLApp {
public static void main(String args[]) {
try {
if(args.length!=1) error(“Usage: java GetURLApp URL”);
System.out.println(“Fetching URL: “+args[0]);
URL url = new URL(args[0]);
BufferedReader inStream = new BufferedReader(new InputStreamReader(url.openStream()));
String line;
while ((line = inStream.readLine())!= null) {
System.out.println(line);
}
inStream.close();
} catch (MalformedURLException ex) {
error(“Bad URL”);
}
catch (IOException ex) {
error(“IOException occurred.”);
}
}

public static void error(String s){
System.out.println(s);
System.exit(1);
}
}

A CLASSE URLCONNECTION:

A classe abstrata URLConnection é uma superclasse para todas as classes que representam um link de comunicação entre a aplicação e uma URL. Intâncias dessa classe podem ser usadas tanto para ler quanto para escrever para o recurso referenciado pelo URL. Em geral, a criação de uma conexão para uma URL é um processo multi-passo:

openConnection()
connect()

manipulam os parâmetros que afetam a conexão para o recursos remoto. Interage com o recursos; cabeçalhos de consultas e conteúdo.

1. O objeto da conexão é criado pela invocação do metódo openConnection em uma URL.

2. Os parâmetros de configuração e propriedades da requisição são manipulados.

3. A conexão atual para o objeto remoto é feita, usando o metódo connect.

4. O objeto remoto fica disponível. Os campos do cabeçalho e o conteúdo do objeto remoto podem ser acessados.

Os parâmetros de configuração são modificados usando os metódos abaixo:

• setAllowUserInteraction
• setDoInput
• setDoOutput
• setIfModifiedSince
• setUseCaches

e as propriedades da requisição são modificadas usando o metódo setRequestProperty. Valores padrão para os parametros AllowUserInteraction e UseCaches podem ser configurados pelos metódos setDefaultAllowUserInteraction e setDefaultUseCaches. Valores padrão para as propriedades podem ser configuradas pelo metódo setDefaultRequestProperty.

Cada um dos metódos acima tem um metódo get correspondente para recuperar o valor do parâmetro ou propriedade. Os parâmetros e propriedades específicas que são aplicáveis são especificas ao protocolos.

Os seguintes metódos são usados para acessar o cabeçalho e o conteúdo deposi que a conexão foi realizada com o objeto remoto:

  • getContent
  • getHeaderField
  • getInputStream
  • getOutputStream

Certos campos do cabeçalho são mais acessados que outros. Os metódos:

  • getContentEncoding
  • getContentLength
  • getContentType
  • getDate
  • getExpiration
  • getLastModified

fornecem o acesso conveniente a esses metódos. O metódo getContentType é usado pelo metódo getContent para determinar o tipo do objeto remoto; as subclasses podem convenientemente sobrecarregar o metódo getContentType.

Em geral, todos os parâmetros de pré-conexão podem ser ignorados: os parâmetros padrão da pré-conexão tem valores sensíveis. Para muitos clientes dessa interface, existem apenas dois metódos interessantes: getInputStream e getObject, quse são espelhados pela classe URL nos metódos convenientes.

Mais informações sobre as propriedades e campos do cabeçalho de uma conexão http pode ser encontrada em:

http://www.w3.org/hypertext/WWW/Protocols/HTTP1.0/draft-ietf-http-spec.html

Conceitos básicos sobvre sockets TCP:

Os sockets foram originalmente desenvolvidos na Universidade da California em Berkeley como uma ferramenta para cumprir facilmente programas em rede. Originalmente parte do sistema operacional UNIX, o conceito de sockets foi incorporado a vários ambientes operacionais, inclusive Java.
O que é um Socket?

Um socket é um mecanismo de comunicação em rede com outras conexões. Um socket TCP é o que utiliza o protocolo TCP, herdando o comportamento do protocolo de transporte. Quatro peças de informação é necessária para criar um socket TCP:

• O endereço IP do sistema local
• O númeroda porta TCP que o sistema local está usando
• O endereço IP do sistema remoto
• O número da porta TCP onde o sistema remoto está escutando

Os sockets são frequentemente usados em aplicações cliente servidor: Um serviço centralizado espera que várias máquinas remotas requisitem seus recursos, manipulando cada requisição a medida que chega. Para que os clientes saibam como se comunicar com o servidor, protocolos conhecidos são associados a portas conhecidas. Em sistemas operacianais baseados no UNIX, as portas abaixo de 1024 são usadas por aplicações  do super-usuário (por exemplo, root), e para efeito de controle, essas portas conhecidas ficam nessa faixa, por convenção. Algumas portas conhecidas são mosradas na lista abaixo:

Porta Serviço
21 FTP
23 Telnet
25 SMTP (Internet Mail Transfer)
79 Finger
80 HTTP

Os clientes devem ter acesso a uma porta para estabelecer uma conexão a um socket. Já que os clientes iniciam a comunicação com o servidor, um número de porta pode ser associado em tempo de execução. Aplicações cliente são normalmente excutadas por usuários sem provilégios em sistemas UNIX, e por isso essas portas são alocadas na faixa acima de 1024. Essa convenção foi usada por outros sistemas operacionais, e clientes geralmente usam protas alocadas dinamicamente com mumeração acima de 1024.

Já que duas aplicações não podem usar a mesma porta na mesma máquina ao mesmo tempo, um socket identifica unicamente um link de comunicação. Observe que um servidor pode responder a dois clientes na mesma porta, desde que os clientes estejam em sistemas diferentes e/ou portas diferentes; a individualidade do link é preservada.

CLASSES PARA SOCKETS TCP EM JAVA TCP:

O Java possui várias classes que permitem que você crie uma aplicação baseada em sockets. Essas duas classes são java.net.Socket e java.net.ServerSocket.

A CLASSE SERVERSOCKET

public class ServerSocket extends Object

Essa classe implementa sockets no lado servidor. Um socket no lado servidor espera por requisições vindas da rede. Executa algumas operações baseadas na requisição, e por fim retorna algo para o solicitante.

O trabalho do socket no lado servidor é executado por uma instância da classe SocketImpl. Uma aplicação pode mudar o molde do socket que cria a implementação do socket para se configurar para criar sockets apropriados a um firewall local.

Exemplo para ServerSocket:

ServerExample.java

import java.io.*;
import java.net.*;
import java.util.Date;

public class ServerExample
{
public static void main(String args[])
{
ServerSocket server = null;
Socket socket = null;
BufferedOutputStream send = null;
try
{
server = new ServerSocket(3000);
System.out.println(“server started”);
while(true)
{
socket = server.accept();
send = new BufferedOutputStream(socket.getOutputStream());
String date = (new Date()).toString();
byte data[] = new byte[date.length()];
data = date.getBytes();
send.write(data,0,data.length);
send.flush();
System.out.println(“data flushed”);
send.close();
socket.close();
}
}
catch(Exception err) {
System.out.println(“Exception in transferring data to client”);
}
}
}

A CLASSE SOCKET:

Essa classe implementa o socket no lado do cliente (chamado somente de socket). Um socket é o ponto final na comunicação entre duas máquinas. O trabalho do socket é executado por uma instância da classe SocketImpl. Uma aplicação pode altera o molde que cria a implementação do socket, se configurando para criar sockets adequados ao firewall local.

Exemplo para Socket :

import java.io.*;
import java.net.*;

public class ClientExample {
public static void main(String args[]) {
Socket socket = null;
BufferedInputStream receive = null;
if(args.length == 0){
System.out.println(“Usage : java ClientExample “);
System.exit(0);
}
String ser_address = args[0];
try {
socket = new Socket(InetAddress.getByName(ser_address),3000);
receive = new BufferedInputStream(socket.getInputStream());
System.out.println(“socket created”);
byte data[] = new byte[100];
receive.read(data,0,data.length);
String date = new String(data);
System.out.println(“Date from server : “+date);
receive.close();
socket.close();
} catch(Exception err){
System.out.println(“Exception in accessing file”);
}
}
}

Visão geral sobre mensagens UDP:

O densenvolvimento com o protocolo UDP tem ramificações significativas. O entendimento desses fatores aperfeiçoará sua programação em rede. O UDP é uma boa escolha para aplicações em que a comunicação pode ser separada em mensagens discretas, onde uma única consulta de um cliente invoca uma única resposta do servidor. Dados que dependem da hora é particularmente bem ajustado ao UDP. O UDP requer muito menos tráfego de dados, e todo o volume de engenharia necessária para garantir a confiabilidade do dados fica sob sua responsabilidade. Por exemplo, se os clientes nunca recebem respostas para suas consultas – o que é bem possivel com o UDP – você pode querer programar os clientes para retransmitir a requisição ou talvez mostra uma mensagem informativa indicando das dificuldades de comunicação.

Caracteristicas dos sockets UDP:

O UDP é descrito como sendo não-confiavel, independente de conexão e orientado  a mensagens. Uma analogia comum que elucida o UDP é a comunicação via cartões postais. Um dialogo com o UDP pode ser feito através de pequenas mensagens que cabem dentro de um pacote pequeno de tamanho especifico, apensar de que alguns pacotes poderem ter mais dados que outros. Quando você envia uma mensagem, você nunca saberá ao certo se receberá uma mensagem de volta. A menos que receba uma resposta, nãom terá nenhuma idéia de que a mensagem foi recebida – sua mensagem pode ter sido perdida na rota, a confirmação do recipiente pode ter sudo perdida ou o recipiente pode ignorar sua mensagem. Os cartões postais que são trocados entre os programas da rede são chamados de datagramas. Dentro de um datagrama, você pode armazenar uma cadeia de bytes. Uma aplicação, ao receber um datagrama, pode extrair essa cadeiae decodificar a mensagem, possivelmente enviando um datagrama de resposta. Como no TCP, você programará com o UDP usando a abstração so socket. Porém, os sockets UDP são muitos diferentes dos sockets TCP. Usando a analogia, os sockets UDP são parecidos com uma caixa de correio. Uma caixa de correio é identificada pelo seu endereço, mas você não constrói uma nova caixa para cada pessoa que envia uma mensagem. Ao invés disso, você coloca um endereço no cartão que indica a quem a mensagem é dirigida. Você coloca o cartão na caixa de correios e ela é (eventualmente) enviada ao destino.

Quando estiver esperando uma mensagem, você deve potencialmente ficar esperando até que uma mensagem chegue em sua caixa de correio. Uma vez que chegue, você pode ler o cartão. As meta-informações aparecem no cartão que identifica que enviou a mensagem. Como a analogia anterior sugere, a programação com UDP envolve as seguintes tarefas:

• Criar um datagrama corretamente endereçado para envio.
• Configurar um socket para enviar e receber datagramas de uma aplicação em particular.
• Inserir os datagramas em um socket para transmissão.
• Ficar em modo de espera por datagramas de um socket.
• Decodificar um datagrama para extrair a mensagem e outras meta-informação.

Classes UDP do Java:

O pacote java.net possui as ferramentas necessárias para executar comunicação UDP. Para trabalhar com datagramas, o Java fornece as classes DatagramPacket e DatagramSocket. Quando receber um datagrama UDP, você pode usar a classe DatagramPacket para ler os dados.

A CLASSE DATAGRAMPACKET

Essa classe representa um pacote de datagrama. Os pacotes de datagrama são usados para implementar um serviço de entrega de pacotes independente de conexão. Cada mensagem é direciona de uma máquina para outra baseada somente na informação contida no pacote. Múltiplos pacotes enviados de uma máquina para outra podem ter rotas diferentes, e podem chegar em qualquer ordem.

Exemplo para o DatagramPacket:

import java.net.*;
import java.io.*;

public class DatagramClient
{
public static void main(String args[])
{
if(args.length == 0)
{
System.out.println(“Usage : java DatagramClient “);
System.exit(0);
}
String address = args[0];
DatagramPacket dgp = null;
DatagramSocket dgs = null;
byte receive[] = new byte[50];
try
{
dgs = new DatagramSocket(5000,InetAddress.getByName(address));
dgp = new DatagramPacket(receive,receive.length);
dgs.receive(dgp);
System.out.println(“data received : “+(new String(receive)));
dgs.close();
}
catch(Exception err) {
System.out.println(“Exception in client”);
}
}
}

A CLASSE DATAGRAMSOCKET:

Essa classe representa um socket para envio e recebimento de pacotes de datagramas. Um socket para datagrama é o ponto de envio e recebimento para o serviço de entrega de pacotes. Cada pacote enviado ou recebido em um socket de datagrama é endereçado e roteado individualmente. Múltiplos pacotes enviados de uma máquina para outra pode ter rotas diferentes, e podem chegar em qualquer ordem.