A sigla IP, que significa Internet Protocol ou Protocolo de Internet, é um identificador de um dispositivo em uma rede local ou pública, e neste artigo estudaremos a extração de informações dado um número de IP qualquer.

Um número de IP contém diversas informações que podem ser obtidas através de vários cálculos, tais como: Máscara, Rede, Classe e etc. O objetivo deste artigo é demonstrar como realizar a extração dessas informações em Java e mostrar ao usuário de uma forma amigável e rápida. Principalmente para quem esta cursando matérias relacionadas a Rede de Computadores, na Universidade, este será um programa muito bem-vindo.

Desenvolvimento da Interface (GUI)

O artigo em questão é focado na linguagem Java e estamos partindo do princípio que você, caro leitor, conhece as teorias básicas de Rede de Computadores (Máscaras, Rede, Broadcast, Classes de IP e etc.) pois não entraremos nesses méritos que fugiria do foco do artigo.

Primeiro veja como ficará nossa interface (Figura 1).

Interface
Figura 1. Interface

O que temos acima é um Jframe que possui um campo para inserção do número IP e outro campo para mostrar o resultado das informações extraídas ao usuário. O campo IP está com o valor todo zerado pois usamos um Placeholder afim de não deixar espaços em branco, confundindo o usuário. O processo é simples: O usuário preenche o número de IP clica em processar e nosso programa irá mostrar as informações extraídas dentro do campo Resultado.

Para você que irá construir a interface acima no Netbeans, disponibilizamos Listagem 1 o nosso arquivo Form para que você pode ter a mesma interface como mostrada na Figura 1.


<?xml version="1.0" encoding="UTF-8" ?>
 
<Form version="1.3" type="org.netbeans.modules.form.forminfo.JFrameFormInfo">
  <Properties>
    <Property name="defaultCloseOperation" type="int" value="3"/>
  </Properties>
  <SyntheticProperties>
    <SyntheticProperty name="formSizePolicy" type="int" value="1"/>
  </SyntheticProperties>
  <AuxValues>
    <AuxValue name="FormSettings_generateMnemonicsCode" 
    type="java.lang.Boolean" value="false"/>
    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
    <AuxValue name="FormSettings_listenerGenerationStyle" 
    type="java.lang.Integer" value="0"/>
    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
  </AuxValues>
 
  <Layout>
    <DimensionLayout dim="0">
      <Group type="103" groupAlignment="0" attributes="0">
          <Group type="102" attributes="0">
              <EmptySpace max="-2" attributes="0"/>
              <Group type="103" groupAlignment="0" attributes="0">
                  <Component id="jScrollPane1" alignment="0" min="-2" 
                  pref="364" max="-2" attributes="0"/>
                  <Component id="jFormattedTextFieldIP" alignment="0" 
                  min="-2" pref="154" max="-2" attributes="0"/>
                  <Component id="jLabel1" alignment="0" min="-2" max="-2" attributes="0"/>
                  <Component id="jLabel2" alignment="0" min="-2" max="-2" attributes="0"/>
                  <Component id="jButtonProcessar" alignment="0" min="-2" max="-2" 
                  attributes="0"/>
              </Group>
              <EmptySpace pref="24" max="32767" attributes="0"/>
          </Group>
      </Group>
    </DimensionLayout>
    <DimensionLayout dim="1">
      <Group type="103" groupAlignment="0" attributes="0">
          <Group type="102" alignment="0" attributes="0">
              <EmptySpace max="-2" attributes="0"/>
              <Component id="jLabel1" min="-2" max="-2" attributes="0"/>
              <EmptySpace max="-2" attributes="0"/>
              <Component id="jFormattedTextFieldIP" min="-2" max="-2" attributes="0"/>
              <EmptySpace min="-2" pref="20" max="-2" attributes="0"/>
              <Component id="jLabel2" min="-2" max="-2" attributes="0"/>
              <EmptySpace max="-2" attributes="0"/>
              <Component id="jScrollPane1" min="-2" pref="116" max="-2" attributes="0"/>
              <EmptySpace min="-2" pref="14" max="-2" attributes="0"/>
              <Component id="jButtonProcessar" min="-2" max="-2" attributes="0"/>
              <EmptySpace max="32767" attributes="0"/>
          </Group>
      </Group>
    </DimensionLayout>
  </Layout>
  <SubComponents>
    <Component class="javax.swing.JLabel" name="jLabel1">
      <Properties>
        <Property name="text" type="java.lang.String" value="IP"/>
      </Properties>
    </Component>
    <Component class="javax.swing.JFormattedTextField" name="jFormattedTextFieldIP">
    </Component>
    <Component class="javax.swing.JLabel" name="jLabel2">
      <Properties>
        <Property name="text" type="java.lang.String" value="Resultado"/>
      </Properties>
    </Component>
    <Container class="javax.swing.JScrollPane" name="jScrollPane1">
      <AuxValues>
        <AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
      </AuxValues>
 
      <Layout 
      class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
      <SubComponents>
        <Component class="javax.swing.JTextArea" name="jTextAreaResultado">
          <Properties>
            <Property name="columns" type="int" value="20"/>
            <Property name="rows" type="int" value="5"/>
          </Properties>
        </Component>
      </SubComponents>
    </Container>
    <Component class="javax.swing.JButton" name="jButtonProcessar">
      <Properties>
        <Property name="text" type="java.lang.String" value="Processar"/>
      </Properties>
      <Events>
        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" 
        parameters="java.awt.event.ActionEvent" handler="jButtonProcessarActionPerformed"/>
      </Events>
    </Component>
  </SubComponents>
