Giter Site home page Giter Site logo

php-websocket's Introduction

Bloatless PHP WebSockets

Simple WebSocket server implemented in PHP.

Installation

Requirements

  • PHP >= 7.4
  • ext-json
  • ext-sockets

Installation procedure

Install the package using composer:

composer require bloatless/php-websocket

Usage

Server

After downloading the sourcecode to your machine, you need some code to actually put your websocket server together. Here is a basic exmaple:

<?php

// require necessary files here

// create new server instance
$server = new \Bloatless\WebSocket\Server('127.0.0.1', 8000, '/tmp/phpwss.sock');

// server settings
$server->setMaxClients(100);
$server->setCheckOrigin(false);
$server->setAllowedOrigin('example.com');
$server->setMaxConnectionsPerIp(20);

// add your applications
$server->registerApplication('status', \Bloatless\WebSocket\Application\StatusApplication::getInstance());
$server->registerApplication('chat', \Bloatless\WebSocket\Examples\Application\Chat::getInstance());

// start the server
$server->run();

Assuming this code is in a file called server.php you can then start your server with the following command:

php server.php

The websocket server will then listen for new connections on the provided host and port. By default, this will be localhost:8000.

This repositoy also includes a working example in examples/server.php

Applications

The websocket server itself handles connections but is pretty useless without any addional logic. This logic is added by applications. In the example above two applications are added to the server: status and chat.

The most important methods in your application will be:

interface ApplicationInterface
{
    public function onConnect(Connection $connection): void;

    public function onDisconnect(Connection $connection): void;

    public function onData(string $data, Connection $client): void;

    public function onIPCData(array $data): void;
}

onConnect and onDisconnect can be used to keep track of all the clients connected to your application. onData will be called whenever the websocket server receives new data from one of the clients connected to the application. onIPCData will be called if data is provided by another process on your machine. (See Push-Client (IPC))

A working example of an application can be found in examples/Application/Chat.php

Timers

A common requirement for long-running processes such as a websocket server is to execute tasks periodically. This can be done using timers. Timers can execute methods within your server or application periodically. Here is an example:

$server = new \Bloatless\WebSocket\Server('127.0.0.1', 8000, '/tmp/phpwss.sock');
$chat = \Bloatless\WebSocket\Examples\Application\Chat::getInstance();
$server->addTimer(5000, function () use ($chat) {
    $chat->someMethod();
});
$server->registerApplication('chat', $chat);

This example would call the method someMethod within your chat application every 5 seconds.

Push-Client (IPC)

It is often required to push data into the websocket-server process from another application. Let's assume you run a website containing a chat and an area containing news or a blog. Now every time a new article is published in your blog you want to notify all users currently in your chat. To achieve this you somehow need to push data from your blog logic into the websocket server. This is where the Push-Client comes into play.

When starting the websocket server, it opens a unix-domain-socket and listens for new messages. The Push-Client can then be used to send these messages. Here is an example:

$pushClient = new \Bloatless\WebSocket\PushClient('//tmp/phpwss.sock');
$pushClient->sendToApplication('chat', [
    'action' => 'echo',
    'data' => 'New blog post was published!',
]);

This code pushes data into your running websocket-server process. In this case the echo method within the chat-application is called and it sends the provided message to all connected clients.

You can find the full working example in: examples/push.php

Important Hint: Push messages cannot be larger than 64kb!

Client (Browser/JS)

Everything above this point was related to the server-side of things. But how to connect to the server from your browser?

Here is a simple example:

<script>
 // connect to chat application on server
let serverUrl = 'ws://127.0.0.1:8000/chat';
let socket = new WebSocket(serverUrl);

// log new messages to console
socket.onmessage = (msg) => {
    let response = JSON.parse(msg.data);
    console.log(response.data);
};
</script>

This javascript connects to the chat application on your server and prints all incoming messages into the console.

A better example of the chat client can be found in: examples/public/chat.html

Intended use and limitations

