Usamos cookies para medir audiência e melhorar sua experiência. Você pode aceitar ou recusar a qualquer momento. Veja sobre o iMasters.
Fala galera, estou aqui para pedir a ajuda de todos voces. Tudo que iremos tentar fazer é criar um chat privado utilizando websockets junto ao php sockets, para adiantar algumas coisas eu ja fiz um chat e disponibilizei no github, voces podem acessar atraves do link abaixo:
>
https://github.com/micilini/PHPSockets-With-WebSockets
Esse chat funciona perfeitamente, mas no entanto ele é um chat global, ou seja, uma mensagem enviada por um usuario X, vai para todos os usuarios que estão na sala do chat. Como dito anteriormente a nossa ideia é criar um chat privado, então vamos a algumas considerações:
Agora chegou a hora de dar uma pincelada no meu codigo:
Aplicacao.js
Existem duas funções responsaveis por enviar e receber a mensagem do servidor e abaixo estão elas:
form.onsubmit = function(e) { e.preventDefault(); // Recuperando a mensagem do textarea.
var mensagem = mensagemTexto.value; // Enviando a mensagem através do WebSocket.
socket.send(mensagem); // Adicionando a mensagem numa lista de mensagens enviadas.
listaMensagem.innerHTML += '<li class="envia"><span>Enviado:</span>' + mensagem + '</li>'; // Limpando o campo da mensagem após o envio.
mensagemTexto.value = ''; return false;
};
socket.onmessage = function(event) {
var mensagem = event.data; listaMensagem.innerHTML += '<li class="recebida"><span>Recebida:</span>' + mensagem + '</li>';
};
Quando enviamos uma mensagem ele executa o comando socket.send(mensagem) ali eu pensei em enviar mais alguns parametros como:
Já no lado do PHP, o comando abaixo é responsavel por pegar a resposta do usuario e retorna-la de volta:
Server.php
}else{
$bytes = socket_recv($sock, $data, 2048, null);
$d = unmask($data);
foreach ($cls as $socket) {
if($socket != $m_sock && $val > 0){unset($cls[$socket]);
socket_close($cls[$socket]);
}
}
}
}
}
Com o comando socket_recv ele consegue captar a mensagem que esta sendo enviada e realiza a função unmask nela, um pouco mais adiante ele faz o encode desta mensagem que recebeu e envia de volta para aquele socket (e todos que estão conectados naquele socket recebem a mensagem).
Nesse caso, como nosso chat é privada devemos pensar tambem em fazer o insert e em seguida um select no banco de dados, entao melhor maneira de fazer isso é inserindo esses comandos entre os codigos **try** e **socket_write**, então vamos aos testes:
......
try{
//Aqui estabilizamos uma conexão com o banco de dados e inserimos na tabela a mensagem + ip + data + Chat1 (ou 2 ou 3).
socket_write($socket,(encode($d)));
}
.....
Acho que tambem seria melhor criar um campo no banco de dados para verificar se a mensagem foi visualizada ou não?
Depois que inserimos no banco de dados chegou a hora do puzzle:
Como iremos fazer para retornar a mensagem para aquele determinado usuario?
Antes de respondermos a esta pergunta vamos criar um cenario novo e de simples visualização para todos nos:
Ambos os indexs irão utilizar a porta 8080 (assim como esta no github), Não! nos não iremos separar os chats em portas diferentes (tenha em mente que isso é so um teste para realizar algo mais completo lá na frente, IMAGINE 10000 chats privados acontecendo simultaneamente e abrimos uma porta para cada um deles......).
Entao apartir deste momento temos 2 indexes diferentes utilizando os mesmos arquivos: aplicacao.js e server.php, no caso do arquivo aplicacao.js ele envia valores diferentes para o socket php como ip, mensagem e o tipo de chat que ele esta inserido, como chat1 ou chat 2.
Executandos os Testes
OK, vamos abrir o arquivo indexChat1.php em uma aba, e indexChat2.php em outra aba do nosso navegador, vamos inicialmente escrever uma mensagem no chat1, quando fizermos isso (e do jeito que nosso codigo atualmente esta), a mensagem será recebida tambem na aba do chat2, e a mesma coisa aconteceria caso digitarmos algo no chat2.
Mas pelo menos no banco de dados esta dá seguinte forma:
ID = 0
IP = 32.22.221.22
Local = Chat1
Mensagem = Olá mundo
Data = 01/01/2016
Visualizado = false
Agora voltamos a seguinte pergunta: Como iremos fazer para os usuarios que estão no chat1, nao receber e nem enviar mensagens para os usuarios que estão no chat2 e virce-versa?
Eu acredito que a resposta esta na criação de um codigo que ainda nao sabemos mas que deve ser colocado dentro do arquivo server.php mais especificamente entre os comandos try e socket_write.
Alguem tem mais alguma ideia para contribuir, assim que vou avançando postarei algumas soluções neste post ;)
Solução 2
Fazendo uma breve busca na documentação do php foi encontrado um comando que acredito que possa substituir o socket_write, o socket_sendTo, de acordo com o que foi visto parece que ele envia uma resposta para o socket conectado, se estiver conectado ou não, abaixo se encontra um simples exemplo:
<?php
$sh = socket_create(AF_INET,SOCK_STREAM,SOL_TCP);
if (socket_bind($sh, '127.0.0.1', 4242)) {
echo "Socket bound correctly";
}
$buf = 'Test Message';
$len = strlen($buf);
if (socket_sendto($sh, $buf, $len, 0x100, '192.168.0.2', 4242) !== FALSE) {
echo "Message sent correctly";
}
socket_close($sh);
?>
Parece que este comando ele envia a resposta para o socket conectado naquela porta, mais especificamente para um determinado IP que esta conectado, isso evita que diferente da primeira solução, agora as respostas são enviadas para determinados ips conectados na rede.
Com isso nos iremos pegar a resposta do usuario, registrar no banco de dados, e criar um loop deste comando socket_sendTo e enviando o ip do pessoal que esta no chat1, acredito que isso evitaria o chat2 receber mensagens paralelas.
Esta solução ainda não foi testada, caso funciona estarei voltando aqui para trazer os resultados ;)
Atualização: Este metodo não funcionou, como o esperado.
Solução 3 - Definitiva
Analisando mais um pouco o segundo chat (mediumChat) localizado naquele link do github, e analisando tambem alguns chats na internet foi analisado que toda nova conexão de um socket é armazenado dentro de um array.
Isso significa que quando voce entra em uma sala de chat global, uma instancia do seu socket é criada, e enumerada como:
Resource id #3
Resource id #4
Resource id #5
.......
Utilizando o comando socket_getpeername é possivel selecionar o ip do socket da pessoa junto a uma porta de acesso (esta porta de acessa nao é a mesma porta de escuta do servidor do socket) diferente para cada usuario que entra (e muda constantemente -> se eu entrar na sala a porta é uma, se eu sair e entrar novamente a porta será outra).
Sabendo disso, tudo que devemos fazer é criar um array como por exemplo:
$socketsLista = array();
E quando um novo socket entrar nós iremos adicionar a instancia do socket dentro desse array, no meu caso eu fiz da seguinte forma (ESTE É UM EXEMPLO DO EASYCHAT)
if($sock === $m_sock){
......
socket_write($msgsock, $headers);
echo "handshak done...\n";
//Insira a variavel $m_sock dentro do array
$socketLista[] = $_sock;
}
A variavel $m_sock é a responsável por criar uma nova conexão com o socket sempre quando que um novo usuario entra no chat o codigo responsavel por isso pode se encontrar abaixo:
$m_sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
Para realizar um simples teste inicial, nos iremos substituir este comando:
}else{
$bytes = socket_recv($sock, $data, 2048, null);
$d = unmask($data);
foreach ($cls as $socket) {
if($socket != $m_sock && $val > 0){unset($cls[$socket]);
socket_close($cls[$socket]);
}
}
}
}
}
Por este:
}else{
$bytes = socket_recv($sock, $data, 2048, null);
$d = unmask($data);unset($cls[$socket]);
socket_close($cls[$socket]);
}
}
}
Isso vai fazer com que todas as respostas sejam enviadas somente para a instancia do socket que esta no index 0 do nosso array (que no caso seria o nosso primeiro usuario que abriu o chat), agora vamos aos testes:
Agora experimente digitar algum texto dentro dessas duas novas abas que voce abriu, voce verá que nem a 2º aba nem a 3º aba receberam as mensagens enviadas, mas ambas enviaram para o socket, por fim abra a primeira aba (aquela que contem o chat) e veja que voce recebeu 2 mensagens (tais mensagens enviadas das 2º e 3º aba), é claro que se enviarmos uma mensagem pela 1º aba nada ocorre (porque não configuramos isso).
Mas a ideia é essa mesmo, agora basta criarmos um banco de dados que armazene qual index do socket está se comunicando com qual index do socket e no socket write colocarmos algo como:
socket_write($socketLista[0],(encode($d)));
socket_write($socketLista[1],(encode($d)));
Que no caso ja estabelecemos uma conexão privada entre o chat 1 e o chat 2, no caso, se colocarmos assim, o chat 3 nao irá enviar nem receber nada, somente o chat 1 e 2.
Assim que eu terminar de fazer um teste final (chat privado de uma forma mais automatizada e com banco de dados), estarei postando no github e aqui tambem o projeto final e open source :D
Resolvido
SOLUÇÃO 1
Estava pensando em uma solução para esse determinado problema, atualmente não tenho absoluta certeza se o arquivo php junto ao socket se comportam como se fosse um SINGLETON (o que pode ser um problema para esta solução) mas a solução pensada foi:
Quando inserirmos no banco de dados, automaticamente nos iremos puxar as conversas que estão como visualizadas = false, isso vai fazer com que se retorne a mensagem que o proprio usuario enviou (e que apareça no chat) e tambem poderá puxar as mensagens que outras pessoas enviaram. Puxaremos tambem no banco de dados as mensagens que estão como marcadas como Local = chat1, isso vai fazer com que se retorne somente as mensagens daquela pessoa.
Ate ai tudo bem, e parece ser uma otima ideia, mas como todos nos estamos na mesma porta, acredito que ainda continuaremos a receber as mensagens advindas do chat2. Então como proceder?
Se lembra que no arquivo aplicacao.js as mensagens são recebidas desta forma: var mensagem = event.data;
Neste caso será que nao poderiamos fazer com que o socket_Write nos retorne 2 parametros como a mensagem e o local (chat1), isso faria com que no javascript ele fizesse uma checagem e filtrasse as mensagens e pegasse somente aquela referente ao chat1? Bem tenho algumas considerações a fazer:
Atualização: Este metodo não é muito confiavel e dependendo da velocidade de conexão do usuario isso pode se tornar um grande problema.