Trabalhando com interfaces de rede em Java
1. Visão geral
Neste artigo, vamos nos concentrar nas interfaces de rede e como acessá-las de maneira programática em Java.
Simplificando, anetwork interface is the point of interconnection between a device and any of its network connections.
Na linguagem do dia-a-dia, nos referimos a eles pelo termo Network Interface Cards (NICs) - mas nem todos precisam ter a forma de hardware.
Por exemplo, o popular localhost IP127.0.0.1, que usamos muito para testar aplicativos da web e de rede, é a interface de loopback - que não é uma interface direta de hardware.
Obviamente, os sistemas geralmente têm várias conexões de rede ativas, como Ethernet com fio, WIFI, Bluetooth, etc.
Em Java, a principal API que podemos usar para interagir diretamente com eles é a classejava.net.NetworkInterface. E assim, para começar rapidamente, vamos importar o pacote completo:
import java.net.*;
2. Por que acessar interfaces de rede?
A maioria dos programas Java provavelmente não interagirá com eles diretamente; no entanto, existem cenários especiais em que precisamos desse tipo de acesso de baixo nível.
O mais notável deles é quando um sistema tem vários cartões e você gostaria de terfreedom to choose a specific interface to use a socket with. Nesse cenário, geralmente sabemos o nome, mas não necessariamente o endereço IP.
Normalmente, quando queremos fazer uma conexão de soquete com um endereço de servidor específico:
Socket socket = new Socket();
socket.connect(new InetSocketAddress(address, port));
Dessa forma, o sistema escolherá um endereço local adequado, vinculará a ele e se comunicará com o servidor por meio de sua interface de rede. No entanto, essa abordagem não nos permite escolher a nossa.
Faremos uma suposição aqui; não sabemos o endereço, mas sabemos o nome. Apenas para fins de demonstração, vamos supor que queremos a conexão pela interface de loopback, por convenção, seu nome élo, pelo menos nos sistemas Linux e Windows, no OSX élo0:
NetworkInterface nif = NetworkInterface.getByName("lo");
Enumeration nifAddresses = nif.getInetAddresses();
Socket socket = new Socket();
socket.bind(new InetSocketAddress(nifAddresses.nextElement(), 0));
socket.connect(new InetSocketAddress(address, port));
Portanto, recuperamos a interface de rede conectada alo primeiro, recuperamos os endereços anexados a ela, criamos um soquete, vinculamos a qualquer um dos endereços enumerados que nem conhecemos em tempo de compilação e, em seguida, conectamos.
Um objetoNetworkInterface contém um nome e um conjunto de endereços IP atribuídos a ele. Portanto, a ligação a qualquer um desses endereços garantirá a comunicação por essa interface.
Isso realmente não diz nada de especial sobre a API. Sabemos que, se quisermos que nosso endereço local seja host local, o primeiro trecho seria suficiente se apenas adicionássemos o código de ligação.
Além disso, nunca teríamos realmente que passar por todas as várias etapas, já que localhost tem um endereço conhecido,127.0.0.1e podemos facilmente ligar o socket a ele.
No entanto, no seu caso,lo poderia talvez ter representado outras interfaces como Bluetooth -net1, rede sem fio -net0 ou ethernet -eth0. Nesses casos, você não saberia o endereço IP no momento da compilação.
3. Recuperando Interfaces de Rede
Nesta seção, exploraremos as outras APIs disponíveis para recuperar as interfaces disponíveis. Na seção anterior, vimos apenas uma dessas abordagens; o método estáticogetByName().
É importante notar que a classeNetworkInterface não tem nenhum construtor público, então é claro que não podemos criar uma nova instância. Em vez disso, usaremos as APIs disponíveis para recuperar um.
A API que examinamos até agora é usada para pesquisar uma interface de rede com o nome especificado:
@Test
public void givenName_whenReturnsNetworkInterface_thenCorrect() {
NetworkInterface nif = NetworkInterface.getByName("lo");
assertNotNull(nif);
}
Ele retornanull se nenhum for para o nome:
@Test
public void givenInExistentName_whenReturnsNull_thenCorrect() {
NetworkInterface nif = NetworkInterface.getByName("inexistent_name");
assertNull(nif);
}
A segunda API égetByInetAddress(), ela também requer que forneçamos um parâmetro conhecido, desta vez podemos fornecer o endereço IP:
@Test
public void givenIP_whenReturnsNetworkInterface_thenCorrect() {
byte[] ip = new byte[] { 127, 0, 0, 1 };
NetworkInterface nif = NetworkInterface.getByInetAddress(
InetAddress.getByAddress(ip));
assertNotNull(nif);
}
Ou nome do host:
@Test
public void givenHostName_whenReturnsNetworkInterface_thenCorrect() {
NetworkInterface nif = NetworkInterface.getByInetAddress(
InetAddress.getByName("localhost"));
assertNotNull(nif);
}
Ou se você é específico sobre o host local:
@Test
public void givenLocalHost_whenReturnsNetworkInterface_thenCorrect() {
NetworkInterface nif = NetworkInterface.getByInetAddress(
InetAddress.getLocalHost());
assertNotNull(nif);
}
Outra alternativa é também usar explicitamente a interface de loopback:
@Test
public void givenLoopBack_whenReturnsNetworkInterface_thenCorrect() {
NetworkInterface nif = NetworkInterface.getByInetAddress(
InetAddress.getLoopbackAddress());
assertNotNull(nif);
}
A terceira abordagem disponível apenas desde o Java 7 é obter uma interface de rede por seu índice:
NetworkInterface nif = NetworkInterface.getByIndex(int index);
A abordagem final envolve o uso da APIgetNetworkInterfaces. Ele retorna umEnumeration de todas as interfaces de rede disponíveis no sistema. Cabe a nós recuperar os objetos retornados em um loop, o idioma padrão usa umList:
Enumeration nets = NetworkInterface.getNetworkInterfaces();
for (NetworkInterface nif: Collections.list(nets)) {
//do something with the network interface
}
4. Parâmetros de interface de rede
Há muitas informações valiosas que podemos obter de uma após recuperar seu objeto. One of the most useful is the list of IP addresses assigned to it.
Podemos obter endereços IP usando duas APIs. A primeira API égetInetAddresses(). Ele retorna umEnumeration deInetAddress instâncias que podemos processar como consideramos adequado:
@Test
public void givenInterface_whenReturnsInetAddresses_thenCorrect() {
NetworkInterface nif = NetworkInterface.getByName("lo");
Enumeration addressEnum = nif.getInetAddresses();
InetAddress address = addressEnum.nextElement();
assertEquals("127.0.0.1", address.getHostAddress());
}
A segunda API égetInterfaceAddresses(). Ele retornaList deInterfaceAddress instâncias que são mais poderosas do queInetAddress instâncias. Por exemplo, além do endereço IP, você pode estar interessado no endereço de transmissão:
@Test
public void givenInterface_whenReturnsInterfaceAddresses_thenCorrect() {
NetworkInterface nif = NetworkInterface.getByName("lo");
List addressEnum = nif.getInterfaceAddresses();
InterfaceAddress address = addressEnum.get(0);
InetAddress localAddress=address.getAddress();
InetAddress broadCastAddress = address.getBroadcast();
assertEquals("127.0.0.1", localAddress.getHostAddress());
assertEquals("127.255.255.255",broadCastAddress.getHostAddress());
}
Podemos acessar parâmetros de rede sobre uma interface além do nome e endereços IP atribuídos a ela. Para verificar se está em funcionamento:
@Test
public void givenInterface_whenChecksIfUp_thenCorrect() {
NetworkInterface nif = NetworkInterface.getByName("lo");
assertTrue(nif.isUp());
}
Para verificar se é uma interface de loopback:
@Test
public void givenInterface_whenChecksIfLoopback_thenCorrect() {
NetworkInterface nif = NetworkInterface.getByName("lo");
assertTrue(nif.isLoopback());
}
Para verificar se representa uma conexão de rede ponto a ponto:
@Test
public void givenInterface_whenChecksIfPointToPoint_thenCorrect() {
NetworkInterface nif = NetworkInterface.getByName("lo");
assertFalse(nif.isPointToPoint());
}
Ou se for uma interface virtual:
@Test
public void givenInterface_whenChecksIfVirtual_thenCorrect() {
NetworkInterface nif = NetworkInterface.getByName("lo");
assertFalse(nif.isVirtual());
}
Para verificar se o multicasting é suportado:
@Test
public void givenInterface_whenChecksMulticastSupport_thenCorrect() {
NetworkInterface nif = NetworkInterface.getByName("lo");
assertTrue(nif.supportsMulticast());
}
Ou para recuperar seu endereço físico, geralmente chamado de endereço MAC:
@Test
public void givenInterface_whenGetsMacAddress_thenCorrect() {
NetworkInterface nif = NetworkInterface.getByName("lo");
byte[] bytes = nif.getHardwareAddress();
assertNotNull(bytes);
}
Outro parâmetro é a unidade máxima de transmissão, que define o maior tamanho de pacote que pode ser transmitido através desta interface:
@Test
public void givenInterface_whenGetsMTU_thenCorrect() {
NetworkInterface nif = NetworkInterface.getByName("net0");
int mtu = nif.getMTU();
assertEquals(1500, mtu);
}
5. Conclusão
Neste artigo, mostramos as interfaces de rede, como acessá-las programaticamente e por que precisamos acessá-las.
O código-fonte completo e os exemplos usados neste artigo estão disponíveis emGithub project.