Ir para conteúdo

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

Micilini Roll

[Resolvido] [Puzzle] Chat Privado (WebSockets + PHP Sockets)

Recommended Posts

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:

 

 

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:

 

  • No meu caso estou utilizando os arquivos dentro da pasta easychat (que esta no link enviado anteriormente)
  • Podemos utilizar node.js, socket.io e afins? Não, estou querendo resolver somente com php e javascript puro.

 

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:

 

  • IP do usuario (Pode precisar)
  • Chat de onde ele esta acessando (Chat1, Chat2, Chat3)
  • Mensagem (é claro rs)

 

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){
try{
socket_write($socket,(encode($d)));
}catch(Exception $e){
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:

 

  • Vamos criar um arquivo php com 2 links: Ir para o Chat 1 & Ir para o Chat 2
  • Vamos reaproveitar aqueles arquivos dentro da pasta EasyChat
  • Vamos duplicar o arquivo index.php, criando indexChat1.php e IndexChat2.php

 

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 ;)

 

 

Compartilhar este post


Link para o post
Compartilhar em outros sites

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:

 

  • Problema na segurança, alguem pode ir lá no codigo e tirar essa filtragem e começar a receber a mensagem de todos.
  • Considerando que temos 1000 chats ativos simultaneamente, uma pessoa irá receber todas as mensagens que não são referentes a ela. (E não acho que esses chats privados funcionam desta forma)

 

Atualização: Este metodo não é muito confiavel e dependendo da velocidade de conexão do usuario isso pode se tornar um grande problema.

Compartilhar este post


Link para o post
Compartilhar em outros sites

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.

Compartilhar este post


Link para o post
Compartilhar em outros sites

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){
try{
socket_write($socket,(encode($d)));
}catch(Exception $e){
unset($cls[$socket]);
socket_close($cls[$socket]);
}
}
}
}
} 

