Escrever código que é executado em um determinado dispositivo é muito satisfatório. Mas, escrever código que é executado em vários dispositivos que se comunicam entre si é simplesmente uma afirmação de vida. Este artigo ensinará como conectar e trocar mensagens pela rede usando o protocolo de controle de transmissão (TCP).
Neste artigo, você configurará um aplicativo que conectará seu computador a ele mesmo e, essencialmente, o deixará louco - fale com ele mesmo. Você também aprenderá a diferença entre os dois fluxos mais amplamente usados para rede em Java e como eles funcionam.
Streams de dados e objetos
Antes de mergulhar no código, a diferença entre os dois fluxos usados no artigo precisa ser distinguida.
Streams de dados
Os fluxos de dados processam strings e tipos de dados primitivos. Os dados enviados por fluxos de dados precisam ser serializados e desserializados manualmente, o que torna mais difícil a transferência de dados complexos. Porém, os fluxos de dados podem se comunicar com servidores e clientes escritos em outras linguagens além do Java. Os fluxos brutos são semelhantes aos fluxos de dados nesse aspecto, mas os fluxos de dados garantem que os dados sejam formatados de forma independente da plataforma, o que é benéfico porque ambas as partes poderão ler os dados enviados.
Streams de objetos
Os fluxos de objetos processam tipos de dados primitivos e objetos que implementam
Serializável
interface. Os dados enviados por fluxos de objetos são serializados e desserializados automaticamente, o que facilita a transferência de dados complexos. Porém, os fluxos de objetos só podem se comunicar com servidores e clientes escritos em Java. Também,
ObjectOutputStream
na inicialização, envia um cabeçalho para o
InputStream
da outra parte que, na inicialização, bloqueia a execução até que o cabeçalho seja recebido.
Passos
Etapa 1. Crie uma classe
Crie uma classe e nomeie-a como quiser. Neste artigo, ele será nomeado
NetworkAppExample
public class NetworkAppExample {}
Etapa 2. Crie um método principal
Crie um método principal e declare que pode lançar exceções de
Exceção
tipo e qualquer subclasse dele - todas as exceções. Isso é considerado uma prática ruim, mas é aceitável para exemplos simples.
public class NetworkAppExample {public static void main (String args) lança exceção {}}
Etapa 3. Declare o endereço do servidor
Este exemplo usará o endereço do host local e um número de porta arbitrário. O número da porta deve estar no intervalo de 0 a 65535 (inclusive). No entanto, os números de porta a serem evitados variam de 0 a 1023 (inclusive) porque são portas reservadas do sistema.
public class NetworkAppExample {public static void main (String args) lança Exceção {String host = "localhost"; porta int = 10430; }}
Etapa 4. Crie um servidor
O servidor está vinculado ao endereço e à porta e escuta as conexões de entrada. Em Java,
ServerSocket
representa o endpoint do lado do servidor e sua função é aceitar novas conexões.
ServerSocket
não possui streams para leitura e envio de dados, pois não representa a conexão entre um servidor e um cliente.
import java.net. InetAddress; import java.net. ServerSocket; public class NetworkAppExample {public static void main (String args) lança Exceção {String host = "localhost"; porta int = 10430; Servidor ServerSocket = novo ServerSocket (porta, 50, InetAddress.getByName (host)); }}
Etapa 5. Início do servidor de log
Para fins de registro, imprima no console que o servidor foi iniciado.
import java.net. InetAddress; import java.net. ServerSocket; public class NetworkAppExample {public static void main (String args) lança Exceção {String host = "localhost"; porta int = 10430; Servidor ServerSocket = novo ServerSocket (porta, 50, InetAddress.getByName (host)); System.out.println ("Servidor iniciado."); }}
Etapa 6. Crie um cliente
O cliente está vinculado ao endereço e à porta de um servidor e escuta os pacotes (mensagens) após o estabelecimento da conexão. Em Java,
Soquete
representa um ponto de extremidade do lado do cliente conectado ao servidor ou uma conexão (do servidor) ao cliente e é usado para se comunicar com a parte na outra extremidade.
import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) lança Exceção {String host = "localhost"; porta int = 10430; Servidor ServerSocket = novo ServerSocket (porta, 50, InetAddress.getByName (host)); System.out.println ("Servidor iniciado."); Cliente de soquete = novo soquete (host, porta); }}
Etapa 7. Registrar tentativa de conexão
Para fins de registro, imprima no console que a conexão foi tentada.
import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) lança Exceção {String host = "localhost"; porta int = 10430; Servidor ServerSocket = novo ServerSocket (porta, 50, InetAddress.getByName (host)); System.out.println ("Servidor iniciado."); Cliente de soquete = novo soquete (host, porta); System.out.println ("Conectando ao servidor…"); }}
Etapa 8. Estabeleça a conexão
Os clientes nunca se conectarão a menos que o servidor ouça e aceite, em outras palavras, estabeleça conexões. Em Java, as conexões são estabelecidas usando
aceitar()
método de
ServerSocket
classe. O método bloqueará a execução até que um cliente se conecte.
import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) lança Exceção {String host = "localhost"; porta int = 10430; Servidor ServerSocket = novo ServerSocket (porta, 50, InetAddress.getByName (host)); System.out.println ("Servidor iniciado."); Cliente de soquete = novo soquete (host, porta); System.out.println ("Conectando ao servidor…"); Conexão de soquete = server.accept (); }}
Etapa 9. Registrar a conexão estabelecida
Para fins de registro, imprima no console que a conexão entre o servidor e o cliente foi estabelecida.
import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) lança Exceção {String host = "localhost"; porta int = 10430; Servidor ServerSocket = novo ServerSocket (porta, 50, InetAddress.getByName (host)); System.out.println ("Servidor iniciado."); Cliente de soquete = novo soquete (host, porta); System.out.println ("Conectando ao servidor…"); Conexão de soquete = server.accept (); System.out.println ("Conexão estabelecida."); }}
Etapa 10. Prepare os fluxos de comunicação
A comunicação é feita por meio de fluxos e, neste aplicativo, fluxos brutos de (conexão de) servidor (para cliente) e cliente precisam ser encadeados a fluxos de dados ou objetos. Lembre-se de que ambas as partes precisam usar o mesmo tipo de fluxo.
-
Streams de dados
import java.io. DataInputStream; import java.io. DataOutputStream; import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) lança Exceção {String host = "localhost"; porta int = 10430; Servidor ServerSocket = novo ServerSocket (porta, 50, InetAddress.getByName (host)); System.out.println ("Servidor iniciado."); Cliente de soquete = novo soquete (host, porta); System.out.println ("Conectando ao servidor…"); Conexão de soquete = server.accept (); System.out.println ("Conexão estabelecida."); DataOutputStream clientOut = new DataOutputStream (client.getOutputStream ()); DataInputStream clientIn = new DataInputStream (client.getInputStream ()); DataOutputStream serverOut = new DataOutputStream (connection.getOutputStream ()); DataInputStream serverIn = new DataInputStream (connection.getInputStream ()); }}
-
Fluxos de objetos
Quando vários fluxos de objetos são usados, os fluxos de entrada devem ser inicializados na mesma ordem que os fluxos de saída porque
ObjectOutputStream
envia um cabeçalho para a outra parte e
ObjectInputStream
bloqueia a execução até ler o cabeçalho.
import java.io. ObjectInputStream; import java.io. ObjectOutputStream; import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) lança Exceção {String host = "localhost"; porta int = 10430; Servidor ServerSocket = novo ServerSocket (porta, 50, InetAddress.getByName (host)); System.out.println ("Servidor iniciado."); Cliente de soquete = novo soquete (host, porta); System.out.println ("Conectando ao servidor…"); Conexão de soquete = server.accept (); System.out.println ("Conexão estabelecida."); ObjectOutputStream clientOut = new ObjectOutputStream (client.getOutputStream ()); ObjectOutputStream serverOut = new ObjectOutputStream (connection.getOutputStream ()); ObjectInputStream clientIn = new ObjectInputStream (client.getInputStream ()); ObjectInputStream serverIn = new ObjectInputStream (connection.getInputStream ()); }}
A ordem especificada no código acima pode ser mais fácil de lembrar - primeiro inicialize os fluxos de saída e, em seguida, os fluxos de entrada na mesma ordem. No entanto, outra ordem de inicialização de fluxos de objetos é a seguinte:
ObjectOutputStream clientOut = new ObjectOutputStream (client.getOutputStream ()); ObjectInputStream serverIn = new ObjectInputStream (connection.getInputStream ()); ObjectOutputStream serverOut = new ObjectOutputStream (connection.getOutputStream ()); ObjectInputStream clientIn = new ObjectInputStream (client.getInputStream ());
Etapa 11. Registre que a comunicação está pronta
Para fins de registro, imprima no console que a comunicação está pronta.
// código omitido import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) lança Exceção {String host = "localhost"; porta int = 10430; Servidor ServerSocket = novo ServerSocket (porta, 50, InetAddress.getByName (host)); System.out.println ("Servidor iniciado."); Cliente de soquete = novo soquete (host, porta); System.out.println ("Conectando ao servidor…"); Conexão de soquete = server.accept (); System.out.println ("Conexão estabelecida."); // código omitido System.out.println ("A comunicação está pronta."); }}
Etapa 12. Crie uma mensagem
Neste aplicativo,
Olá Mundo
o texto será enviado para o servidor como
byte
ou
Fragmento
. Declare uma variável do tipo que depende do fluxo usado. Usar
byte
para fluxos de dados e
Fragmento
para fluxos de objetos.
-
Streams de dados
Usando fluxos de dados, a serialização é feita convertendo objetos em tipos de dados primitivos ou um
Fragmento
. Nesse caso,
Fragmento
é convertido para
byte
em vez de escrever usando
writeBytes ()
método para mostrar como isso seria feito com outros objetos, como imagens ou outros arquivos.
import java.io. DataInputStream; import java.io. DataOutputStream; import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) lança Exceção {String host = "localhost"; porta int = 10430; Servidor ServerSocket = novo ServerSocket (porta, 50, InetAddress.getByName (host)); System.out.println ("Servidor iniciado."); Cliente de soquete = novo soquete (host, porta); System.out.println ("Conectando ao servidor…"); Conexão de soquete = server.accept (); System.out.println ("Conexão estabelecida."); DataOutputStream clientOut = new DataOutputStream (client.getOutputStream ()); DataInputStream clientIn = new DataInputStream (client.getInputStream ()); DataOutputStream serverOut = new DataOutputStream (connection.getOutputStream ()); DataInputStream serverIn = new DataInputStream (connection.getInputStream ()); System.out.println ("A comunicação está pronta."); byte messageOut = "Hello World".getBytes (); }}
-
Fluxos de objetos
import java.io. ObjectInputStream; import java.io. ObjectOutputStream; import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) lança Exceção {String host = "localhost"; porta int = 10430; Servidor ServerSocket = novo ServerSocket (porta, 50, InetAddress.getByName (host)); System.out.println ("Servidor iniciado."); Cliente de soquete = novo soquete (host, porta); System.out.println ("Conectando ao servidor…"); Conexão de soquete = server.accept (); System.out.println ("Conexão estabelecida."); ObjectOutputStream clientOut = new ObjectOutputStream (client.getOutputStream ()); ObjectOutputStream serverOut = new ObjectOutputStream (connection.getOutputStream ()); ObjectInputStream clientIn = new ObjectInputStream (client.getInputStream ()); ObjectInputStream serverIn = new ObjectInputStream (connection.getInputStream ()); System.out.println ("A comunicação está pronta."); String messageOut = "Hello World"; }}
Etapa 13. Envie a mensagem
Grave dados no fluxo de saída e libere o fluxo para garantir que os dados foram inteiramente gravados.
-
Streams de dados
O comprimento de uma mensagem precisa ser enviado primeiro para que a outra parte saiba quantos bytes ela precisa ler. Depois que o comprimento é enviado como um tipo inteiro primitivo, os bytes podem ser enviados.
import java.io. DataInputStream; import java.io. DataOutputStream; import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) lança Exceção {String host = "localhost"; porta int = 10430; Servidor ServerSocket = novo ServerSocket (porta, 50, InetAddress.getByName (host)); System.out.println ("Servidor iniciado."); Cliente de soquete = novo soquete (host, porta); System.out.println ("Conectando ao servidor…"); Conexão de soquete = server.accept (); System.out.println ("Conexão estabelecida."); DataOutputStream clientOut = new DataOutputStream (client.getOutputStream ()); DataInputStream clientIn = new DataInputStream (client.getInputStream ()); DataOutputStream serverOut = new DataOutputStream (connection.getOutputStream ()); DataInputStream serverIn = new DataInputStream (connection.getInputStream ()); System.out.println ("A comunicação está pronta."); byte messageOut = "Hello World".getBytes (); clientOut.writeInt (messageOut.length); clientOut.write (messageOut); clientOut.flush (); }}
-
Fluxos de objetos
import java.io. ObjectInputStream; import java.io. ObjectOutputStream; import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) lança Exceção {String host = "localhost"; porta int = 10430; Servidor ServerSocket = novo ServerSocket (porta, 50, InetAddress.getByName (host)); System.out.println ("Servidor iniciado."); Cliente de soquete = novo soquete (host, porta); System.out.println ("Conectando ao servidor…"); Conexão de soquete = server.accept (); System.out.println ("Conexão estabelecida."); ObjectOutputStream clientOut = new ObjectOutputStream (client.getOutputStream ()); ObjectOutputStream serverOut = new ObjectOutputStream (connection.getOutputStream ()); ObjectInputStream clientIn = new ObjectInputStream (client.getInputStream ()); ObjectInputStream serverIn = new ObjectInputStream (connection.getInputStream ()); System.out.println ("A comunicação está pronta."); String messageOut = "Hello World"; clientOut.writeObject (messageOut); clientOut.flush (); }}
Etapa 14. Registrar a mensagem enviada
Para fins de registro, imprima no console que a mensagem foi enviada.
-
Streams de dados
import java.io. DataInputStream; import java.io. DataOutputStream; import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) lança Exceção {String host = "localhost"; porta int = 10430; Servidor ServerSocket = novo ServerSocket (porta, 50, InetAddress.getByName (host)); System.out.println ("Servidor iniciado."); Cliente de soquete = novo soquete (host, porta); System.out.println ("Conectando ao servidor…"); Conexão de soquete = server.accept (); System.out.println ("Conexão estabelecida."); DataOutputStream clientOut = new DataOutputStream (client.getOutputStream ()); DataInputStream clientIn = new DataInputStream (client.getInputStream ()); DataOutputStream serverOut = new DataOutputStream (connection.getOutputStream ()); DataInputStream serverIn = new DataInputStream (connection.getInputStream ()); System.out.println ("A comunicação está pronta."); byte messageOut = "Hello World".getBytes (); clientOut.writeInt (messageOut.length); clientOut.write (messageOut); clientOut.flush (); System.out.println ("Mensagem enviada ao servidor:" + nova String (messageOut)); }}
-
Fluxos de objetos
import java.io. ObjectInputStream; import java.io. ObjectOutputStream; import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) lança Exceção {String host = "localhost"; porta int = 10430; Servidor ServerSocket = novo ServerSocket (porta, 50, InetAddress.getByName (host)); System.out.println ("Servidor iniciado."); Cliente de soquete = novo soquete (host, porta); System.out.println ("Conectando ao servidor…"); Conexão de soquete = server.accept (); System.out.println ("Conexão estabelecida."); ObjectOutputStream clientOut = new ObjectOutputStream (client.getOutputStream ()); ObjectOutputStream serverOut = new ObjectOutputStream (connection.getOutputStream ()); ObjectInputStream clientIn = new ObjectInputStream (client.getInputStream ()); ObjectInputStream serverIn = new ObjectInputStream (connection.getInputStream ()); System.out.println ("A comunicação está pronta."); String messageOut = "Hello World"; clientOut.writeObject (messageOut); clientOut.flush (); System.out.println ("Mensagem enviada ao servidor:" + messageOut); }}
Etapa 15. Leia a mensagem
Leia os dados do fluxo de entrada e converta-os. Uma vez que sabemos exatamente o tipo de dados enviados, iremos criar um
Fragmento
a partir de
byte
ou elenco
Objeto
para
Fragmento
sem verificar, dependendo do fluxo usado.
-
Streams de dados
Como o comprimento foi enviado primeiro e os bytes depois, a leitura deve ser feita na mesma ordem. Caso o comprimento seja zero, não há nada para ler. O objeto é desserializado quando os bytes são convertidos de volta em uma instância, neste caso, de
Fragmento
import java.io. DataInputStream; import java.io. DataOutputStream; import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) lança Exceção {String host = "localhost"; porta int = 10430; Servidor ServerSocket = novo ServerSocket (porta, 50, InetAddress.getByName (host)); System.out.println ("Servidor iniciado."); Cliente de soquete = novo soquete (host, porta); System.out.println ("Conectando ao servidor…"); Conexão de soquete = server.accept (); System.out.println ("Conexão estabelecida."); DataOutputStream clientOut = new DataOutputStream (client.getOutputStream ()); DataInputStream clientIn = new DataInputStream (client.getInputStream ()); DataOutputStream serverOut = new DataOutputStream (connection.getOutputStream ()); DataInputStream serverIn = new DataInputStream (connection.getInputStream ()); System.out.println ("A comunicação está pronta."); byte messageOut = "Hello World".getBytes (); clientOut.writeInt (messageOut.length); clientOut.write (messageOut); clientOut.flush (); System.out.println ("Mensagem enviada ao servidor:" + nova String (messageOut)); comprimento interno = serverIn.readInt (); if (comprimento> 0) {byte mensagemIn = novo byte [comprimento]; serverIn.readFully (messageIn, 0, messageIn.length); }}}
-
Fluxos de objetos
import java.io. ObjectInputStream; import java.io. ObjectOutputStream; import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) lança Exceção {String host = "localhost"; porta int = 10430; Servidor ServerSocket = novo ServerSocket (porta, 50, InetAddress.getByName (host)); System.out.println ("Servidor iniciado."); Cliente de soquete = novo soquete (host, porta); System.out.println ("Conectando ao servidor…"); Conexão de soquete = server.accept (); System.out.println ("Conexão estabelecida."); ObjectOutputStream clientOut = new ObjectOutputStream (client.getOutputStream ()); ObjectOutputStream serverOut = new ObjectOutputStream (connection.getOutputStream ()); ObjectInputStream clientIn = new ObjectInputStream (client.getInputStream ()); ObjectInputStream serverIn = new ObjectInputStream (connection.getInputStream ()); System.out.println ("A comunicação está pronta."); String messageOut = "Hello World"; clientOut.writeObject (messageOut); clientOut.flush (); System.out.println ("Mensagem enviada ao servidor:" + messageOut); String messageIn = (String) serverIn.readObject (); }}
Etapa 16. Mensagem de leitura de log
Para fins de registro, imprima no console a mensagem recebida e imprima seu conteúdo.
-
Streams de dados
import java.io. DataInputStream; import java.io. DataOutputStream; import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) lança Exceção {String host = "localhost"; porta int = 10430; Servidor ServerSocket = novo ServerSocket (porta, 50, InetAddress.getByName (host)); System.out.println ("Servidor iniciado."); Cliente de soquete = novo soquete (host, porta); System.out.println ("Conectando ao servidor…"); Conexão de soquete = server.accept (); System.out.println ("Conexão estabelecida."); DataOutputStream clientOut = new DataOutputStream (client.getOutputStream ()); DataInputStream clientIn = new DataInputStream (client.getInputStream ()); DataOutputStream serverOut = new DataOutputStream (connection.getOutputStream ()); DataInputStream serverIn = new DataInputStream (connection.getInputStream ()); System.out.println ("A comunicação está pronta."); byte messageOut = "Hello World".getBytes (); clientOut.writeInt (messageOut.length); clientOut.write (messageOut); clientOut.flush (); System.out.println ("Mensagem enviada ao servidor:" + nova String (messageOut)); comprimento interno = serverIn.readInt (); if (comprimento> 0) {byte mensagemIn = novo byte [comprimento]; serverIn.readFully (messageIn, 0, messageIn.length); System.out.println ("Mensagem recebida do cliente:" + nova String (messageIn)); }}}
-
Fluxos de objetos
import java.io. ObjectInputStream; import java.io. ObjectOutputStream; import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) lança Exceção {String host = "localhost"; porta int = 10430; Servidor ServerSocket = novo ServerSocket (porta, 50, InetAddress.getByName (host)); System.out.println ("Servidor iniciado."); Cliente de soquete = novo soquete (host, porta); System.out.println ("Conectando ao servidor…"); Conexão de soquete = server.accept (); System.out.println ("Conexão estabelecida."); ObjectOutputStream clientOut = new ObjectOutputStream (client.getOutputStream ()); ObjectOutputStream serverOut = new ObjectOutputStream (connection.getOutputStream ()); ObjectInputStream clientIn = new ObjectInputStream (client.getInputStream ()); ObjectInputStream serverIn = new ObjectInputStream (connection.getInputStream ()); System.out.println ("A comunicação está pronta."); String messageOut = "Hello World"; clientOut.writeObject (messageOut); clientOut.flush (); System.out.println ("Mensagem enviada ao servidor:" + messageOut); String messageIn = (String) serverIn.readObject (); System.out.println ("Mensagem recebida do cliente:" + messageIn); }}
Etapa 17. Desconecte as conexões
A conexão é desconectada quando uma das partes fecha seus streams. Em Java, ao fechar o fluxo de saída, o soquete associado e o fluxo de entrada também são fechados. Depois que uma parte do outro lado descobre que a conexão está inativa, ela também precisa fechar o fluxo de saída para evitar vazamentos de memória.
// código omitido import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) lança Exceção {String host = "localhost"; porta int = 10430; Servidor ServerSocket = novo ServerSocket (porta, 50, InetAddress.getByName (host)); System.out.println ("Servidor iniciado."); Cliente de soquete = novo soquete (host, porta); System.out.println ("Conectando ao servidor…"); Conexão de soquete = server.accept (); System.out.println ("Conexão estabelecida."); // código omitido System.out.println ("A comunicação está pronta."); // código omitido clientOut.close (); serverOut.close (); }}
Etapa 18. Desconexão de log
Para fins de registro, as conexões de impressão para o console foram desconectadas.
// código omitido import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) lança Exceção {String host = "localhost"; porta int = 10430; Servidor ServerSocket = novo ServerSocket (porta, 50, InetAddress.getByName (host)); System.out.println ("Servidor iniciado."); Cliente de soquete = novo soquete (host, porta); System.out.println ("Conectando ao servidor…"); Conexão de soquete = server.accept (); System.out.println ("Conexão estabelecida."); // código omitido System.out.println ("A comunicação está pronta."); // código omitido clientOut.close (); serverOut.close (); System.out.println ("Conexões fechadas."); }}
Etapa 19. Encerre o servidor
As conexões foram desconectadas, mas o servidor ainda está funcionando. Como
ServerSocket
não está associado a nenhum fluxo, ele precisa ser explicitamente fechado chamando
fechar()
método.
// código omitido import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) lança Exceção {String host = "localhost"; porta int = 10430; Servidor ServerSocket = novo ServerSocket (porta, 50, InetAddress.getByName (host)); System.out.println ("Servidor iniciado."); Cliente de soquete = novo soquete (host, porta); System.out.println ("Conectando ao servidor…"); Conexão de soquete = server.accept (); System.out.println ("Conexão estabelecida."); // código omitido System.out.println ("A comunicação está pronta."); // código omitido clientOut.close (); serverOut.close (); System.out.println ("Conexões fechadas."); server.close (); }}
Etapa 20. Terminação do servidor de log
Para fins de registro, a impressão no servidor do console foi encerrada.
// código omitido import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) lança Exceção {String host = "localhost"; porta int = 10430; Servidor ServerSocket = novo ServerSocket (porta, 50, InetAddress.getByName (host)); System.out.println ("Servidor iniciado."); Cliente de soquete = novo soquete (host, porta); System.out.println ("Conectando ao servidor…"); Conexão de soquete = server.accept (); System.out.println ("Conexão estabelecida."); // código omitido System.out.println ("A comunicação está pronta."); // código omitido clientOut.close (); serverOut.close (); System.out.println ("Conexões fechadas."); server.close (); System.out.println ("Servidor encerrado."); }}
Etapa 21. Compile e execute
O registro nos permitiu saber se o aplicativo foi bem-sucedido ou não. Saída esperada:
Servidor iniciado. Conectando ao servidor… Conexão estabelecida. A comunicação está pronta. Mensagem enviada ao servidor: Hello World Mensagem recebida do cliente: Hello World Connections encerrado. Servidor encerrado.
Caso sua saída não seja como a anterior, o que é improvável que aconteça, existem algumas soluções:
-
Se a saída parar na linha
Conexão estabelecida.
e fluxos de objetos são usados, limpe cada
ObjectOutputStream
- imediatamente após a inicialização porque os cabeçalhos, por algum motivo, não foram enviados.
-
Se a saída for impressa
java.net. BindException: Endereço já em uso
- escolha um número de porta diferente porque aquele especificado já está em uso.
Pontas
- A conexão a um servidor em uma rede diferente é feita conectando-se ao endereço IP externo de um dispositivo que executa o servidor que possui uma porta encaminhada.
- A conexão a um servidor na mesma rede é feita conectando-se ao endereço IP privado de um dispositivo que executa o servidor ou encaminhando uma porta e conectando-se ao endereço IP externo do dispositivo.
- Existem softwares, como o Hamachi, que permitem a conexão ao servidor em uma rede diferente sem encaminhar uma porta, mas requer a instalação do software em ambos os dispositivos.
Exemplos
Os aplicativos de rede que usam bloqueio de entrada / saída precisam usar threads. Os exemplos a seguir mostram uma implementação minimalista de servidor e cliente com threads. O código de rede é essencialmente o mesmo do artigo, exceto que alguns fragmentos foram sincronizados, movidos para threads e exceções são tratadas.
Server.java
import java.io. IOException; import java.net. InetAddress; import java.net. ServerSocket; import java.net. SocketException; import java.net. UnknownHostException; import java.util. ArrayList; import java.util. Collections; import java.util. List; / ** * A classe {@code Server} representa um ponto de extremidade do servidor em uma rede. {@code Server} uma vez vinculado a um determinado endereço IP * e porta, estabelece conexões com clientes e é capaz de se comunicar com eles ou desconectá-los. *
* Esta classe é threadsafe. * * @ versão 1.0 * @see Client * @see Connection * / public class Server implementa Runnable {private ServerSocket server; lista privada
conexões; thread de discussão privada; objeto final privado connectionsLock = new Object (); / ** * Constrói um {@code Server} que interage com clientes no nome de host e porta especificados com o comprimento máximo solicitado * especificado de uma fila de clientes de entrada. * * @param host Endereço do host a ser usado. * @param port Número da porta a ser usada. * @param backlog Comprimento máximo solicitado da fila de clientes de entrada. * @throws NetworkException Se ocorrer um erro ao iniciar um servidor. * / public Server (String host, int port, int backlog) lança NetworkException {try {server = new ServerSocket (port, backlog, InetAddress.getByName (host)); } catch (UnknownHostException e) {throw new NetworkException ("Nome do host não pôde ser resolvido:" + host, e); } catch (IllegalArgumentException e) {throw new NetworkException ("O número da porta precisa estar entre 0 e 65535 (inclusive):" + porta); } catch (IOException e) {throw new NetworkException ("O servidor não pôde ser iniciado.", e); } conexões = Collections.synchronizedList (new ArrayList ()); thread = novo Thread (este); thread.start (); } / ** * Constrói um {@code Server} que interage com clientes no nome de host e porta especificados. * * @param host Endereço do host a ser vinculado. * @param port Número da porta para vincular. * @throws NetworkException Se ocorrerem erros ao iniciar um servidor. * / public Server (String host, int port) lança NetworkException {this (host, port, 50); } / ** * Escuta, aceita e registra conexões de entrada de clientes. * / @Override public void run () {while (! Server.isClosed ()) {try {connections.add (new Connection (server.accept ())); } catch (SocketException e) {if (! e.getMessage (). equals ("Socket fechado")) {e.printStackTrace (); }} catch (NetworkException | IOException e) {e.printStackTrace (); }}} / ** * Envia dados para todos os clientes cadastrados. * * @param data Dados a serem enviados. * @throws IllegalStateException Se houver tentativa de gravação de dados quando o servidor estiver offline. * @throws IllegalArgumentException Se os dados a serem enviados forem nulos. * / public void broadcast (Object data) {if (server.isClosed ()) {throw new IllegalStateException ("Dados não enviados, servidor está offline."); } if (dados == nulo) {lançar novo IllegalArgumentException ("dados nulos"); } synchronized (connectionsLock) {for (Connection connection: connections) {try {connection.send (data); System.out.println ("Dados enviados ao cliente com sucesso."); } catch (NetworkException e) {e.printStackTrace (); }}}} / ** * Envia uma mensagem de desconexão e desconecta o cliente especificado. * * @param connection Client para desconectar. * @throws NetworkException Se ocorrer um erro ao fechar a conexão. * / public void disconnect (Connection connection) lança NetworkException {if (connections.remove (connection)) {connection.close (); }} / ** * Envia uma mensagem de desconexão a todos os clientes, desconecta-os e encerra o servidor. * / public void close () lança NetworkException {synchronized (connectionsLock) {para (conexão de conexão: conexões) {try {connection.close (); } catch (NetworkException e) {e.printStackTrace (); }}} connections.clear (); tente {server.close (); } catch (IOException e) {throw new NetworkException ("Erro ao fechar o servidor."); } finalmente {thread.interrupt (); }} / ** * Retorna se o servidor está online ou não. * * @return True se o servidor estiver online. Falso, caso contrário. * / public boolean isOnline () {return! server.isClosed (); } / ** * Retorna uma matriz de clientes registrados. * / public Connection getConnections () {synchronized (connectionsLock) {return connections.toArray (new Connection [connections.size ()]); }}}
Client.java
import java.io. IOException; import java.net. Socket; import java.net. UnknownHostException; / ** * A classe {@code Client} representa um ponto de extremidade do cliente em uma rede. {@code Client}, uma vez conectado a um determinado * servidor, tem a garantia de apenas se comunicar com o servidor. Se outros clientes recebem ou não os dados * depende da implementação do servidor. *
* Esta classe é threadsafe. * * @ versão 1.0 * @see Server * @see Connection * / public class Client {private Connection connection; / ** * Constrói um {@code Client} conectado ao servidor no host e na porta especificados. * * @param host Endereço do host a ser vinculado. * @param port Número da porta para vincular. * @throws NetworkException Se ocorrer um erro ao iniciar um servidor. * / public Client (String host, int port) lança NetworkException {try {connection = new Connection (new Socket (host, port)); } catch (UnknownHostException e) {throw new NetworkException ("Nome do host não pôde ser resolvido:" + host, e); } catch (IllegalArgumentException e) {throw new NetworkException ("O número da porta precisa estar entre 0 e 65535 (inclusive):" + porta); } catch (IOException e) {throw new NetworkException ("O servidor não pôde ser iniciado.", e); }} / ** * Envia dados para a outra parte. * * @param data Dados a serem enviados. * @throws NetworkException Se a gravação no fluxo de saída falhar. * @throws IllegalStateException Se a gravação de dados for tentada quando a conexão for fechada. * @throws IllegalArgumentException Se os dados a serem enviados forem nulos. * @throws UnsupportedOperationException Se houver tentativa de envio de tipo de dados sem suporte. * / public void send (Object data) lança NetworkException {connection.send (data); } / ** * Envia uma mensagem de desconexão e fecha a conexão com o servidor. * / public void close () lança NetworkException {connection.close (); } / ** * Retorna se o cliente está ou não conectado ao servidor. * * @return True se o cliente estiver conectado. Falso, caso contrário. * / public boolean isOnline () {return connection.isConnected (); } / ** * Retorna a instância {@link Connection} do cliente. * / conexão pública getConnection () {conexão de retorno; }}
Connection.java
import java.io. DataInputStream; import java.io. DataOutputStream; import java.io. IOException; import java.net. Socket; import java.net. SocketException; / ** * A classe {@code Connection} representa uma conexão do servidor ao cliente ou um ponto final do cliente em uma rede * {@code Connection}, uma vez conectado, é capaz de trocar dados com outra parte ou partes, dependendo em uma implementação de servidor *. *
* Esta classe é threadsafe. * * @ versão 1.0 * @see Server * @see Client * / public class Connection implementa Runnable {private Socket socket; saída de DataOutputStream privada; Private DataInputStream in; thread de discussão privada; objeto final privado writeLock = new Object (); Objeto final privado readLock = new Object (); / ** * Constrói {@code Connection} usando streams de um {@link Socket} especificado. * * @param socket Soquete de onde buscar os streams.* / public Connection (Socket socket) throws NetworkException {if (socket == null) {throw new IllegalArgumentException ("null socket"); } this.socket = socket; experimente {out = new DataOutputStream (socket.getOutputStream ()); } catch (IOException e) {throw new NetworkException ("Não foi possível acessar o fluxo de saída.", e); } tente {in = new DataInputStream (socket.getInputStream ()); } catch (IOException e) {throw new NetworkException ("Não foi possível acessar o fluxo de entrada.", e); } tópico = novo Tópico (este); thread.start (); } / ** * Lê mensagens enquanto a conexão com a outra parte está ativa. * / @Override public void run () {while (! Socket.isClosed ()) {try {int identifier; byte bytes; sincronizado (readLock) {identificador = in.readInt (); comprimento interno = in.readInt (); if (comprimento> 0) {bytes = novo byte [comprimento]; in.readFully (bytes, 0, bytes.length); } else {continue; }} switch (identificador) {case Identifier. INTERNAL: String command = new String (bytes); if (command.equals ("desconectar")) {if (! socket.isClosed ()) {System.out.println ("Pacote de desconexão recebido."); tente {fechar (); } catch (NetworkException e) {return; } } } pausa; Case Identifier. TEXT: System.out.println ("Mensagem recebida:" + nova String (bytes)); pausa; padrão: System.out.println ("Dados não reconhecidos recebidos."); }} catch (SocketException e) {if (! e.getMessage (). equals ("Socket fechado")) {e.printStackTrace (); }} catch (IOException e) {e.printStackTrace (); }}} / ** * Envia dados para a outra parte. * * @param data Dados a serem enviados. * @throws NetworkException Se a gravação no fluxo de saída falhar. * @throws IllegalStateException Se a gravação de dados for tentada quando a conexão for fechada. * @throws IllegalArgumentException Se os dados a serem enviados forem nulos. * @throws UnsupportedOperationException Se houver tentativa de envio de tipo de dados sem suporte. * / public void send (Object data) throws NetworkException {if (socket.isClosed ()) {throw new IllegalStateException ("Dados não enviados, conexão fechada."); } if (dados == nulo) {lançar novo IllegalArgumentException ("dados nulos"); } identificador interno; byte bytes; if (instância de dados String) {identificador = Identificador. TEXT; bytes = ((String) dados).getBytes (); } else {throw new UnsupportedOperationException ("Tipo de dados não suportado:" + data.getClass ()); } tente {synchronized (writeLock) {out.writeInt (identifier); out.writeInt (bytes.length); out.write (bytes); out.flush (); }} catch (IOException e) {throw new NetworkException ("Os dados não puderam ser enviados.", e); }} / ** * Envia uma mensagem de desconexão e fecha a conexão com a outra parte. * / public void close () lança NetworkException {if (socket.isClosed ()) {lança nova IllegalStateException ("A conexão já está fechada."); } tente {byte mensagem = "desconectar".getBytes (); sincronizado (writeLock) {out.writeInt (Identifier. INTERNAL); out.writeInt (message.length); out.write (mensagem); out.flush (); }} catch (IOException e) {System.out.println ("Mensagem de desconexão não pôde ser enviada."); } tente {synchronized (writeLock) {out.close (); }} catch (IOException e) {throw new NetworkException ("Erro ao fechar a conexão.", e); } finalmente {thread.interrupt (); }} / ** * Retorna se a conexão com a outra parte está ativa ou não. * * @return True se a conexão estiver ativa. Falso, caso contrário. * / public boolean isConnected () {return! socket.isClosed (); }}
Identifier.java
/ ** * A classe {@code Identifier} contém constantes usadas por {@link Connection} para serializar e desserializar os dados * enviados pela rede. * * @ versão 1.0 * @ver Connection * / public final class Identifier {/ ** * Identificador para mensagens internas. * / public static final int INTERNAL = 1; / ** * Identificador para mensagens textuais. * / public static final int TEXT = 2; }
NetworkException.java
/ ** * A classe {@code NetworkException} indica um erro relacionado à rede. * / public class NetworkException extends Exception {/ ** * Constrói uma {@code NetworkException} com {@code null} como sua mensagem. * / public NetworkException () {} / ** * Constrói uma {@code NetworkException} com a mensagem especificada. * * @param message Uma mensagem para descrever o erro. * / public NetworkException (String mensagem) {super (mensagem); } / ** * Constrói uma {@code NetworkException} com a mensagem e a causa especificadas. * * @param message Uma mensagem para descrever o erro. * @param cause A causa do erro. * / public NetworkException (String mensagem, Throwable cause) {super (mensagem, causa); } / ** * Constrói uma {@code NetworkException} com a causa especificada. * * @param cause A causa do erro. * / public NetworkException (Throwable cause) {super (cause); }}
UsageExample.java
/ ** * A classe {@code UsageExample} mostra o uso de {@link Server} e {@link Client}. Este exemplo usa * {@link Thread # sleep (long)} para garantir que cada segmento seja executado, pois o início e o fechamento rápidos fazem com que alguns * segmentos não sejam executados. * * @ versão 1.0 * @see Server * @see Client * / public class UsageExample {public static void main (String args) lança Exceção {String host = "localhost"; porta int = 10430; Servidor servidor = novo servidor (host, porta); Cliente cliente = novo cliente (host, porta); Thread.sleep (100L); client.send ("Olá."); server.broadcast ("Ei, cara!"); Thread.sleep (100L); server.disconnect (server.getConnections () [0]); // ou client.close () para desconectar do lado do cliente server.close (); }}