ghedipunk / PHP-Websockets

A Websockets server written in PHP.
BSD 3-Clause "New" or "Revised" License
913 stars 375 forks source link

How to send message to a specific user #119

Closed areaboy closed 5 years ago

areaboy commented 5 years ago

Please is there anyway to send message to a specific user via id or any other means. where can I implement that on the code.

Again is this userid randomly generated and unique for all connected client Thanks

areaboy commented 5 years ago

Hello everyone. any help on this please

DrkAlien commented 5 years ago

Hi, with the current code you cannot send data to a specific user. I've changed the code for my usecase ( chat rooms where each user is connected to a chat room and he doesn't get messages from other rooms ) but it is quite complicated and i use it atm ( so as a security measure i won't post it here ) and maybe i can help you by explaining what i did:

  1. On the class "WebSocketUser" add one more attribute to the user ( $this->room_id ), like: function __construct($id, $socket,$room_id = '') { $this->id = $id; $this->socket = $socket; $this->room_id = $room_id; }
  2. Change the protected method "protected function process ($sender,$message)" from class "echoServer" to make sure you send the message to the users connected to that room, since $this->users holds all connected users. !important: $message variable must have the room_id ( you can have $message as json, not plain text ) # send the data only to the users connected to specific room id $message = json_decode($message,true); foreach($this->users as $user) { if($message['room_id'] == $user->room_id) { $this->send($user, $message); } }
  3. When the user is connecting to the ws server, make sure you send the room id in the URL/Path like: ws://yourdomain.com:9000/room142 where room142 is your room id.
  4. And lastly, change the method: "doHandshake" and add the room_id to the user object when the users is connecting ( step 3 ) like this: if (isset($headers['get'])) { $user->requestedResource = $headers['get']; $user->room_id = trim($headers['get'],'/'); # parse the GET request to get the room id } else { // todo: fail the connection ....

I hope it does help you, this is not simple to implement since you have to change the the source code. If you have specific questions, feel free to ask.

areaboy commented 5 years ago

Thank You so much Sir DrkAlien for your response. I implemented your suggestion. The app runs but message is not sent between the two users.

Here is how I added your solution testwebsock.php


#!/usr/bin/env php
<?php

require_once('./websockets.php');

class echoServer extends WebSocketServer {

  function __construct($addr, $port, $bufferLength) {
    parent::__construct($addr, $port, $bufferLength);
    $this->userClass = 'MyUser';
  }

  //protected $maxBufferSize = 1048576; //1MB... overkill for an echo server, but potentially plausible for other applications.

// send message only to yourself
/*
  protected function process ($user, $message) {
   $this->send($user,$message);
  }
*/

// send the data only to the users connected to specific room id

protected function process ($user, $message) {

if (isset($headers['get'])) { 

$user->requestedResource = $headers['get'];
$user->room_id = trim($headers['get'],'/'); 
//parse the GET request to get the room 

$message = json_decode($message,true); 
foreach($this->users as $user) {
 if($message['room_id'] == $user->room_id) { 
$this->send($user, $message);
 } 
}

 }
 else {
 // todo: fail the connection ....
}

}

  protected function connected ($user) {
    // Do nothing: This is just an echo server, there's no need to track the user.
    // However, if we did care about the users, we would probably have a cookie to
    // parse at this step, would be looking them up in permanent storage, etc.
  }

  protected function closed ($user) {
    // Do nothing: This is where cleanup would go, in case the user had any sort of
    // open files or other objects associated with them.  This runs after the socket 
    // has been closed, so there is no need to clean up the socket itself here.
  }
}
//$echo = new echoServer("localhost","9000");
//$echo = new echoServer("localhost","9000", 1048576);

//$echo = new echoServer("0.0.0.0","9000", 1048576);
$echo = new echoServer("localhost","9000", 1048576);

try {
  $echo->run();
}
catch (Exception $e) {
  $echo->stdout($e->getMessage());
}

here is user.php


<?php

class WebSocketUser {

  public $socket;
  public $id;
  public $headers = array();
  public $handshake = false;

  public $handlingPartialPacket = false;
  public $partialBuffer = "";

  public $sendingContinuous = false;
  public $partialMessage = "";

  public $hasSentClose = false;

  function __construct($id, $socket) {
    $this->id = $id;
    $this->socket = $socket;
  }
}

class MyUser extends WebSocketUser {
  public $myId;
/*
  function __construct($id, $socket, $myId) {
    parent::__construct($id, $socket);
    $this->myId = $myId;
  }
*/

// send mesage to users in a particular room
function __construct($id, $socket,$room_id = '') 
{ $this->id = $id; $this->socket = $socket; $this->room_id = $room_id; }

}
DrkAlien commented 5 years ago

First of all, this line must be added on the doHandshake protected method right after the foreach in that if at line 198. $user->room_id = trim($headers['get'],'/'); Because the roomid is set on that method and make sure the $message variable that is sent between users is a json string ( $message = jsondecode($message,true); ) in this format: {"room_id":123,"message":"some text"} You can hardcode it for testings. One last thing, make sure you send messages like this in the process method: `

protected function process ($user, $message) {
    $message = json_decode($message,true);
    // go in a loop for each user
    foreach($this->users as $user) {
        // if the user i am at has this room id from the $message['message'] array
        if($message['room_id'] == $user->room_id) { 
           // send the data to this user
           $this->send($user, $message['message']);
        } else {
           // do nothing, this user is connected to another room id
        }
    }` // end foreach
}

`

ps: make sure your read everything i wrote, everything is important. I see you've added code lines in the wrong method.

areaboy commented 5 years ago

Thanks so much Sir

DrkAlien commented 5 years ago

Did it worked?

areaboy commented 5 years ago

It partly works but message are not sent across one browser to another so i have decided to let it go for now. Thanks