Quando falamos em networking, não podemos deixar de mencionar o modelo OSI.

Em termos desse modelo, hoje estamos mais interessados ​​na camada de transporte (4).

Este é o nível em que trabalhamos com dados que se deslocam "do ponto A ao ponto B". A principal tarefa da camada de transporte é garantir que uma mensagem seja entregue ao destino, mantendo a sequência correta. Os dois protocolos de camada de transporte mais comuns são: TCP e UDP. Eles funcionam conceitualmente de maneiras diferentes, mas cada um tem suas próprias vantagens que permitem resolver problemas específicos.

Primeiro, vamos ver como o TCP funciona.

TCP (Transmission Control Protocol) é um protocolo de rede que garante que uma conexão entre os hosts seja estabelecida antes da troca de dados.

Este é um protocolo muito confiável, pois toda vez que envia outro pacote de dados, deve verificar se o pacote anterior foi recebido.

Os pacotes transmitidos são ordenados e, se houver problemas com um determinado pacote (ou seja, o destinatário não confirma que o pacote chegou), o pacote é enviado novamente. Como resultado, a taxa de transferência é relativamente baixa, pois é necessário mais tempo para o monitoramento rigoroso e para garantir a ordenação correta.

É aqui que entra seu "irmão", o protocolo UDP. Ao contrário do TCP, o UDP realmente não se importa com a ordem e o status de cada pacote. Ele simplesmente envia dados sem confirmação de entrega. Além do mais, ele não estabelece uma conexão e não depende de forma alguma do status da conexão.

Sua finalidade é simplesmente enviar dados para um endereço. E isso dá origem à principal desvantagem do protocolo, a baixa confiabilidade, pois pode simplesmente perder pedaços de dados. Além disso, o destinatário deve estar preparado para o fato de que os dados podem chegar fora de ordem. Dito isto, o protocolo também tem uma vantagem, uma taxa de transferência mais elevada, devido ao facto de o protocolo se limitar a enviar dados.

Também existem diferenças em como os próprios dados são transmitidos. No TCP, os dados são transmitidos, o que significa que os dados não têm limites. No UDP, os dados são transmitidos como datagramas e possuem limites, e o destinatário verifica a integridade dos dados, mas somente se a mensagem for recebida com sucesso.

Vamos resumir:

O TCP é um protocolo confiável e preciso que evita a perda de dados. Uma mensagem sempre será entregue com a máxima precisão, ou não será entregue. O destinatário não precisa de lógica para solicitar dados, pois os dados recebidos já serão solicitados. O UDP não é tão confiável, mas é um protocolo de transferência de dados mais rápido. As partes de envio e recebimento precisam de alguma lógica adicional para trabalhar com este protocolo. Mas vamos dar uma olhada em como funciona usando o exemplo de um jogo de computador ou jogo para celular jogado pela rede. Podemos não nos importar mais com o que deveria ter chegado 5 segundos atrás e podemos pular alguns pacotes se eles não chegarem a tempo - o jogo pode atrasar, mas você ainda pode jogar!

Em Java, para trabalhar com datagramas transmitidos por UDP, utilizamos objetos das classes DatagramSocket e DatagramPacket .

Para trocar dados, o remetente e o destinatário criam soquetes de datagrama, ou seja, instâncias da classe DatagramSocket . A classe tem vários construtores. A diferença entre eles é onde o socket criado irá se conectar:

DatagramaSocket() Conecta-se a qualquer porta disponível na máquina local
DatagramSocket (porta int) Conecta-se à porta especificada na máquina local
DatagramSocket (porta int, endereço InetAddress) Conecta-se à porta especificada em um endereço na máquina local (addr)

A classe contém muitos métodos para acessar e gerenciar os parâmetros do soquete (vamos vê-los um pouco mais tarde), bem como métodos para receber e enviar datagramas:

enviar (pacote DatagramPacket) Envia datagramas compactados em pacotes
receber (pacote DatagramPacket) Recebe datagramas compactados em pacotes