This project was mainly built for educational purposes. The code is relatively simple and easy to understand. This server was not tested in production, so I strongly recommend not to use it in a live project. It should be totally fine for small educational projects or internal tools, but most probably will not handle huge amounts of traffic or connections very well.

Also, some "features" are missing by design:

  • SSL is not supported. If required, you can use a reverse proxy like nginx.
  • Binary messages are not supported.
  • A lot of other stuff I did not even realize ;)

In case you need a more "robust" websocket server written in PHP, please have a look at the excellent alternatives listed below.

Alternatives

License

MIT

php-websocket's People

Contributors

chrisharrison avatar colindev avatar dominics avatar fushihara avatar haugli92 avatar joserolles avatar mateusan avatar mazhack avatar mgrinspan avatar nekudo avatar nicokaiser avatar olehs avatar r-martins avatar ricardoboss avatar sikanderiqbal avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

php-websocket's Issues

Server drops messages

When several messages are sent at once (say 10 'echo' commands) the server responds only to some of them.

The problem is with Connection::hybi10decode() function: once a frame is successfully decoded, all remaining data from Connection::$dataBuffer is dropped. A quick fix is to add

$this->dataBuffer = substr($data, $dataLength)

at the end of the function and then process Connection::handle() inside Connection::onData in a loop until false is returned (together with some extra checks on the size of $this->dataBuffer while decoding).

Use with WebRTC

I want to be able to make this server connect the client to the server via a webrtc stream how would i do this??

is this even possible?

i need to be able to encode the stream from the client directly to the server in some way FFmpeg??

Missing client documentation

Where is the option to creat a websocket client? That is keep getting data from a certain ws:// url from php?

socket_bind creates file in /tmp, but www-data has no permissions to it

Server.php -> openIPCSocket() -> socket_bind -> creates a file in /tmp but www-data has no permissions to use it. This happened when i tried to send IPC data from an action of a website to the WebSocket server. This change did the trick:

FROM
if (socket_bind($this->icpSocket, $ipcSocketPath) === false) {
throw new \RuntimeException('Could not bind to ipc socket.');
}

TO
if (socket_bind($this->icpSocket, $ipcSocketPath) === false) {
throw new \RuntimeException('Could not bind to ipc socket.');
} else {
chown($ipcSocketPath, 'www-data');
}

Class "Bloatless\WebSocket\Examples\Application\Chat" not found

Hello,

Maybe this is a composer.json problem,
Chat.php is available in respective location, but my application could not recognize that.
On the other hand, Bloatless\WebSocket\Application\StatusApplication is found and working fine.

Error

Class "Bloatless\WebSocket\Examples\Application\Chat" not found

at F:\Server\app\Console\Commands\webSocket.php:53
 49▕         $server->setMaxConnectionsPerIp(20);
 50▕
 51▕ // add your applications
 52▕         $server->registerApplication('status', StatusApplication::getInstance());
 ➜  53▕         $server->registerApplication('chat', Chat::getInstance());
 54▕
 55▕ // start the server
 56▕         $server->run();

Tnanks in advance.

Adding a Client.php Message Listener

In your client example you provided a example on how to send a message with $client->sendData. I've checked the Client.php but could not find a way to listen for a new Message from the Server.php. Do you support bi-directional communication? You already have a function to send a message from server to the client.

Can't set timer

