Socket - não recebe a mensagem por completo

Tenho esses códigos:
Estou fazendo testes ainda.

Sistema cliente:

private void btComunicarActionPerformed(java.awt.event.ActionEvent evt) {                                            
        List<PessoaMOD> pessoas = new ArrayList<PessoaMOD>();

        for (int i = 1; i <= 400; i++) {
            pessoas.add(new PessoaMOD(i,
                    "Pessoa " + i,
                    "111.111.111.11",
                    "11.111.111-1",
                    "Rua Violetas " + i,
                    "Numero " + i,
                    "Complemento " + i,
                    "Bairro " + i,
                    "Cidade " + i,
                    "Estado" + i,
                    "Telefone 1" + i,
                    "Telefone 2" + i,
                    "Email" + i,
                    'f'));
        }

        try {
            Socket cliente = new Socket("127.0.0.1", 12345);
            enviarMensagem(serializarPessoa(pessoas), cliente);
            cliente.close();
        } catch (Exception e) {
            System.out.println("Erro: " + e.getMessage());
        } finally {
        }
    }                                           

    public byte[] serializarPessoa(List<PessoaMOD> pessoas) throws IOException {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        DataOutputStream dos = new DataOutputStream(bos);
        dos.writeInt(pessoas.size()); // tamanho da lista
        int cont = 0;
        for (PessoaMOD p : pessoas) {
            dos.writeInt(p.getId()); // id da pessoa
            dos.writeUTF(p.getNome());
            dos.writeUTF(p.getCpf());
            dos.writeUTF(p.getRg());
            dos.writeUTF(p.getEndereco());
            dos.writeUTF(p.getNumero());
            dos.writeUTF(p.getComplemento());
            dos.writeUTF(p.getBairro());
            dos.writeUTF(p.getCidade());
            dos.writeUTF(p.getEstado());
            dos.writeUTF(p.getTelefone1());
            dos.writeUTF(p.getTelefone2());
            dos.writeUTF(p.getEmail());
            dos.writeChar(p.getPerfil());//Nome da pessoa
        }
        return bos.toByteArray();
    }

    public void enviarMensagem(byte[] mensagem, Socket socket) throws IOException {
        DataOutputStream out = new DataOutputStream(socket.getOutputStream());
        System.out.println("Lenght: " + mensagem.length);
        out.writeInt(mensagem.length); //O tamanho da mensagem
        out.write(mensagem); //Os dados
        out.flush();
        out.close();
    }

Sistema servidor:

private void btIniciarActionPerformed(java.awt.event.ActionEvent evt) {                                          
        new Thread() {
            @Override
            public void run() {
                try {
                    ServerSocket servidor = new ServerSocket(12345);
                    System.out.println("Servidor ouvindo a porta 12345");
                    while (true) {
                        Socket cliente = servidor.accept();
                        DataInputStream entrada = new DataInputStream(cliente.getInputStream());

                        int tamanhoMsg = entrada.readInt();
                        System.out.println("Tam. Msg.: " + tamanhoMsg);

                        byte[] bytes = new byte[tamanhoMsg];
                        entrada.read(bytes, 0 , bytes.length);
                        System.out.println("Bytes size: " + bytes.length);

                        DataInputStream entrada2 = new DataInputStream(new ByteArrayInputStream(bytes));

                        int tamanhoLista = entrada2.readInt();

                        int id;
                        String nome;
                        String cpf;
                        String rg;
                        String endereco;
                        String numero;
                        String complemento;
                        String bairro;
                        String cidade;
                        String estado;
                        String telefone1;
                        String telefone2;
                        String email;
                        char perfil;

                        for (int i = 0; i < tamanhoLista; i++) {
                            id = entrada2.readInt();
                            nome = entrada2.readUTF();
                            cpf = entrada2.readUTF();
                            rg = entrada2.readUTF();
                            endereco = entrada2.readUTF();
                            numero = entrada2.readUTF();
                            complemento = entrada2.readUTF();
                            bairro = entrada2.readUTF();
                            cidade = entrada2.readUTF();
                            estado = entrada2.readUTF();
                            telefone1 = entrada2.readUTF();
                            telefone2 = entrada2.readUTF();
                            email = entrada2.readUTF();
                            perfil = entrada2.readChar();

                            System.out.println("Id: " + id);
                            System.out.println("Nome: " + nome);
                            System.out.println("Cpf: " + cpf);
                            System.out.println("Rg: " + rg);
                            System.out.println("Endereço: " + endereco);
                            System.out.println("Número: " + numero);
                            System.out.println("Complemento: " + complemento);
                            System.out.println("Bairro: " + bairro);
                            System.out.println("Cidade: " + cidade);
                            System.out.println("Estado: " + estado);
                            System.out.println("Telefone 1: " + telefone1);
                            System.out.println("Telefone 2: " + telefone2);
                            System.out.println("Email: " + email);
                            System.out.println("Perfil: " + perfil);
                            System.out.println("");
                        }
                        entrada.close();
                        entrada2.close();
                        cliente.close();
                    }
                } catch (Exception e) {
                    System.out.println("Erro: " + e.getMessage());
                } finally {
                }
            }
        }.start();
    }    