</Form>
Listagem 1. FInfoIP.form

Desenvolvimento da Lógica (Core)

A próxima etapa é começar a definir a lógica do nosso programa. Iremos mostrar passo a passo a construção da nossa lógica e depois uniremos tudo em uma só classe para facilitar a compreensão e aprendizagem. Observe a Listagem 2.


public class FInfoIP extends javax.swing.JFrame {
       
       //1
       private enum Classe{A,B,C,D,E};
       //2
       private Classe classeIP;
       //3
       private int[] ip = new int[4];
 
…
 
}
Listagem 2. Atributos de instância

Logo abaixo da declaração da nossa classe FinfoIP, definimos alguns atributos de instância que irão nos ajudar a construir a lógica mais à frente. Vejamos: No item 1 temos um enum chamado Classe que possui as classes IP possíveis, no item 2 temos um atributo classeIP do tipo Classe (enum definido no item 1) que armazenará a classe à qual o IP digitado pertence e por fim na linha 3 temos um vetor de inteiros que possui quatro posições, onde cada posição armazenará um octeto do endereço de IP. Agora observe a Listagem 3.


public FInfoIP() {
  initComponents();
 
  
  jFormattedTextFieldIP.setFormatterFactory(new AbstractFormatterFactory() {
    
    @Override
    public AbstractFormatter getFormatter(JFormattedTextField tf) {
      try {
        MaskFormatter mask = new MaskFormatter("#**.#**.#**.#**");
        mask.setValidCharacters("0123456789");
        mask.setPlaceholderCharacter('0');
        return mask;
      } catch (ParseException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      }
      return null;
    }
  });
}
Listagem 3. Construtor

Nosso construtor acima possui duas seções importantes, onde a primeira é a chamada ao método initComponents() que inicializa toda a interface gráfica do nosso formulário e a segunda seção diz respeito a criação de uma máscara para o campo onde será digitado o IP.

O método setFormatterFactory() recebe como parâmetro a instância da classe AbstractFormatterFactory. O que fizemos foi criar de forma anônima e implementar um comportamento para o método getFormatter(). Dentro do método getFormatter() nós criamos um objeto do tipo MaskFormatter e configuramos os caracteres válidos para a máscara e um placeholderCharacter que é o caractere que será colocado quando não for digitado nada em algum local da máscara, por isso no início todos os valores estão zerados.

A ação de clicar no botão “Processar”, mostrado na Figura 1, irá disparar o método mostrado na Listagem 4.


private void jButtonProcessarActionPerformed(java.awt.event.ActionEvent evt) {
 try{
 jTextAreaResultado.removeAll();
 String ipTmp = jFormattedTextFieldIP.getText().trim();
 
 if (ipTmp.replace(".", "").isEmpty()){
       JOptionPane.showMessageDialog(this, "Digite um IP válido");              
 }
 
 constroiIpAsArray(ipTmp);
 extraiClasse();
 extraiMascaraRedeEBroadcast();
 }catch(Exception e){
       e.printStackTrace();
       JOptionPane.showMessageDialog(this, e.getMessage());
 }
 
}
Listagem 4. Click do Botão Processar