Por este:

}else{
$bytes = socket_recv($sock, $data, 2048, null);
$d = unmask($data);
try{
socket_write($socketLista[0],(encode($d)));
}catch(Exception $e){
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:

 

  • Abra no navegador o arquivo server.php.
  • Em seguida abra o index.php em uma aba.
  • Agora abra mais 2 novas tabs no seu navegador e abra o index.php em cada uma delas.

 

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

Compartilhar este post


Link para o post
Compartilhar em outros sites

  • Conteúdo Similar

    • Por ILR master
      Fala galera, tudo bem?
       
      Tenho o seguinte codigo:
       
       class Data {
      public static function ExibirTempoDecorrido($date)
      {
          if(empty($date))
          {
              return "Informe a data";
          }
          $periodos = array("segundo", "minuto", "hora", "dia", "semana", "mês", "ano", "década");
          $duracao = array("60","60","24","7","4.35","12","10");
          $agora = time();
          $unix_data = strtotime($date);
          // check validity of date
          if(empty($unix_data))
          {  
              return "Bad date";
          }
          // is it future date or past date
          if($agora > $unix_data) 
          {  
              $diferenca     = $agora - $unix_data;
              $tempo         = "atrás";
          } 
          else 
          {
              $diferenca     = $unix_data - $agora;
              $tempo         = "agora";
          }
          for($j = 0; $diferenca >= $duracao[$j] && $j < count($duracao)-1; $j++) 
          {
              $diferenca /= $duracao[$j];
          }
          $diferenca = round($diferenca);
          if($diferenca != 1) 
          {
              $periodos[$j].= "s";
          }
          return "$diferenca $periodos[$j] {$tempo}";
      }
      }
       
      Funciona redondinho se o valor retornado for de algumas horas, mas...
      Quando passa de dois meses, ele retorna a palavra mess. Deve ser por conta dessa linha
      if($diferenca != 1) 
          {
              $periodos[$j].= "s";
          }
       
      Quero que modre:
       
      2 meses atrás
      e não
      2 mess atrás.
       
      Espero que tenham entendido.
       
      Valeu
    • Por Carlos Web Soluções Web
      Olá...
      Estou tentando fazer o seguinte !!
      Listando dados em tabela !!
      Gostaria que....se na listagem houver 4 linhas...indepedente de seu número de ID, faça a listagem em ID ser em ordem 1 2 3 4 !!
      Exemplo...se tiver uma listagem de dados que está em ID 1 3 3...faça ficar 1 2 3 !!

       
      echo "<table class='tabela_dados' border='1'> <tr> <td>ID</td> <td>Nome Empresa</td> <td>Responsável</td> <td>Telefone 1</td> <td>Telefone 2</td> <td>E-mail 1</td> <td>E-mail 2</td> <td>Endereço</td> <td>CEP</td> <td>Bairro</td> <td>AÇÃO 1</td> <td>AÇÃO 2</td> </tr> "; $sql = "SELECT ID FROM usuarios_dados WHERE Usuario='$usuario'"; $result = $conn->query($sql); $num_rows = $result->num_rows; $Novo_ID = 1; for ($i = 0; $i < $num_rows; $i++) { $registro = $result -> fetch_row(); $sql2 = "UPDATE usuarios_dados SET ID='$Novo_ID' WHERE ID='$Novo_ID'"; $result2 = $conn->query($sql2); $Novo_ID++; } $sql = "SELECT * FROM usuarios_dados"; $result = $conn->query($sql); if ($result->num_rows > 0) { // output data of each row while($row = $result->fetch_assoc()) { echo "<tr> <td>$row[ID]</td> <td>$row[Nome_Empresa]</td> <td>$row[Responsavel]</td> <td>$row[Telefone_1]</td> <td>$row[Telefone_2]</td> <td>$row[Email_1]</td> <td>$row[Email_2]</td> <td>$row[Endereço]</td> <td>$row[CEP]</td> <td>$row[Bairro]</td> <td> <form method='post' action='Editar_Dados.php'> <input type='hidden' name='usuario' value='$usuario'> <input type='hidden' name='senha' value='$senha'> <input type='hidden' name='ID' value='$row[ID]'> <input type='submit' style='padding: 10px;' value='EDITAR'> </form> </td> <td> <form method='post' action='Deletar_Dados.php'> <input type='hidden' name='usuario' value='$usuario'> <input type='hidden' name='senha' value='$senha'> <input type='hidden' name='ID' value='$row[ID]'> <input type='submit' style='padding: 10px;' value='DELETAR'> </form> </td> </tr> "; } } else { echo "0 results"; } $conn->close();  
    • Por ILR master
      Boa tarde pessoal, tudo bem ?
       
      Eu uso o tinymce para cadastro de textos no meu siite, porém, quero fazer um sistema para que os colunistas possam fazer o próprio post.
      O problema do tinymce, é que ele mantém a formatação do texto copiado, como tamanho de fonts, negritos, etc... Quero que o usuário cole o texto e a própria textarea limpe a formatação para que ele formate como quiser.
       
      A pergunta é:
       
      O tinymce tem uma opção para desabilitar a formatação quando um texto é colocado?
      Tem alguma função via java ou php para retirar a formatação assim que o texto é colado?
      Ou é melhor usar um outro editor?
       
      Agradeço deste já.
    • Por Giovanird
      Olá a todos!
      Tenho uma pagina que possui uma DIV onde coloquei uma pagina PHP.
      Uso a função setInterval para atualizar a pagina inclusa dentro da DIV.
      O problema é que ao acessar o site , a DIV só me mostra a pagina inclusa somente quando completo o primeiro minuto.
      Preciso que a pagina inclusa já inicie carregada
       
      Meu código JavaScript e a DIV com a pagina PHP
       
      <script> function atualiza(){ var url = 'direita.php'; $.get(url, function(dataReturn) { $('#direita').html(dataReturn); }); } setInterval("atualiza()",60000); </script> <div> <span id="direita"></span> </div>  
    • Por ILR master
      Fala pessoal.
       
      Seguinte:
       
      Quero selecionar duas tabelas e mostrar com resultados intercalados. Abaixo segue um código explicando para vcs terem uma ideia.
       
      $consulta = "SELECT A.*, B.* FROM tabela1 A, tabela2 B'";
      $resultado = mysqli_query($conexao, $consulta) or die ("erro");
      while($busca = mysqli_fetch_array($resultado)){
       
      print $busca['cod_evento']; --> traz o código da tabela1 
      print $busca['titulo_evento']; -->  traz o titulo da tabela1
      print $busca['cod_noticia']; --> traz o código da tabela2
      print $busca['titulo_noticia']; --> traz o tituloda tabela2
       
      }
       
      Espero que entendam. Grato
       
×

Informação importante

Ao usar o fórum, você concorda com nossos Termos e condições.