Acontece que a mensagem não vem completa, tem vez que executo e a lista vem ate a pessoa 37, as vezes ate a pessoa 392, cada hora vem um tanto de pesssoa da lista…

A variavel de tamanho da mensagem e a de tamanho da lista estão vindo certo…

Você está no caminho certo. Mas não prestou atenção ao detalhe que o método read, da classe InputStream, lê no máximo (e não exatamente) o número de dados que você passar no terceiro parâmetro. Entretanto, isso não significa que o método vá aguardar todos os dados chegarem. Tanto que ele retorna a quantidade de dados realmente lida.

Felizmente a solução para seu problema é fácil. Como você está usando o DataInputStream, você não precisa ler os dados num byte[] previamente. Leia diretamente do DataInputStream. Ele espera cada tipo primitivo daquele vir da rede:

private void btIniciarActionPerformed(java.awt.event.ActionEvent evt) {                                          
    new Thread() {
        @Override
        public void run() {
            try {
                ServerSocket servidor = new ServerSocket(12345);
                System.out.println("Servidor ouvindo a porta 12345");
                while (true) {
                    Socket cliente = servidor.accept();
                    DataInputStream entrada = new DataInputStream(cliente.getInputStream());

                    int tamanhoMsg = entrada.readInt();
                    System.out.println("Tam. Msg.: " + tamanhoMsg);

                    int tamanhoLista = entrada.readInt();

                    for (int i = 0; i < tamanhoLista; i++) {
                        int id = entrada.readInt();
                        String nome = entrada.readUTF();
                        Stirng cpf = entrada.readUTF();
                        String rg = entrada.readUTF();
                        String endereco = entrada.readUTF();
                        String numero = entrada.readUTF();
                        String complemento = entrada2.readUTF();
                        String bairro = entrada.readUTF();
                        String cidade = entrada.readUTF();
                        String estado = entrada.readUTF();
                        String telefone1 = entrada.readUTF();
                        String telefone2 = entrada.readUTF();
                        String email = entrada.readUTF();
                        char perfil = entrada.readChar();

                        System.out.println("Id: " + id);
                        System.out.println("Nome: " + nome);
                        System.out.println("Cpf: " + cpf);
                        System.out.println("Rg: " + rg);
                        System.out.println("Endereço: " + endereco);
                        System.out.println("Número: " + numero);
                        System.out.println("Complemento: " + complemento);
                        System.out.println("Bairro: " + bairro);
                        System.out.println("Cidade: " + cidade);
                        System.out.println("Estado: " + estado);
                        System.out.println("Telefone 1: " + telefone1);
                        System.out.println("Telefone 2: " + telefone2);
                        System.out.println("Email: " + email);
                        System.out.println("Perfil: " + perfil);
                        System.out.println("");
                    }
                    entrada.close();
                    entrada2.close();
                    cliente.close();
                }
            } catch (Exception e) {
                System.out.println("Erro: " + e.getMessage());
            } finally {
            }
        }
    }.start();
}
2 curtidas

Se você fizer questão de preencher o Buffer, crie um método readBuffer assim:

public byte[] lerBuffer(InputStream entrada, int tamanhoMsg) {
  int count = 0;
  bytes = new byte[tamanhoMsg];
  while (count < tamanhoMsg) {
        bytes[count] = (int) entrada.read();
        count++;
  }
  return bytes;
}

Ler num buffer pode ser muito interessante se você quiser desempacotar esses dados em camadas. Por exemplo, uma parte do seu programa tratar o cabeçalho, outra parte tratar a parte de roteamento e outra os dados.

1 curtida

Cara, vlw pela resposta, me ajudou bastante. Só mais uma dúvida, vi alguns exemplos na internet que fazem a transferência dos dados por JSON. No cliente transforma a lista em JSON e no servidor transforma o JSON em lista. É uma alternativa boa ou inviável por algum motivo?

Depende.

Se você precisar otimizar o canal de dados, ou tornar os dados mais difíceis de ler para um terceiro, é melhor usar um protocolo binário, como você está fazendo. Do ponto de vista didático, é legal também entender como a comunicação binária funciona pois ela é a forma de se comunicar com muitos dispositivos externos.

Agora, se você for disponibilizar os dados para outros aplicativos, ou quer só facilidade no desenvolvimento, aí é melhor usar JSON. Como é um formato padrão, legível e difundido, há diversas APIs que automatizam drasticamente o trabalho, como a JAX-RS ou o org.json. Evita que você precise fazer toda essa serialização na mão.

PS: Há alternativas mais interessantes para protocolos binários também, como os protocol buffers, da google: https://developers.google.com/protocol-buffers/

1 curtida