Perceba que circundamos todo nosso código com um block try-catch, dessa forma qualquer erro que ocorrer será mostrado ao usuário. Antes de iniciar qualquer coisa nós limpamos o jTextAreaResultado para garantir que não há nada lá, evitando confusão de informações. No bloco “if” nós checamos se o valor não é vazio, ou seja, se foi digitado algum IP antes de clicar em processar.

Mais adiante temos os métodos que fazem a nossa lógica funcionar, são eles: constroiIpAsArray, extraiClasse, extraiMascaraRedeEBroadcast.


private void constroiIpAsArray(String ipAsString){
 String[] ipAsStringArray = ipAsString.split("\\.");
 for(int i = 0; i < 4; i++){
       ip[i] = Integer.parseInt(ipAsStringArray[i].trim());
 }
 
 addInfo("IP: "+ipAsString);
}
Listagem 5. constroiIpAsArray

Quando o usuário digita o IP no campo de texto este IP é uma String com pontos entre os octetos que precisamos, então o método mostrado na Listagem 5 irá extrair os quatro octetos entre os pontos da String e colocar no array de inteiros chamado ip que declaramos na Listagem 2. Trabalhar com os quatro octetos separados por um vetor e em inteiros torna-se muito mais fácil do que trabalhar direto com String.


private void extraiClasse(){
  if (ip[0] >= 1 && ip[0] <= 127){
   addInfo("Classe: A");
   classeIP = Classe.A;
  }else if (ip[0] >= 128 && ip[0] <= 191){
   addInfo("Classe: B");
   classeIP = Classe.B;
  }else if (ip[0] >= 192 && ip[0] <= 223){
   addInfo("Classe: C");
   classeIP = Classe.C;
  }else if (ip[0] >= 224 && ip[0] <= 239){
   addInfo("Classe: D [Reservado para Multicast]");
   classeIP = Classe.D;
  }else if (ip[0] >= 240 && ip[0] <= 255){
   addInfo("Classe: E [Reservado para pesquisas]");
   classeIP = Classe.E;
  }else{
   JOptionPane.showMessageDialog(this, "O IP digitado é inválido, pois não possui nenhum classe");
   throw new RuntimeException("O IP digitado é inválido, pois não possui nenhum classe");
 }
}
Listagem 6. extraiClasse

De posse dos quatro octetos separados por um vetor de inteiros, fica simples identificar a classe do IP digitado. O que nós fazemos é pegar o primeiro octeto (int[0]) e checar qual o range dele, ou seja, entre que valores ele se encontra. As classes só podem ser: A, B, C, D, E então o primeiro octeto digitado não esteja em nenhuma dessas classes o IP é inválido e o programa não pode continuar executando a lógica.

Ainda na Listagem 6 toda vez que encontramos a classe para o IP digitado nós atribuímos um valor do enum Classe para o atributo classeIP, pois iremos usá-lo mais à frente.

O método addInfo() que estamos utilizando com constância é apenas um auxiliar para evitar código repetido, como mostra a Listagem 7.


private void addInfo(String info){
   jTextAreaResultado.append(info + "\n");
}
Listagem 7. addInfo

Evitamos assim ficar digitando a toda hora o append(...), além de usarmos o conceito de encapsulamento.


private void extraiMascaraRedeEBroadcast(){
   if (classeIP.equals(Classe.A)){
          addInfo("Máscara: 255.0.0.0");
          addInfo("Rede: "+ip[0]+".0.0.0");
          addInfo("Broadcast: "+ip[0]+".255.255.255");
   }else if (classeIP.equals(Classe.B)){
         addInfo("Máscara: 255.255.0.0");
         addInfo("Rede: "+ip[0]+"."+ip[1]+".0.0");
         addInfo("Broadcast: "+ip[0]+"."+ip[1]+".255.255");
   }else if (classeIP.equals(Classe.C)){
         addInfo("Máscara: 255.255.255.0");
         addInfo("Rede: "+ip[0]+"."+ip[1]+"."+ip[2]+".0");
         addInfo("Broadcast: "+ip[0]+"."+ip[1]+"."+ip[2]+".255");
   }      
}
Listagem 8. extraiMascaraRedeEBroadcast

Por fim, na Listagem 8 nós verificamos qual foi a classe de IP encontrada e adicionamos as informações de Máscara, Rede e Broadcast. Perceba que aqui fazemos uso dos três octetos, se necessário.


import java.text.ParseException;