This is my error:
Typed property Bloatless\WebSocket\Server::$timers must not be accessed before initialization

        $server = new \Bloatless\WebSocket\Server(env('WS_IP'), (int) env('WS_PORT'));

        // add a PSR-3 compatible logger (optional)
        $server->setLogger(new \Bloatless\WebSocket\Logger\StdOutLogger());

        // server settings:
        $server->setMaxClients(50);
        $server->setCheckOrigin(false);
        $server->setAllowedOrigin('foo.lh');
        $server->setMaxConnectionsPerIp(100);
        $server->setIPCOwner('foo');
        $server->setIPCGroup('www-data');
        $server->setIPCMode(0755);

        $webSocket = WebSocketController::getInstance();

        // timers
        $server->addTimer(250, function () use ($webSocket) {
            $webSocket->socketJobsTimer();
        });
        $server->addTimer(250, function () use ($webSocket) {
            $webSocket->socketBotJobsTimer();
        });
        $server->addTimer(500, function () use ($webSocket) {
            $webSocket->gameEngineTimer();
        });
        $server->addTimer(250, function () use ($webSocket) {
            $webSocket->calculateUsersBetTimer();
        });
        /*$server->addTimer(2500, function () use ($webSocket) {
            $webSocket->updateBalanceTimer();
        });*/

        // Hint: Status application should not be removed as it displays usefull server informations:
        $server->registerApplication('connect', $webSocket);

        $server->run();

        return 1;

how to implement private chat in DemoApplication as in StatusAplication?

I'm using php websocket Bloatless V2.0, because my framework (Zend) only supports php 7.2.

i want to implement private chat like the example in StatusAplication to DemoAplication.

"The Status-Application includes an example for this: https://github.com/bloatless/php-websocket/blob/release/2.1.0/src/Application/StatusApplication.php#L49
Just use the $client->send() Method to send data to a specific client."

Private Chat in StatusAplication
` public function onConnect(Connection $client): void
{
$id = $client->getClientId();
$this->clients[$id] = $client;
$this->sendServerinfo($client);

}

private function sendServerinfo(Connection $client): void
{
if (count($this->clients) < 1) {
return;
}
$currentServerInfo = $this->serverInfo;
$currentServerInfo['clientCount'] = count($this->serverClients);
$currentServerInfo['clients'] = $this->serverClients;
$encodedData = $this->encodeData('serverInfo', $currentServerInfo);
$client->send($encodedData);
}
`

Send Chat in DemoAplikation
`
public function onConnect(Connection $client): void
{
$id = $client->getClientId();
$this->clients[$id] = $client;
}

private function actionEcho(string $text): void
{
$encodedData = $this->encodeData('echo', $text);
foreach ($this->clients as $sendto) {
$sendto->send($encodedData);
}
}
`

if my actionEcho input 2 parameters to be (string $tex, , Connection $client) like the example in sendServerinfo, the socket will error.

How can I add $client->send() in DemoAplication ?

Release v3.0.2

The commit cc7d6d8 fixes a fatal error in which I ran as-well.

As it is the only change on master since v3.0.1 could we get a quick hotfix release?

Best Regards
Mario

Typo

In Server.php line 212
'maxRequetsPerMinute' => $this->maxRequestsPerMinute,

left side Requets without 's' befor 't'

Possibility to close a Websocket Server?

Is there any possibility to close a websocket server instance gracefully? \Bloatless\WebSocket\Server has no method or command to end the while(true) in \Bloatless\WebSocket\Server::run.

Do not accept new connection after running for a while (+/- 1h)

App do not accept new connections after running for a while.
These connections which are already established - exchange with messages happens successfully, but after disconnect - it is not possible to connect anymore to server

this part is still alive:

