Dúvidas Básicas de Socket

[quote=ViniGodoy]Em termos de código, esse exemplo ficaria:

Escrevendo:

out.writeInt(nomes.size()); for (String nome : nomes) { out.writeInt(nome.size()); out.writeChars(nome.toCharArray()); }

Lendo:

int tamanhoArray = in.readInt(); List<String> nomes = new ArrayList<String>(); for (int i = 0; i < tamanhoArray; i++) { nomes.add(readString(in)); }

Sendo readString o método que descrevi no post anterior. :wink:

PS: Aquele writeInt e writeChars provavelmente eu transformaria num método chamado writeString.[/quote]

Mas ali no “for de leitura” voce nao re-calcula o tamanho da String do próximo nome do array;

Eu havia entendido assim :

Por exemplo :

ArrayList<String > lista = …

lista.add(“André”);
lista.add("Viny);

//

out.writeInt(5); // Só um exemplo! Mas 5 porque é A,N,D,R,E ( 5 letras)
out.writeChars(“André”);

out.WriteInt(4); // Porque sao 4 Letras : V I N Y
out.WriteChars(“Viny”);

//

Lógico que acima, teria um for com um método pra calcular o numero de letras…

Nesse caso o Array Teria o tamanho de cada Nome, e as Strings.
E antes eu iria colocar o tamanho do array todo…

hmm…

O que estou fazendo é enviar exatamente o que falei. O tamanho do array, o tamanho da String e o texto. No exemplo do seu list o envio seria:
O int 2, pois tem 2 elementos no array
5, pois é a quantidade de caracteres da palavra Andre
Os caracteres André
4, pois é a quantidade de caracteres da palavra Vini
Os caracteres Vini

Por isso, na leitura, o primeiro for vai de 0 até <2. E para cada a String, lê o tamanho e o texto (que é o que o método readString do post que citei faz).

Veja o método readString, do post anterior. Nele eu leio o int e os caracteres.

[quote=ViniGodoy]O que estou fazendo é enviar exatamente o que falei. O tamanho do array, o tamanho da String e o texto. No exemplo do seu list o envio seria:
O int 2, pois tem 2 elementos no array
5, pois é a quantidade de caracteres da palavra Andre
Os caracteres André
4, pois é a quantidade de caracteres da palavra Vini
Os caracteres Vini

Por isso, na leitura, o primeiro for vai de 0 até <2. E para cada a String, lê o tamanho e o texto (que é o que o método readString do post que citei faz).

Veja o método readString, do post anterior. Nele eu leio o int e os caracteres.[/quote]

Agora entendi!!!
Consigo fazer! xD

Aqui está o link do post onde eu defino o método readString:

[quote=ViniGodoy]Aqui está o link do post onde eu defino o método readString:

aa Obrigado :stuck_out_tongue:

Acabei de imprimir os dois agora.
Pena que eu tenho que passar pro Word primeiro…
Se eu mando imprimir a seleção , sai quase em miniatura no sulfite :smiley:

[quote=ViniGodoy]Aqui está o link do post onde eu defino o método readString:

Viny, eu imprimi, rabisquei, rabisquei.
Rasurei…
Mas estou confuso na hora de ler os bytes.

A escrita eu fiz assim :

[code] public void encodeAndSendArrayList(ArrayList<Usuarios> listUsers) {
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bos);

        dos.writeByte(1);//1 == Array List!!!
        dos.writeInt(listUsers.size());// Tamanho do Array List!!!

        for (int i = 0; i &lt; listUsers.size(); i++) {
            Usuarios aux = listUsers.get(i);

            dos.writeInt(aux.getNomeDeUsuario().length());
            dos.writeChars(aux.getNomeDeUsuario());

        }

        //
        byte[] msg = bos.toByteArray();
        int tamanhoDoArray = listUsers.size();
        
        for (int i = 0; i &lt; listUsers.size(); i++) {
            Socket auxSocket = listUsers.get(i).getSocket();
            DataOutputStream out = new DataOutputStream(auxSocket.getOutputStream());
            
            out.writeInt(tamanhoDoArray);
            out.write(msg);
            out.flush();
        }

    } catch (Exception e) {
        System.out.println(e.getMessage());
    }
}[/code]

