Primeiro entenda as diferenças entre TCP e UDP:
TCP (Trasmission Control Protocol) | UDP (Use Datagram Protocol) |
---|---|
Orientado a conexão | Orientado a datagrama |
É confiável | Não é confiável |
Stream (segmentos) | Datagramas (pacotes) |
Controle de fluxo e retrasmissão de mensagens | Sem controle de fluxo |
Mensagens ordenadas | Sem garantia de ordem ou chegada |
Recebimento dos segmentos é confirmado (ACK) | Mais apropriado a broadcast e multicast |
Mais lento | Mais rápido |
A comunicação cliente-servidor mais simples que existe em Java pode ser esquematizada, depois da conexão, qualquer lado pode iniciar a comunicação (o canal é bidirecional) e é assíncrona e bloqueante (em geral)..
O servidor cria um socket associado a uma porta. A operação new ServerSocket()
realiza as operações, depois espera conexões de clientes. Quando aceita conexão, um novo socket é criado para a conexão em uma nova porta e continua aceitando outras conexões na mesma porta enquanto ServerSocket() estiver instanciado.
O cliente deve conhecer o nome e porta do servidor, é quem solicita a conexão, se a conexão é aceita pelo servidor, um socket é criado e associado a uma porta no cliente permitindo a conexão porta com porta.
A classe Socket em Java não possui os equivalente da api C/C++, a entrada e saída de dados é feita por Streams, onde:
Crie um novo projeto sem classe principal, e crie duas classes, a Cliente e a Servidor.
Na classe Cliente, colocamos esse código:
package conexoes;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.logging.Logger;
import java.util.logging.Level;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Arrays;
public class Cliente {
public static void main(String[] args) {
int porta = 33333; // Porta default do servidor
String serv = "127.0.0.1"; // Endereço do servidor, no caso, o de loopback do próprio cliente, pode ser qualquer endereço
byte conteudo[] = {1, 4, 5, 6, 14, 18, 2, 4, 10, 11, 9, 12, 13, 3}; // Bytes fora de ordem a serem transmitidos
try {
try(Socket sock = new Socket(serv, porta)) { // Importe
OutputStream out = sock.getOutputStream(); // Importe
out.write(conteudo); // Escrevemos os dados no Output para transmitir
System.out.printf("Dados transmitidos para servidor %s: %04d.\n", serv, porta);
InputStream in = sock.getInputStream(); // Importe
in.read(conteudo); // Obtem Input para receber dados utilizando o socket criado
System.out.append(Arrays.toString(conteudo)); // Importe
}
}
catch(UnknownHostException ex) { // Importe
System.err.println("Host desconhecido!");
}
catch(IOException ex) { // Importe
Logger.getLogger(Cliente.class.getName()).log(Level.SEVERE, null, ex); // Importe
}
}
}
O lado servidor recebe o stream de bytes, ordena e devolve ao cliente, esse é o código do Servidor:
package conexoes;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Arrays;
public class Servidor {
private Socket sock; // Importe
public Servidor(Socket sock) {
this.sock = sock;
}
public static void main(String[] args) {
int porta = 33333; // Porta default (a mesma do cliente)
try {
ServerSocket servSock = new ServerSocket(porta); // Criamos o socket apenas com a porta, importe
System.out.println("Socket criado!");
while(true) { // Loop para receber comunicações de clientes
System.out.println("Aguardando cliente...");
try(Socket clie = servSock.accept()) {
try(InputStream in = clie.getInputStream(); OutputStream out = clie.getOutputStream()) { // Importe
byte buffer[] = new byte[2048];
int n;
while((n = in.read(buffer)) > 0) {
for(int i = 0; i < n; i++) {
System.out.printf("%02d", buffer[i]);
}
System.out.println(servSock.getInetAddress().toString());
Arrays.sort(buffer, 0, n); // Ordena, importe
out.write(buffer);
}
}
catch(IOException ex) {
System.err.println("Erro no accept: " + ex.getMessage());
}
}
}
}
catch(IOException ex) {
System.err.println(ex.getMessage());
}
}
}
Agora rode, não esqueça de fechar todas as conexões que usam a determinada porta.
Vamos fazer uma aplicação simples que funciona da seguinte maneira: O cliente cria um datagrama socket, um datagrama em pacote, recebe um pacote, obtém datae hora local e envia pacote para o emissor. O servidor recebe esse pacote.
A classe servidor deverá ficar assim:
package conexoes;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.util.Date;
public class Servidor {
public static void main(String[] args) {
int porta = 33333;
// Vamos aceitar a porta como argumento do programa:
if(args.length >= 1) {
porta = Integer.valueOf(args[0]);
}
// Datagram sockeT indica que isto é UDP:
DatagramSocket sock; // Importe
try {
sock = new DatagramSocket(porta);
// Buffer criado para enviar ou receber dados:
byte buffer[] = new byte[8];
// Criação do datagrama pack:
DatagramPacket pct = new DatagramPacket(buffer, buffer.length); // Importe
// Espera e recebe um pacotecom a data no buffer:
System.out.println("Esperando mensagem do cliente...");
try {
sock.receive(pct);
}
catch(IOException ex) { // Importe
System.err.println("Erro: Recebimento de pacote!");
}
// Obter a data:
Date data = new Date(); // Importe
// A melhor forma de escrever é utilizar streams, o socket lê isso:
ByteArrayOutputStream baos = new ByteArrayOutputStream(); // Importe
// E usamos em conjunto com isso:
DataOutputStream dos = new DataOutputStream(baos); // Importe
try {
dos.writeLong(data.getTime());
}
catch(IOException ex) {
System.err.println("Erro: Conversão da data para o array!");
}
// Manda o pacote para o cliente, em pct tem o IP do cliente:
System.out.println("Enviando data para cliente " + pct.getAddress());
buffer = baos.toByteArray();
pct = new DatagramPacket(buffer, buffer.length, pct.getAddress(), pct.getPort());
try {
sock.send(pct);
}
catch(IOException ex) {
System.err.printf("Erro: Envio de pacote para %s.\n", pct.getAddress().toString());
}
System.out.printf("Pacote enviado para %s!\n", pct.getAddress().toString());
System.out.printf("Data enviada: %s.\n", data.toString());
}
catch(SocketException ex) { // Importe
System.err.println("Erro: Criação do socket!");
}
}
}
E no cliente:
package conexoes;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Date;
public class Cliente {
public static void main(String[] args) throws UnknownHostException {
// Endereço do servidor:
String serv = "127.0.0.1";
int porta = 33333;
if(args.length >= 1) {
serv = args[0];
if(args.length == 2) {
porta = Integer.valueOf(args[1]);
}
}
try {
// Criar datasocket:
DatagramSocket sock = new DatagramSocket(); // Importe
// Criamos um buffer para poder enviar ou receber dados:
byte buffer[] = new byte[8];
// Criar um endereço para enviar pacote ao servidor:
InetAddress end = InetAddress.getByName(serv); // Importe e adicione o throws no método main
// Criar um datagrama:
DatagramPacket pct = new DatagramPacket(buffer, buffer.length, end, porta); // Importe
// Enviamos um pacote em branco como mensagem para receber a data:
System.out.printf("Enviando solicitação de data para %s.\n", end.toString());
try {
sock.send(pct);
}
catch(IOException ex) {
System.err.println("Erro: Envio de mensagem para servidor!");
}
// Espera e recebe um pacote com a data no buffer:
pct = new DatagramPacket(buffer, buffer.length);
System.out.println("Aguardando data do servidor...");
try {
sock.receive(pct);
}
catch(IOException ex) {
System.err.println("Erro: Recebimento de Mensagem do Servidor!");
}
// Decodificando, processo inverso do que vimos no servidor:
DataInputStream dis = new DataInputStream(new ByteArrayInputStream(buffer)); // Importe
try {
Date data = new Date(dis.readLong()); // Isso converte a data, importe
System.out.printf("Data recebida: %s.\n", data.toString());
}
catch(IOException ex) {
System.err.println("Erro: Conversão de data!");
}
}
catch(SocketException ex) {
System.err.println("Erro: Criação do socket!");
}
}
}
Execute a classe servidor e depois a cliente.