public function run(): void
    {
        while (true) {
            echo count($this->allsockets)."|";
            $changed_sockets = $this->allsockets;
....

I have this issue with almost default example script: https://github.com/bloatless/php-websocket/blob/master/examples/server.php

Initially thought it is Raspberry Pi memory issue, but the same was tested with MacBook - and issue is the same.
After restart - works again +/- 1h (test was done with 5 messages per sec approx.)

how to do private message using php-websocket Bloatless V2.0 ?

I'm using php websocket Bloatless V2.0, because my framework (Zend) only supports php 7.2. I've been able to send messages through the web socket, but messages can be received by all connected clients (Broadcast). How can you do private messages between the server and only one client, so that messages sent by the server are not received by other connected clients?

StatusApplication may loose clients

the serverClients array store client connections by port, thus if another client ( with different IP ) is connecting with the same port as a previous one -> the first client is lost !

this patch solve the problem :
-------------------- src/Application/StatusApplication.php --------------------
index 6469453..7400d94 100644
@@ -97,7 +97,7 @@ class StatusApplication extends Application
*/
public function clientConnected(string $ip, int $port): void
{

  •    $this->serverClients[$port] = $ip;
    
  •    $this->serverClients[$ip.":".$port] = date("U");
       $this->serverClientCount++;
       $this->statusMsg('Client connected: ' . $ip . ':' . $port);
       $data = [
    

@@ -118,10 +118,10 @@ class StatusApplication extends Application
*/
public function clientDisconnected(string $ip, int $port): void
{

  •    if (!isset($this->serverClients[$port])) {
    
  •    if (!isset($this->serverClients[$ip.":".$port])) {
           return;
       }
    
  •    unset($this->serverClients[$port]);
    
  •    unset($this->serverClients[$ip.":".$port]);
       $this->serverClientCount--;
       $this->statusMsg('Client disconnected: ' . $ip . ':' . $port);
       $data = [
    

Use php5

Hello
How can i use version 1.0 for php 5.
Thanks

WebSocketServer behind haproxy

Hi.

When the server is behind a HaProxy, the HTTP headers come in "lowercase"

diff --git a/src/Connection.php b/src/Connection.php
index 98ea406..e9269c2 100644
--- a/src/Connection.php
+++ b/src/Connection.php
@@ -109,13 +109,13 @@ class Connection
         foreach ($lines as $line) {
             $line = chop($line);
             if (preg_match('/\A(\S+): (.*)\z/', $line, $matches)) {
-                $headers[$matches[1]] = $matches[2];
+                $headers[ strtolower( $matches[1] )] = $matches[2];
             }
         }
 
         // check for supported websocket version:
-        if (!isset($headers['Sec-WebSocket-Version']) || $headers['Sec-WebSocket-Version'] < 6) {
-            $this->log('Unsupported websocket version.');
+        if (!isset($headers['sec-websocket-version']) || $headers['sec-websocket-version'] < 6) {
+            $this->log('Unsupported websocket version.'.print_r( $headers, true ) );
             $this->sendHttpResponse(501);
             stream_socket_shutdown($this->socket, STREAM_SHUT_RDWR);
             $this->server->removeClientOnError($this);
@@ -124,8 +124,8 @@ class Connection
 
         // check origin:
         if ($this->server->getCheckOrigin() === true) {
-            $origin = (isset($headers['Sec-WebSocket-Origin'])) ? $headers['Sec-WebSocket-Origin'] : '';
-            $origin = (isset($headers['Origin'])) ? $headers['Origin'] : $origin;
+            $origin = (isset($headers['sec-websocket-origin'])) ? $headers['sec-websocket-origin'] : '';
+            $origin = (isset($headers['origin'])) ? $headers['origin'] : $origin;
             if (empty($origin)) {
                 $this->log('No origin provided.');
                 $this->sendHttpResponse(401);
@@ -144,13 +144,13 @@ class Connection
         }
 
         // do handyshake: (hybi-10)
-        $secKey = $headers['Sec-WebSocket-Key'];
+        $secKey = $headers['sec-websocket-key'];
         $secAccept = base64_encode(pack('H*', sha1($secKey . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')));
         $response = "HTTP/1.1 101 Switching Protocols\r\n";
         $response .= "Upgrade: websocket\r\n";
         $response .= "Connection: Upgrade\r\n";
         $response .= "Sec-WebSocket-Accept: " . $secAccept . "\r\n";
-        if (isset($headers['Sec-WebSocket-Protocol']) && !empty($headers['Sec-WebSocket-Protocol'])) {
+        if (isset($headers['sec-websocket-protocol']) && !empty($headers['sec-websocket-protocol'])) {
             $response .= "Sec-WebSocket-Protocol: " . substr($path, 1) . "\r\n";
         }
         $response .= "\r\n";

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.