Me da um empurrão de novo :smiley: ???

[quote=Andre Lopes][quote=ViniGodoy]Aqui está o link do post onde eu defino o método readString:

Viny, eu imprimi, rabisquei, rabisquei.
Rasurei…
Mas estou confuso na hora de ler os bytes.

A escrita eu fiz assim :

[code] public void encodeAndSendArrayList(ArrayList<Usuarios> listUsers) {
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bos);

        dos.writeByte(1);//1 == Array List!!!
        dos.writeInt(listUsers.size());// Tamanho do Array List!!!

        for (int i = 0; i &lt; listUsers.size(); i++) {
            Usuarios aux = listUsers.get(i);

            dos.writeInt(aux.getNomeDeUsuario().length());
            dos.writeChars(aux.getNomeDeUsuario());

        }

        //
        byte[] msg = bos.toByteArray();
        int tamanhoDoArray = listUsers.size();
        
        for (int i = 0; i &lt; listUsers.size(); i++) {
            Socket auxSocket = listUsers.get(i).getSocket();
            DataOutputStream out = new DataOutputStream(auxSocket.getOutputStream());
            
            out.writeInt(tamanhoDoArray);
            out.write(msg);
            out.flush();
        }

    } catch (Exception e) {
        System.out.println(e.getMessage());
    }
}[/code]

Me da um empurrão de novo :smiley: ??? [/quote]

OK, depois de algumas hora pensando…

-Se eu puder ler na ordem que eu escrevi, ao invés da ordem inversa…
Leio o 1 byte, que define que é um arrayList
Leio o int que vem após o byte, que define o tamanho.

–Leio o tamanho da primeira String
–Leio a String

Esta Correto Isto ?

Dúvidas extras :

Se o array estiver vazio… O tamanho deve vir 0, correto ?
Sobre Mac e PC, se eu tiver esse app rodando no mac, eu tenho que mudar o código?

Muito Obrigado
Guj Rules :smiley:

OK, eu refiz todo o código do servidor e deixei os métodos e classes mais separados.

Agora assim, Enquanto eu escrevia esse post, eu pensei num problema.
Aquele ArrayList, eu nao posso enviar em forma de String, porque eu vou precisar enviar o socket pra que o cliente consiga enviar mensagens pra outros clientes.

Tem como eu escrever o byte de tamanho e o byte de tipoDeMensagem e enviar a lista serializada?

Eu gostei de organizar por Bytes, achei muito bacana.
Eu queria misturar os dois.

Como posso fazer isso?

Não se envia o socket. Um cliente manda para o servidor o nome (ou o id) de outro. O servidor vai então num hashmap, pega esse id, acha o socket do cliente em questão e então redireciona a mensagem para lá.

Ah ok.
Acabei de descobrir que nao posso serializar um socket.
Mas eu tinha feito certo rsrsr.

Vou fazer assim,
Vou criar um lista de forma que eu possa re-montar os sockets do lado do cliente.

O cliente geralmente só tem um socket, com o servidor. Não faz sentido abrir outros sockets.

O servidor vai possuir um socket para cada cliente conectado. Então as conversas sempre vão do cliente para o servidor, e do servidor para o outro cliente.

Se A quer falar com B, através de um servidor S, a conversa segue esse fluxo:
A->S->B

[quote=ViniGodoy]O cliente geralmente só tem um socket, com o servidor. Não faz sentido abrir outros sockets.

O servidor vai possuir um socket para cada cliente conectado. Então as conversas sempre vão do cliente para o servidor, e do servidor para o outro cliente.

Se A quer falar com B, através de um servidor S, a conversa segue esse fluxo:
A->S->B[/quote]

Verdade!
Não tinha pensado assim…
Muito bem, entao vou fazer a lista apenas com o nome dos usuarios!

Mas pelo menos agora entendi como empacotar um objeto dentro dos bytes e fazer os protocolos :smiley:

Esqueça essa história de ObjectStreams.
No fundo, ele não envia “o objeto” pela rede. Isso não existe. Ele simplesmente faz de forma automática o envio campo-a-campo do objeto, e remonta uma cópia desse objeto do outro lado da conexão.

