Simple WebSocket server implemented in PHP.

Overview

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

Comments
  • StatusApplication may loose 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 = [
      
    opened by DanRoche 4
  • WebSocketServer behind haproxy

    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";
    
    
    opened by mateusan 3
  • Adding a Client.php Message Listener

    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.

    opened by JavanXD 3
  • Server drops messages

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

    opened by kputyra 3
  • Make IPC optional

    Make IPC optional

    By passing null as the IPC path, opening a UNIX socket can be skipped.

    I want to be able to do this in order to start a websocket server on Windows without it failing to open a UNIX socket.

    This change is backwards-compatible and the earliest supported version is PHP 7.4.

    opened by ricardoboss 2
  • Release v3.0.2

    Release v3.0.2

    The commit https://github.com/bloatless/php-websocket/commit/cc7d6d8a2ac7635efd9c3fb5e5f5fa0828e5f151 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

    opened by mario-deluna 2
  • how to do private message using php-websocket Bloatless V2.0 ?

    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?

    opened by HardiYana-creator 2
  • Use with WebRTC

    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??

    opened by The-Code-Monkey 2
  • Remove final from private magic method

    Remove final from private magic method

    In order to fix PHP Warning: Private methods cannot be final as they are never overridden by other classes in .../src/Application/Application.php on line 19

    Fixes #36

    opened by r-martins 1
  •  Class

    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.

    opened by MasumNishat 1
  • Fix Warning on PHP8

    Fix Warning on PHP8

    PHP8 emits a warning when final and private are used on the same method.

    Warning: Private methods cannot be final as they are never overridden by other classes in {file} on line {line}
    

    Example: https://3v4l.org/p2Ccp

    opened by mgrinspan 1
  • Possibility to close a Websocket Server?

    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.

    opened by raubv0gel 0
  • Added LoggerAwareInterface support and abstract implementation

    Added LoggerAwareInterface support and abstract implementation

    This PR adds support for the LoggerAwareInterface by passing the servers Logger to the application being registered.

    This way, applications can log to the same interface as the server while maintaining full backwards compatibility.

    opened by ricardoboss 1
  • Fixed some typos and code styling, dropped support for PHP 7.4 and use new PHP 8 only features

    Fixed some typos and code styling, dropped support for PHP 7.4 and use new PHP 8 only features

    This PR drops support for PHP 7.4 and leverages some of PHP 8s features. Apart from requiring PHP 8, the API is still the same, meaning apps already running on PHP 8 won't be broken.

    Along with that, I fixed some typos and some code style inconsistencies and added/updated DocBlocks.

    opened by ricardoboss 1
  • how to implement private chat in DemoApplication as in StatusAplication?

    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 ?

    opened by HardiYana-creator 0
  • Do not accept new connection after running for a while (+/- 1h)

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

    opened by raiviszel 0
Releases(v3.0.3)
  • v3.0.3(Aug 20, 2022)

  • v3.0.2(Mar 17, 2022)

    Changelog

    • Bugfix: socket_create Error on Windows machines (Thx @ineternet )
    • Bugfix: Fix type error: Typed property Bloatless\WebSocket\Server::$timers must not be accessed before initialization (Thx @mateusan )
    • Feature: Add getClientHeaders method (Thx @haugli92 )
    Source code(tar.gz)
    Source code(zip)
  • v3.0.1(Dec 28, 2021)

    Changelog

    • Bugfix: Add possibility to set owner/group/mode for ICP socket
    • Feature: Add possibility to add PSR-3 loggers
    • Feature: Introduce composer to manage dependencies
    • Bugfix: Fix some typos
    • Bugfix: Fix PHP warning on final and private keywords
    Source code(tar.gz)
    Source code(zip)
  • v3.0.0(Jan 12, 2021)

    Changelog

    • Code refactoring and cleanup
    • Remove request-limit feature
    • Min. PHP version is now 7.4
    • Added Push-Client/IPC feature (see README)
    • Removed Websocket-Client
    • Added improved and reorganized examples
    • Updated README
    • Switched to MIT license

    Attention This version contains breaking changes:

    • Method public function onIPCData(array $data): void; was added to ApplicationInterface.
    • Class Client (Websocket Client) was removed.
    Source code(tar.gz)
    Source code(zip)
  • v2.1.0(Nov 15, 2020)

    Changelog

    • Add periodic timers (by chrisharrison https://github.com/bloatless/php-websocket/pull/16/)
    • Fix bug in status application (by DanRoche https://github.com/bloatless/php-websocket/pull/21)
    Source code(tar.gz)
    Source code(zip)
  • v2.0.1(Apr 5, 2020)

  • v2.0(Mar 10, 2019)

    Changelog

    • Move project to Bloatless namespace.
    • Migration to PHP 7.
    • Remove SSL Support (Use reverse proxy instead!)
    • Remove support for binary data.
    • Remove classloader.
    • General code cleanup
    • Migrate CoffeScript to Vanilla JS.
    • Remove jQuery dependency.
    Source code(tar.gz)
    Source code(zip)
  • v1.0(Mar 10, 2019)

Owner
bloatless.org
The goal of bloatless.org is to provide simple, bloat-free web applications with minimal complexity.
bloatless.org
Asynchronous server-side framework for network applications implemented in PHP using libevent

phpDaemon https://github.com/kakserpom/phpdaemon Asynchronous framework in PHP. It has a huge number of features. Designed for highload. Each worker i

Vasily Zorin 1.5k Nov 30, 2022
PHP Websocket Server that is compatible with socket.io

PHP SocketIO Server PHP Websocket Server that is compatible with socket.io So far the function use in this package is almost same with the naming in s

Cydrick Nonog 7 Dec 21, 2022
An asynchronous event driven PHP socket framework. Supports HTTP, Websocket, SSL and other custom protocols. PHP>=5.3.

Workerman What is it Workerman is an asynchronous event-driven PHP framework with high performance to build fast and scalable network applications. Wo

walkor 10.2k Dec 31, 2022
REST APIs using Slim framework. Implemented all CRUD operations on the MySql database

PHP REST API using slim framework and CRUD operations ?? Hi there, this is a simple REST API built using the Slim framework. And this is for the folks

Hanoak 2 Jun 1, 2022
FrankenPHP is a modern application server for PHP built on top of the Caddy web server

FrankenPHP: Modern App Server for PHP FrankenPHP is a modern application server for PHP built on top of the Caddy web server. FrankenPHP gives superpo

Kévin Dunglas 2.8k Jan 2, 2023
A multithreaded application server for PHP, written in PHP.

appserver.io, a PHP application server This is the main repository for the appserver.io project. What is appserver.io appserver.io is a multithreaded

appserver.io 951 Dec 25, 2022
Framework for building extensible server-side progressive applications for modern PHP.

Chevere ?? Subscribe to the newsletter to don't miss any update regarding Chevere. Framework for building extensible server-side progressive applicati

Chevere 65 Jan 6, 2023
PHP Web Socket server

Important ⛔️ This project is no longer maintained ⛔️ We urge you to look for a replacement. Description WebSocket Server and Client library for PHP. W

Chris Tanaskoski 346 Nov 8, 2022
jojo, another http server written in PHP 8.0

جوجو | jojo جوجو، وب‌سروری در ابعاد جوجه برای کارهای کوچک داستان نوشتن جوجو وب‌سروری که تنظیمات مودم TP-link TD-8811 توی اتاقم رو serve میکنه اسمش mic

Amirhossein Baghaie 6 Dec 25, 2022
Socks5 proxy server written in Swoole PHP

php-socks This is a Socks5 proxy server implementation built with PHP & Swoole. To start the proxy server, clone this repo, run composer install to in

Nazmul Alam 3 Jan 23, 2022
💾 High-performance PHP application server, load-balancer and process manager written in Golang. RR2 releases repository.

RoadRunner is an open-source (MIT licensed) high-performance PHP application server, load balancer, and process manager. It supports running as a serv

Spiral Scout 45 Nov 29, 2022
🤯 High-performance PHP application server, load-balancer and process manager written in Golang

RoadRunner is an open-source (MIT licensed) high-performance PHP application server, load balancer, and process manager. It supports running as a serv

Spiral Scout 6.9k Jan 3, 2023
A server side alternative implementation of socket.io in PHP based on workerman.

phpsocket.io A server side alternative implementation of socket.io in PHP based on Workerman. Notice Only support socket.io v1.3.0 or greater. This pr

walkor 2.1k Jan 6, 2023
Simple PHP framework that helps you quickly understand and write simple APIs.

Lightweight-PHP-Framework-For-APIs. Simple PHP framework that helps you quickly understand and write simple APIs. Installation Use the package manager

Youssef Hajjari 24 Jul 22, 2022
Simple PHP framework that helps you quickly understand and write simple APIs.

Lightweight PHP Framework For Web and APIs PHP framework that helps you write quickly simple but powerful web apps and APIs Installation Use the packa

Youssef Hajjari 24 Jul 22, 2022
A pocketmine-mp server that we develop live on Twitch every Saturday from 8pm to 10pm (FR)

Server A pocketmine-mp server that we develop live on Twitch every Saturday from 8pm to 10pm (FR) Contributing Pull requests are welcome. For major ch

Gaëtan H 11 Oct 9, 2022
Phpactor Language Server

This package provides a platform for building a Language Server according to the Language Server Specification

Phpactor 27 Dec 24, 2022
A easy way to install your basic yii projetc, we have encrypt database password in phpfile, my class with alot funtions to help you encrypt and decrypt and our swoole server install just run ./yii swoole/start and be happy!

Yii 2 Basic Project Template with swoole and Modules Yii 2 Basic Project Template is a skeleton Yii 2 application best for rapidly creating small proj

null 3 Apr 11, 2022
TCP Worker Client for RoadRunner 2.0 application server

RoadRunner TCP Plugin RoadRunner is an open-source (MIT licensed) high-performance PHP application server, load balancer, and process manager. It supp

Spiral Scout 8 Nov 26, 2022