DatagramPacket é uma classe que representa um pacote de datagramas. Pacotes de datagramas são usados ​​para implementar um serviço de entrega de pacotes sem conexão. Cada mensagem é roteada de uma máquina para outra com base apenas nas informações contidas naquele pacote. Vários pacotes enviados de uma máquina para outra podem ser roteados de forma diferente e podem chegar em qualquer ordem. A entrega de pacotes não é garantida.

Construtores:

DatagramPacket(byte[] buf, comprimento int) Cria um DatagramPacket para aceitar pacotes de comprimento length .
DatagramPacket(byte[] buf, comprimento int, endereço InetAddress, porta int) Cria um pacote de datagrama para enviar pacotes de comprimento para o número de porta especificado no host especificado.
DatagramPacket(byte[] buf, int offset, int length) Cria um DatagramPacket para aceitar pacotes de comprimento length , especificando um deslocamento no buffer.
DatagramPacket(byte[] buf, deslocamento int, comprimento int, endereço InetAddress, porta int) Cria um pacote de datagrama para enviar pacotes de comprimento com deslocamento de deslocamento para o número de porta especificado no host especificado.
DatagramPacket(byte[] buf, deslocamento int, comprimento int, endereço SocketAddress) Cria um pacote de datagrama para enviar pacotes de comprimento com deslocamento de deslocamento para o número de porta especificado no host especificado.
DatagramPacket(byte[] buf, comprimento int, endereço SocketAddress) Cria um pacote de datagrama para enviar pacotes de comprimento para o número de porta especificado no host especificado.

Lembramos que a abordagem UDP não estabelece uma conexão. Os pacotes são enviados na esperança de que o destinatário os esteja esperando. Mas você pode estabelecer uma conexão usando o método connect(InetAddress addr, int port) da classe DatagramSocket .

Uma conexão unidirecional é estabelecida com o host com base em um endereço e porta: para enviar ou receber datagramas. A conexão pode ser encerrada usando o método desconectar() .

Vamos tentar escrever código de servidor baseado em DatagramSocket para receber dados:

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;

class Recipient {

   public static void main(String[] args) {
       try {
           DatagramSocket ds = new DatagramSocket(1050);

           while (true) {
               DatagramPacket pack = new DatagramPacket(new byte[5], 5);
               ds.receive(pack);
               System.out.println(new String(pack.getData()));
           }
       } catch (IOException e) {
           e.printStackTrace();
       }
   }
}

Criamos um objeto DatagramSocket para escutar na porta 1050. Ao receber uma mensagem, ele a imprime no console. Vamos transmitir a palavra "Olá", então limitamos o tamanho do buffer a cinco bytes.

Agora vamos criar a classe do remetente:

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

class Sender {
   private String host;
   private int port;

   Sender(String host, int port) {
       this.host = host;
       this.port = port;
   }

   private void sendMessage(String mes) {
       try {
           byte[] data = mes.getBytes();
           InetAddress address = InetAddress.getByName(host);
           DatagramPacket pack = new DatagramPacket(data, data.length, address, port);
           DatagramSocket ds = new DatagramSocket();
           ds.send(pack);
           ds.close();
       } catch (IOException e) {
           System.err.println(e);
       }
   }

   public static void main(String[] args) {
   Sender sender = new Sender("localhost", 1050);
   String message = "Hello";

   Timer timer = new Timer();
   timer.scheduleAtFixedRate(new TimerTask() {
       @Override
       public void run() {
           sender.sendMessage(message);
       }
   }, 1000, 1000);
}

}

No método sendMessage , criamos um DatagramPacket e um DatagramSocket e enviamos nossa mensagem. Observe que o método close() é usado para fechar o DatagramSocket após o envio da mensagem.

A cada segundo, o console do destinatário exibe a mensagem "Olá" enviada pelo remetente. Isso significa que nossa comunicação está funcionando corretamente.