Estou tentando criar um chat usando swing, a comunicação está funcionando entre todos os clientes, não usei DataInput como o Viny disse e sim PrintStreams…(Acho que por isso me lascei). Eis que resolvo criar uma simples JList para saber quem está Online no chat, e é quando o problema começa.
Ultilzando somente um socket não consegui fazer isto, então estou a utilizar dois socket por Cliente, o que deve estar completamente errado.
O que eu fiz: Sempre que um novo Cliente se conecta ao servidor, o servidor manda o nome deste cliente para todos os outros, mas o ultimo cliente não recebe os clientes já conectados, assim como o penúltimo que só recebe os clientes conectados anterior a ele.
Uma solução que eu pensei seria enviar uma Lista para o cliente e não somente o novo Cliente conectado… mas como fazer?
Como alguém que já teve de implementar protocolos segundo a especificação, posso lhe dizer: protocolos já existentes costumam ser muito complexos. Em vez disso, crie um mais simples, mas que seja flexível. Exemplo de um protocolo simples e flexível:
Primeiros 2 bytes - um identificador único e fixo, que você pode usar para verificar se a mensagem é válida (normalmente chamado “número mágico”). É interessante que sejam coisas que não apareçam normalmente nas mensagens que você manda.
4 bytes seguintes - o tamanho total da mensagem
2 bytes seguintes - um identificador para a mensagem (por exemplo, 0x0001 = PING, 0x0002 = LOGIN, 0x0003 = LOGOUT, 0x0004 = USER LIST etc. )
resto - o resto da mensagem (a estrutura dela depende, obviamente, do tipo de mensagem que você vai criar.
Você deve parar para pensar no seu protocolo, obrigatoriamente.
[size=18][color=red]
Não é uma opção.
Não é uma opção.
Não é uma opção.
[/color]
[/size]
O protocolo nada mais é do que as regras que regem sua comunicação.
Não interessa se é por texto ou binário, mas você deve definir exatamente isso:
Como os clientes avisam uns aos outros quem está conectado?
Como o cliente avisa o servidor que quer parar de se comunicar?
Como o servidor avisa os outros clientes que alguém saiu?
Como o servidor testa se um cliente ainda está conectado?
Como um cliente manda uma mensagem particular para outro?
E assim por diante. Para cada uma dessas perguntas, provavelmente haverá uma ou mais mensagens que deverão ser trocadas. E seria bom você estruturar essas mensagens de forma que seja fácil de quebrar o texto e analisa-las depois. No caso de usar o DataInputStream (como sugeri no tópico sobre protocolos) você estará usando um protocolo binário (como o Entanglement sugeriu) o que na, minha opinião, não só é mais leve como muito mais fácil de implementar. Basta estruturar os dados (como no exemplo que eu e ele demos) e le-los na ordem correta.
Se você não pensar no seu protocolo, você ainda terá um protocolo (na verdade, você já tem um hoje). Mas será tosco, desorganizado e provavelmente vai te dar muito trabalho.
[quote=diego123321]Entendi, então o protocolo seria a camada entra o servidor e o cliente?
Estas perguntas que você citou eu realmente já fiz, e sim, o meu “protocolo” está bem tosco.
E tenho outras dúvidas quanto a lógica cliente-servidor. Usando sua pergunta: - Como o servidor testa se um cliente ainda está conectado?
Eu teria que usar um while(true) para isso? não causaria problemas?
Obrigado a todos pela paciência…[/quote]
Exatamente. O código de sockets sempre roda num while:
while (!desconectou) {
Mensagem m = pegaAProximaMensagem();
processa(mensagem);
}
Como o servidor testa se um cliente desconectou? Há algumas opções:
a) O cliente envia uma mensagem dizendo que ele desconectou (isso é chamado de Graceful Disconnection);
b) Se o cliente está mudo há muito tempo, o servidor envia uma mensagem de KEEP_ALIVE. O cliente, se estiver online, deve responder com uma mensagem de ALIVE.
As mensagens de KEEP_ALIVE e ALIVE são partes desse protocolo. Nele vc especificou como é a mensagem, e que obrigatoriamente, uma mensagem de ALIVE deve ser resposta de um KEEP_ALIVE. Se a mensagem não chegar, o servidor encerra a conexão com aquele cliente.
O protocolo é justamente a lista de mensagens que podem circular pela rede, e como essas mensagens são codificadas (se são binárias, texto, em suma, seu formato). Sem definir que mensagens vão e vem, sua aplicação estará fadada ao fracasso.
Eu devo utilizar as duas opções ou somente uma? No caso se eu usar KEEP_ALIVE e ALIVE e estiver em uma rede ou pela web, poderia haver algum tipo de delay, então eu teria que esperar a resposta por um determinado tempo?
Analisando o protocolo, este seria, por exemplo, uma classe no servidor e uma para o cliente?
No exemplo em que o entanglement passou:
[quote]Primeiros 2 bytes - um identificador único e fixo, que você pode usar para verificar se a mensagem é válida (normalmente chamado “número mágico”). É interessante que sejam coisas que não apareçam normalmente nas mensagens que você manda.
4 bytes seguintes - o tamanho total da mensagem
2 bytes seguintes - um identificador para a mensagem (por exemplo, 0x0001 = PING, 0x0002 = LOGIN, 0x0003 = LOGOUT, 0x0004 = USER LIST etc. )
resto - o resto da mensagem (a estrutura dela depende, obviamente, do tipo de mensagem que você vai criar. [/quote]
Como eu montaria uma mensagem utilizando bytes?
Edit:
Como você citou, um socket sempre roda dentro de um while , isso não iria gerar um loop inifinito? Ou existe algum método blocante?
Geralmente as duas. O KeepAlive é para aquele cliente em que a luz da casa dele simplesmente acabou.
Ele jamais enviará a mensagem de que saiu e demorará muito até que o socket detecte a queda do cliente sozinho (o socket tem um keep alive, mas os tempos giram na casa de 10 minutos).
Leia o meu tópico sobre protocolos que vc linkou, lá dou um exemplo de como montar uma mensagem usando um DataInputStream.
Se você vai usar um protocolo “chatty” (ou seja, muitas mensagens pequenas com perguntas e respostas), então você tem de setar uma opção do socket chamada TCP_NODELAY.
Veja o exemplo de código no Javadoc da classe SocketOptions:
Um protocolo não “chatty” é algo parecido com uma estrada com pista dupla - um sentido não interfere com o outro, e ambos os lados podem rodar na velocidade máxima. Nesse caso, você pode usar a configuração padrão.
Um protocolo “chatty”, como o que você tem de usar em um “chat”, é parecido com uma estrada que tem apenas uma pista, e ela tem de ser usada tanto para a mão quanto para a contra-mão. Se um carro tiver de passar em um sentido, tem de esperar o outro no outro sentido passar, e vice-versa. Nesse caso é interessante usar o TCP_NODELAY porque isso desabilita o algoritmo de Nagle:
Eu sei, mas é por isso que estou falando das opções do socket. Muitas vezes o seu problema não é no DataInputStream (aquilo que você enxerga de perto) e sim no Socket (aquilo que está mais longe).