-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
svyrydenko
committed
Oct 23, 2017
1 parent
1d60a22
commit 411255e
Showing
1 changed file
with
231 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,233 @@ | ||
# Laravel WebSocket chat server | ||
|
||
Under developing... | ||
 | ||
 | ||
 | ||
|
||
## Requirements | ||
|
||
* Unix (extension [pcntl_fork](http://php.net/manual/function.pcntl-fork.php)) | ||
* PHP 7.1+ | ||
* Laravel 5 | ||
* composer | ||
|
||
## Installing WebSockets Chat | ||
|
||
The recommended way to install WebSockets is through | ||
[Composer](http://getcomposer.org). | ||
|
||
|
||
```bash | ||
# Install Composer | ||
curl -sS https://getcomposer.org/installer | php | ||
``` | ||
|
||
Next, run the Composer command to install the latest stable version of WebSockets: | ||
|
||
```bash | ||
php composer.phar require ollyxar/websockets-chat | ||
``` | ||
|
||
## Configuration | ||
|
||
You can customize variables bellow by adding config-file: `websockets-chat.php` in the config folder: | ||
|
||
| parameter | description | example | | ||
| --- | --- | ---: | | ||
| handler | Handler Class (extends of Worker) | `\App\MyHandler` | | ||
| host | Host (ip) | `0.0.0.0` | | ||
| port | Port | `2083` | | ||
| worker_count | Count of forked process | `4` | | ||
| use_ssl | Used protocol | `false` | | ||
| cert | PEM certificate | `/etc/nginx/conf.d/wss.pem` | | ||
| pass_phrase | PEM certificate pass phrase | `secret$#%` | | ||
|
||
## Extended Handler class | ||
|
||
This is example how to use Handler with User authentication. If you have default configuration and file-session-storage you can use this example. | ||
|
||
First you have to install auth-helper: | ||
|
||
```bash | ||
php composer.phar require ollyxar/laravel-auth | ||
``` | ||
|
||
Then create your `Handler.php`: | ||
|
||
```php | ||
namespace App; | ||
|
||
use Ollyxar\LaravelAuth\FileAuth; | ||
use Ollyxar\WebSockets\Frame; | ||
use Ollyxar\WebSockets\Worker; | ||
|
||
/** | ||
* Class Handler | ||
* @package App | ||
*/ | ||
class Handler extends Worker | ||
{ | ||
/** | ||
* Connected users | ||
* | ||
* @var array | ||
*/ | ||
protected $users = []; | ||
|
||
private function fillUser(array $headers, $socket): bool | ||
{ | ||
if ($userId = FileAuth::getUserIdByHeaders($headers)) { | ||
// allow only one connection for worker per user | ||
if (!in_array($userId, $this->users)) { | ||
$this->users[(int)$socket] = $userId; | ||
return true; | ||
} | ||
} | ||
|
||
return false; | ||
} | ||
|
||
/** | ||
* @param $client | ||
*/ | ||
protected function onConnect($client): void | ||
{ | ||
$userName = User::where('id', (int)$this->users[(int)$client])->first()->name; | ||
|
||
$this->sendToAll(Frame::encode(json_encode([ | ||
'type' => 'system', | ||
'message' => $userName . ' connected.' | ||
]))); | ||
} | ||
|
||
/** | ||
* @param array $headers | ||
* @param $socket | ||
* @return bool | ||
*/ | ||
protected function afterHandshake(array $headers, $socket): bool | ||
{ | ||
return $this->fillUser($headers, $socket); | ||
} | ||
|
||
/** | ||
* @param $clientNumber | ||
*/ | ||
protected function onClose($clientNumber): void | ||
{ | ||
$userName = User::where('id', (int)$this->users[$clientNumber])->first()->name; | ||
|
||
$this->sendToAll(Frame::encode(json_encode([ | ||
'type' => 'system', | ||
'message' => $userName . " disconnected." | ||
]))); | ||
|
||
unset($this->users[$clientNumber]); | ||
} | ||
|
||
/** | ||
* @param string $message | ||
* @param int $socketId | ||
*/ | ||
protected function onDirectMessage(string $message, int $socketId): void | ||
{ | ||
$message = json_decode($message); | ||
$userName = User::where('id', (int)$this->users[$socketId])->first()->name; | ||
$userMessage = $message->message; | ||
|
||
$response = Frame::encode(json_encode([ | ||
'type' => 'usermsg', | ||
'name' => $userName, | ||
'message' => $userMessage | ||
])); | ||
|
||
$this->sendToAll($response); | ||
} | ||
} | ||
``` | ||
|
||
Then add markup to the front: | ||
|
||
```html | ||
<div class="chat-wrapper"> | ||
<div class="message-box" id="message-box"></div> | ||
<div class="panel"> | ||
<input type="text" name="message" id="message" placeholder="Message"/> | ||
<button id="send-btn" class="button">Send</button> | ||
</div> | ||
</div> | ||
``` | ||
|
||
And JS code: | ||
|
||
```javascript | ||
var wsUri = "ws://laravel5.dev:2083", | ||
ws = new WebSocket(wsUri); | ||
|
||
ws.onopen = function () { | ||
var el = document.createElement('div'); | ||
el.classList.add('system-msg'); | ||
el.innerText = 'Connection established'; | ||
document.getElementById('message-box').appendChild(el); | ||
}; | ||
|
||
document.getElementById('message').addEventListener('keydown', function (e) { | ||
if (e.keyCode === 13) { | ||
document.getElementById('send-btn').click(); | ||
} | ||
}); | ||
|
||
document.getElementById('send-btn').addEventListener('click', function () { | ||
var mymessage = document.getElementById('message').value; | ||
|
||
if (mymessage === '') { | ||
alert("Enter Some message Please!"); | ||
return; | ||
} | ||
|
||
var objDiv = document.getElementById("message-box"); | ||
objDiv.scrollTop = objDiv.scrollHeight; | ||
|
||
var msg = { | ||
message: mymessage | ||
}; | ||
ws.send(JSON.stringify(msg)); | ||
}); | ||
|
||
ws.onmessage = function (ev) { | ||
var msg = JSON.parse(ev.data), | ||
type = msg.type, | ||
umsg = msg.message, | ||
uname = msg.name; | ||
|
||
var el = document.createElement('div'); | ||
|
||
if (type === 'usermsg') { | ||
el.innerHTML = '<span class="user-name">' + uname + '</span> : <span class="user-message">' + umsg + '</span>'; | ||
document.getElementById('message-box').appendChild(el); | ||
} | ||
if (type === 'system') { | ||
el.classList.add('system-msg'); | ||
el.innerText = umsg; | ||
document.getElementById('message-box').appendChild(el); | ||
} | ||
|
||
document.getElementById('message').value = ''; | ||
|
||
var objDiv = document.getElementById('message-box'); | ||
objDiv.scrollTop = objDiv.scrollHeight; | ||
}; | ||
|
||
ws.onerror = function (e) { | ||
var el = document.createElement('div'); | ||
el.classList.add('system-error'); | ||
el.innerText = 'Error Occurred - ' + e.data; | ||
document.getElementById('message-box').appendChild(el); | ||
}; | ||
ws.onclose = function () { | ||
var el = document.createElement('div'); | ||
el.classList.add('system-msg'); | ||
el.innerText = 'Connection Closed'; | ||
document.getElementById('message-box').appendChild(el); | ||
}; | ||
``` |