import javax.swing.JFormattedTextField;
import javax.swing.JFormattedTextField.AbstractFormatter;
import javax.swing.JFormattedTextField.AbstractFormatterFactory;
import javax.swing.JOptionPane;
import javax.swing.text.MaskFormatter;

public class FInfoIP extends javax.swing.JFrame {
       
       private enum Classe{A,B,C,D,E};
       private Classe classeIP;
       private int[] ip = new int[4];
  
  /** Creates new form FInfoIP */
  public FInfoIP() {
      initComponents();
     
      
      jFormattedTextFieldIP.setFormatterFactory(new AbstractFormatterFactory() {
                          
    @Override
    public AbstractFormatter getFormatter(JFormattedTextField tf) {
      try {
         MaskFormatter mask = new MaskFormatter("#**.#**.#**.#**");
         mask.setValidCharacters("0123456789");
         mask.setPlaceholderCharacter('0');
         return mask;
      } catch (ParseException e) {
         // TODO Auto-generated catch block
         e.printStackTrace();
      }
      return null;
    }
    });
  }
  
  /** This method is called from within the constructor to
   * initialize the form.
   * WARNING: Do NOT modify this code. The content of this method is
   * always regenerated by the Form Editor.
   */
  // <editor-fold defaultstate="collapsed" desc=" Código Gerado "
  >//GEN-BEGIN:initComponents
  private void initComponents() {
      jLabel1 = new javax.swing.JLabel();
      jFormattedTextFieldIP = new javax.swing.JFormattedTextField();
      jLabel2 = new javax.swing.JLabel();
      jScrollPane1 = new javax.swing.JScrollPane();
      jTextAreaResultado = new javax.swing.JTextArea();
      jButtonProcessar = new javax.swing.JButton();

      setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
      jLabel1.setText("IP");

      jLabel2.setText("Resultado");

      jTextAreaResultado.setColumns(20);
      jTextAreaResultado.setRows(5);
      jScrollPane1.setViewportView(jTextAreaResultado);

      jButtonProcessar.setText("Processar");
      jButtonProcessar.addActionListener(new java.awt.event.ActionListener() {
          public void actionPerformed(java.awt.event.ActionEvent evt) {
              jButtonProcessarActionPerformed(evt);
          }
      });

      javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
      getContentPane().setLayout(layout);
      layout.setHorizontalGroup(
          layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
          .addGroup(layout.createSequentialGroup()
              .addContainerGap()
              .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                  .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 364,
                   javax.swing.GroupLayout.PREFERRED_SIZE)
                  .addComponent(jFormattedTextFieldIP, javax.swing.GroupLayout.PREFERRED_SIZE,
                   154, javax.swing.GroupLayout.PREFERRED_SIZE)
                  .addComponent(jLabel1)
                  .addComponent(jLabel2)
                  .addComponent(jButtonProcessar))
              .addContainerGap(24, Short.MAX_VALUE))
      );
      layout.setVerticalGroup(
          layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
          .addGroup(layout.createSequentialGroup()
              .addContainerGap()
              .addComponent(jLabel1)
              .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
              .addComponent(jFormattedTextFieldIP, javax.swing.GroupLayout.PREFERRED_SIZE, 
              javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
              .addGap(20, 20, 20)
              .addComponent(jLabel2)
              .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
              .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 116, 
              javax.swing.GroupLayout.PREFERRED_SIZE)
              .addGap(14, 14, 14)
              .addComponent(jButtonProcessar)
              .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
      );
      pack();
  }// </editor-fold>//GEN-END:initComponents

  private void jButtonProcessarActionPerformed(java.awt.event.ActionEvent evt) 
  {//GEN-FIRST:event_jButtonProcessarActionPerformed
       try{
       jTextAreaResultado.removeAll();
       String ipTmp = jFormattedTextFieldIP.getText().trim();
       
       if (ipTmp.replace(".", "").isEmpty()){
                 JOptionPane.showMessageDialog(this, "Digite um IP 
                 válido");                       
       }
       
       constroiIpAsArray(ipTmp);
       extraiClasse();
       extraiMascaraRedeEBroadcast();
       }catch(Exception e){
                 e.printStackTrace();
                 JOptionPane.showMessageDialog(this, e.getMessage());
       }
       
  }//GEN-LAST:event_jButtonProcessarActionPerformed
  
  private void constroiIpAsArray(String ipAsString){
       String[] ipAsStringArray = ipAsString.split("\\.");
       for(int i = 0; i < 4; i++){
                 ip[i] = Integer.parseInt(ipAsStringArray[i].trim());
       }
       
       addInfo("IP: "+ipAsString);
  }    
  
  private void extraiClasse(){
       if (ip[0] >= 1 && ip[0] <= 127){
                 addInfo("Classe: A");
                 classeIP = Classe.A;
       }else if (ip[0] >= 128 && ip[0] <= 191){
                 addInfo("Classe: B");
                 classeIP = Classe.B;
       }else if (ip[0] >= 192 && ip[0] <= 223){
                 addInfo("Classe: C");
                 classeIP = Classe.C;
       }else if (ip[0] >= 224 && ip[0] <= 239){
                 addInfo("Classe: D [Reservado para Multicast]");
                 classeIP = Classe.D;
       }else if (ip[0] >= 240 && ip[0] <= 255){
                 addInfo("Classe: E [Reservado para pesquisas]");
                 classeIP = Classe.E;
       }else{
                 JOptionPane.showMessageDialog(this, "O IP digitado é inválido, 
                 pois não possui nenhum classe");
                 throw new RuntimeException("O IP digitado é inválido, pois não 
                 possui nenhum classe");
       }
  }
  
  private void extraiMascaraRedeEBroadcast(){
       if (classeIP.equals(Classe.A)){
                addInfo("Máscara: 255.0.0.0");
                addInfo("Rede: "+ip[0]+".0.0.0");
                addInfo("Broadcast: "+ip[0]+".255.255.255");
       }else if (classeIP.equals(Classe.B)){
                 addInfo("Máscara: 255.255.0.0");
                 addInfo("Rede: "+ip[0]+"."+ip[1]+".0.0");
                 addInfo("Broadcast: "+ip[0]+"."+ip[1]+".255.255");
       }else if (classeIP.equals(Classe.C)){
                 addInfo("Máscara: 255.255.255.0");
                 addInfo("Rede: "+ip[0]+"."+ip[1]+"."+ip[2]+".0");
                 addInfo("Broadcast: "+ip[0]+"."+ip[1]+"."+ip[2]+".255");
       }        
  }
  
  private void addInfo(String info){
       jTextAreaResultado.append(info + "\n");
  }
  
  
  
  /**
   * @param args the command line arguments
   */
  public static void main(String args[]) {
      java.awt.EventQueue.invokeLater(new Runnable() {
          public void run() {
              new FInfoIP().setVisible(true);
          }
      });
  }
  
  // Declaração de variáveis - não modifique//GEN-BEGIN:variables
  private javax.swing.JButton jButtonProcessar;
  private javax.swing.JFormattedTextField jFormattedTextFieldIP;
  private javax.swing.JLabel jLabel1;
  private javax.swing.JLabel jLabel2;
  private javax.swing.JScrollPane jScrollPane1;
  private javax.swing.JTextArea jTextAreaResultado;
  // Fim da declaração de variáveis//GEN-END:variables
  
}
Listagem 9. FinfoIP.java completo

A Listagem 9 possui a implementação completa de todos os métodos explicados nas seções acima. Com ela você poderá treinar e testar a lógica ministrada.

Proposta de Atividade

Dado o desenvolvimento do programa proposto é perceptível que o mesmo pode e deve sofrer algumas melhorias para tornar-se ainda mais útil. Propomos a você, caro leitor, o desenvolvimento dessas melhorias como forma de praticar e aperfeiçoar suas habilidades. Vejamos a lista de melhorias propostas:

  1. Possibilitar que cada octeto do endereço IP possa ser digitado de forma independente, possibilitando o uso da tecla TAB para “pular” entre octetos. Dica: Usar vários campos de texto, um para cada octeto.
  2. Possibilitar que o usuário informe uma máscara de sub-rede customizada (ex: 255.255.255.240), dessa forma o cálculo para obter as outras informações irá mudar. Dica: Utilizar a classe BitSet para realizar operações lógicas entre a máscara e o IP para obter informações pertinentes ao usuário.

Com este artigo foi possível aprender um pouco mais sobre a linguagem Java, aplicada a problemas reais, usando técnicas como: Enumeração, Máscara de Valores, Vetorização de valores e etc. Além disso, propomos a implementação de melhorias no programa proposta, afim de aperfeiçoar as habilidades do leitor e forçando o mesmo a praticar as listagens demonstradas.