Mas essa abordagem tem problemas. Você não tem controle sobre como essa serialiazação é feita. Não há garantias de que ela não vá mudar de uma versão de Java para outra. Então, se você serializar com um ObjectInputStream, nada garante que tua aplicação não vá dar pau pq o cliente resolveu atualizar a JRE dele do outro lado da linha.

Sempre que fizer uma aplicação com sockets tenha total controle sobre o protocolo de comunicação. Isso vai evitar muitas dores de cabeça. Se quiser deixar o código profissional, faça algumas mensagens de handshake, onde uma aplicação diz a outra que versão do protocolo está usando. Assim você poderia dar uma mensagem de erro elegante, caso um cliente muito antigo tente se conectar (ou desconectar sem crash caso alguém simplesmente jogue dados idiotas por sua conexão socket).

[quote=ViniGodoy]Esqueça essa história de ObjectStreams.
No fundo, ele não envia “o objeto” pela rede. Isso não existe. Ele simplesmente faz de forma automática o envio campo-a-campo do objeto, e remonta uma cópia desse objeto do outro lado da conexão.

Mas essa abordagem tem problemas. Você não tem controle sobre como essa serialiazação é feita. Não há garantias de que ela não vá mudar de uma versão de Java para outra. Então, se você serializar com um ObjectInputStream, nada garante que tua aplicação não vá dar pau pq o cliente resolveu atualizar a JRE dele do outro lado da linha.

Sempre que fizer uma aplicação com sockets tenha total controle sobre o protocolo de comunicação. Isso vai evitar muitas dores de cabeça. Se quiser deixar o código profissional, faça algumas mensagens de handshake, onde uma aplicação diz a outra que versão do protocolo está usando. Assim você poderia dar uma mensagem de erro elegante, caso um cliente muito antigo tente se conectar (ou desconectar sem crash caso alguém simplesmente jogue dados idiotas por sua conexão socket).[/quote]

Tudo bem.

Fiz do jeito que voce me aconselhou.
O cliente recebe o tamanho da lista ( que é uma String ) mas nao consegue decodificar:

//Código do servidor :

[code] public void sendListaUsuarios(ArrayList<Usuarios> listaUsuarios) {
try {

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
         DataOutputStream dos = new DataOutputStream(bos);
          dos.writeByte(1);//Avisa que é uma lista de usuarios
        
        for (int i = 0; i &lt; listaUsuarios.size(); i++) 
        {
            Usuarios aux = listaUsuarios.get(i);
            //dos.writeInt(aux.getiNetAdress().length());
            dos.writeUTF(&quot;::&quot;);
            dos.writeUTF(&quot;&quot; + aux.getiNetAdress());
            
        }
         byte[] msg = bos.toByteArray();
         
         for(int i = 0;i&lt;listaUsuariosSockets.size();i++)
         {
             UsuariosSockets aux = listaUsuariosSockets.get(i);
             DataOutputStream out = new DataOutputStream(aux.getSocket().getOutputStream());
             out.writeInt(msg.length);   System.out.println("\n/Server/Lista/Tamanho da mensagem Sendo Enviada : " + msg.length);
             out.write(msg);
             out.flush();
             System.out.println("\nLista em Bytes Enviados para " + aux.getSocket().getInetAddress().getHostName());
         }
    } catch (Exception e) {
        e.printStackTrace();
        System.out.println(e.getMessage());
    }
}[/code]

//Código do cliente :

[code]@Override
public void run() {
try {
while (true) {

            DataInputStream dis = new DataInputStream(socket.getInputStream());

            int tamanho = dis.readInt(); // Como é Read Int, acredito que ja leia 4 Bytes, correto?
            int tipoMensagem = dis.read(); // Tipo de mensagem

            System.out.println("Cliente*Tamanho DIS : " + tamanho);
            System.out.println("Cliente*TipoMensagem DIS : " + tipoMensagem);
                    
            if (tipoMensagem == 0) {
                String input = "";
                for (int i = 1; i &gt;&lt; tamanho; i = i + 2) {
                    input = input + dis.readChar();
                }
                System.out.println(&quot;&quot; + input);
            } else if (tipoMensagem == 1) {
                System.out.println(&quot;È Um ArrayList! Decodificando ....&quot;);
                String lista = &quot;&quot;;
                
                for(int i = 1; i &lt; tamanho; i ++)
                {
                    lista = lista + dis.readUTF();
                }
               System.out.println(&quot;Lista : &quot; + lista);
            }

        }

    } catch (Exception e) {
        e.printStackTrace();
        System.out.println(e.getMessage());
    }


}[/code]

O que estou fazendo de errado???

Obrigadão Viny !!

Até a linha System.out.println(“È Um ArrayList! Decodificando …”);
Ele chega

Mas depois nao acontece nada …

Eu tentei isso aqui agora :

[code] } else if (tipoMensagem == 1) {
System.out.println(“È Um ArrayList! Decodificando …”);
String lista = “”;

                for(int i = 1; i < tamanho; i = i + 2)
                {
                    lista = lista + dis.readUTF();
                }
               System.out.println("Lista : " + lista);
            }

        }[/code]

Nao foi.

OK, só atualizando o tópico…
Tive uma epifania quando fui no banheiro kkk

E fiz isso aqui quando voltei :

[code] } else if (tipoMensagem == 1) {
System.out.println(“È Um ArrayList! Decodificando …”);
String lista = “”;

                for(int i = 1; i < tamanho; i = i + 1)
                {
                    System.out.println("Array : " + lista);
                    lista = lista + dis.readUTF();
                }
               System.out.println("Lista : " + lista);
            }[/code]

Ele esta recebendo o array, mas o problema esta no tamanho.
Eu tentei i+2 mas nao deu certo

Como que devo usar esse readUTF() ?

Eu sei que o erro é do jeito que estou lendo…
Então pelo menos isso ja esta certo
:stuck_out_tongue:

Consegui!!!

[code]} else if (tipoMensagem == 1) {
System.out.println(“È Um ArrayList! Decodificando …”);
String lista = “”;

                for(int i = 0; i < tamanho; i = i + 1)
                {
                    //System.out.println("Array -> " + dis.readUTF() + " -> Linha " + i);
                    lista = lista +"\n"+ dis.readUTF();
                }
               System.out.println(" Lista : " + lista);
               System.out.println("Lista decodificada! Fim!");
            }[/code]

[code]public void sendListaUsuarios(ArrayList listaUsuarios) {
try {

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
         DataOutputStream dos = new DataOutputStream(bos);
          dos.writeByte(1);//Avisa que é uma lista de usuarios
        
          int tamanho = listaUsuarios.size();
        for (int i = 0; i < tamanho; i++) 
        {
            Usuarios aux = listaUsuarios.get(i);
            
            dos.writeUTF("" + aux.getiNetAdress());
            
        }
         byte[] msg = bos.toByteArray();
         
         for(int i = 0;i<listaUsuariosSockets.size();i++)
         {
             UsuariosSockets aux = listaUsuariosSockets.get(i);
             DataOutputStream out = new DataOutputStream(aux.getSocket().getOutputStream());
             out.writeInt(tamanho);   System.out.println("\n/Server/Lista/Tamanho da mensagem Sendo Enviada : " + msg.length + " Ou seja,lista.size() == " +tamanho);
             out.write(msg);
             out.flush();
             System.out.println("\nLista em Bytes Enviados para " + aux.getSocket().getInetAddress().getHostName());
         }
    } catch (Exception e) {
        e.printStackTrace();
        System.out.println(e.getMessage());
    }
}[/code]

aaa Viny.

Re-fiz os dois projetos.
O servidor e o cliente.

Mas ele só recebe uma mensagem e nao recebe a outra.

Será que se eu postar o link do 4shared com meus dois projetos é abusar ?
Não tenho mais pra quem pedir ajuda, colega meu esta viajando >.<
Mandei email pra ele com o link, mas duvido que ele vai responder logo haha

Só por Desencargo de consciencia


Ou Então :
http://www.4shared.com/archive/EpyyKX0O/Chat_Java_20.html

Quem sabe alguém baixa e me ajuda :smiley: