zeus/pusher

v1.0.5 2024-02-04 10:08 UTC

This package is auto-updated.

Last update: 2024-10-03 05:21:26 UTC


README

Socket Channels

这是一个socket库,它是一个高级库,其目的是订阅socket上的通道并发送消息到通道,以及离开通道...

安装

composer require zeus/pusher

Socket服务器管理

我们使用从AbstractSocketClientHandler对象继承的对象来管理整个socket服务器...在下面的例子中,是ClientHandler对象,但这完全取决于您的意愿。让我们,例如,让我们向所有人发送消息。

server.php

use Zeus\Pusher\AbstractSocketClientHandler;
use Zeus\Pusher\SocketServer;


class ClientHandler extends AbstractSocketClientHandler
{


    #[\Override]
    public function run(): void
    {
        $this->sendTo()->everyone($this->getMessage());
    }
}


$socketServer = new SocketServer(ClientHandler::class);
$socketServer->serve('127.0.0.1', 8080);

在终端中运行

php server.php

js websocket代码

<script>
    const socket = new WebSocket('ws://127.0.0.1:8080');

    socket.addEventListener('open', (event) => {
        console.log('WebSocket connection opened');
        socket.send('test message');
    });

    socket.addEventListener('message', (event) => {
        const message = event.data;
        alert(`Received message: ${message}`);
    });

    socket.addEventListener('close', (event) => {
        console.log('socket is closed', JSON.stringify(event));
    });


</script>
</body>
</html>

支持通配符

您可以在通道管理中使用通配符。在这里,join方法是在订阅通道的同时,您还可以使用leave方法取消对通道的订阅,您可以对通道进行订阅并接收通知,实际上,主要逻辑就像真实场景一样,如果我们想取消对通道的通知,我们使用leave方法,就是这样。

此外,还有一些方法,除了hasJoin、join和leave等方法之外,这完全是用于更好地管理通道...

在下面的例子中,我们订阅了it.backendit.frontend通道,并且开始是it.我们向所有以开始的所有通道发送通知,这里的目的是分组通道。

首先,我们使用$this->getClient()方法来获取已连接客户端...

class Handler extends AbstractSocketClientHandler
{

    #[\Override]
    public function run(): void
    {

        $this->join('it.frontend', $this->getClient());
        $this->join('it.backend', $this->getClient());

        $this->sendTo()->channel('it.*', 'hello world');


    }
}


$socketServer = new SocketServer(Handler::class);
$socketServer->serve('127.0.0.1', 8080);

通道的客户端

例如,您可以通过默认方式获取所有订阅了名为public的通道的客户端。

您可以接收系统中的所有通道并吸引连接到这些通道的客户端以执行事务。

class Handler extends AbstractSocketClientHandler
{

    #[\Override]
    public function run(): void
    {
      //get all channels
       $channels=$this->getChannels();
       
       //get all clients of the public channel
       $clients=$this->findChannel('public')->getClients();
       foreach($clients as $client){
           $ip=$this->getRemoteHost($client);
           if('x.x.x.x'===$ip){
               $this->disconnect($client);
           }
       }
       
    }
}


$socketServer = new SocketServer(Handler::class);
$socketServer->serve('127.0.0.1', 8080);

一些通道的方法,但别忘了还有很多...

class Handler extends AbstractSocketClientHandler
{

    #[\Override]
    public function run(): void
    {
        $this->leave('it.backend', $this->getClient());
        $this->hasJoin('it.backend', $this->getClient());
        $this->hasChannel('it.backend');
        $this->findChannel('it.backend');
        $this->getChannels();
        //and more
    }
}

注意:目前leave方法不支持通配符。它不起作用,我认为这并不合适,因为它有完全不可逆的影响

$this->leave('it.*');

支持json

让我们使用来自前端的json订阅通道并向通道发送消息。js代码

此示例根据来自客户端的json数据订阅通道并向已订阅的通道发送消息。

在前端

const message = document.querySelector('#message');
const channel = document.querySelector('#channel');
const data = {channel: channel.value, message: message.value};
socket.send(JSON.stringify(data));

在后端

/**
 * This is a PHP code that extends
 * the AbstractSocketClientHandler class
 * and implements the run() method.
 * It checks if the received message is in JSON format,
 * joins the specified channel, and sends the message to that channel.
 */

class Handler extends AbstractSocketClientHandler
{

    /**
     * @throws JsonException
     */
    #[\Override]
    public function run(): void
    {
        if ($this->isMessageJson()) {

            $this->join(
                $this->getJsonValue('channel'),
                $this->getClient()
            );

            $this->sendTo()->channel(
                $this->getJsonValue('channel'),
                $this->getJsonValue('message')
            );
        }
    }
}


$socketServer = new SocketServer(Handler::class);
$socketServer->serve('127.0.0.1', 8080);

客户端

PHP客户端不监听socket,那么它的目的是什么?

它允许向已连接的连接发送消息。您甚至可以使此客户端与API兼容并发送消息到语言无关的socket。

此代码将消息转发到已连接的服务器并关闭连接。这样,它提供了极大的灵活性,您甚至可以创建一个API socket服务器,是的,听起来不错,不是吗?

use Zeus\Pusher\SocketClient;                                     
                                                                  
                                                                  
$socketClient = new SocketClient('0.0.0.0', 8080);                
                                                                  
$socketClient->sleep(1);                                          
                                                                  
$message=json_encode([
    'channel'=>'backend',
    'message'=>'a new backend developer has applied'
    ]);    
         
$socketClient->send($message);                                    
                                                                  
return $socketClient->read();                                     

发送自定义消息

您可以向特定客户端发送消息,例如,让我们只向当前连接的客户端发送消息。

class Handler extends AbstractSocketClientHandler
{

    #[\Override]
    public function run(): void
    {

        //send yourself a message
        $this->sendTo()->client($this->getClient(),'Hello, world');
        //send it to everyone except himself
        $this->sendTo()->exceptClient($this->getClient(),'Hello world');
    }
}


$socketServer = new SocketServer(Handler::class);
$socketServer->serve('127.0.0.1', 8080);

通过socket id发送消息

可以使用socket ID来发送自定义消息。下面是向特定socket ID发送消息的示例。这是一个专门为发送个人消息而设计的功能。

class Handler extends AbstractSocketClientHandler
{

    /**
     */
    #[\Override]
    public function run(): void
    {
        $this->sendTo()->id($this->getId(), 'hello');
    }
}

Websocket路由

您可以使用js websocket和路由路径创建命名空间,以下是js和php代码的示例。

class Handler extends AbstractSocketClientHandler
{

    /**
     */
    #[\Override]
    public function run(): void
    {
        $this->sendTo()->route('/chat', 'hello world');
    }
}


$socketServer = new SocketServer(Handler::class);
$socketServer->serve('0.0.0.0',8080);

javascript websocket

const socket = new WebSocket('ws://0.0.0.0:8080/chat');

socket.onopen = function (ev) {
    console.log('opened connection');
    console.log(ev);
};

socket.onmessage = function (ev) {
    console.log(ev);
    alert(ev.data);
};

有Route和hasRoute方法

  1. hasRoute: 是否有客户端通过/chat路由连接?。

  2. isRoute: 当前客户端是否通过/chat路由连接?。

class Handler extends AbstractSocketClientHandler
{

    /**
     */
    #[Override]
    public function run(): void
    {
        $this->hasRoute('/chat');//Is there any client connecting via /chat route?
        $this->isRoute('/chat'); //Is the current client connected via the /chat route?
    }
}


$socketServer = new SocketServer(Handler::class);
$socketServer->serve(
  '0.0.0.0',
   8080
);

客户端的主机地址

让我们断开一个IP地址的套接字连接
你可以获取已连接客户端的主机或IP地址。这样,你可以拒绝一个IP地址,例如,让我们断开IP值为x.x.x.x的客户端。

class Handler extends AbstractSocketClientHandler
{

    /**
     */
    #[\Override]
    public function run(): void
    {
        $host=$this->getRemoteHost();
        if('x.x.x.x'===$host){
            $this->disconnect($this->getClient());
        }
    }
}

获取客户端的状态

你可以获取任何客户端的状态

class Handler extends AbstractSocketClientHandler
{

    /**
     */
    #[\Override]
    public function run(): void
    {
        //type 1 using 
        
        $status=$this->getStatus();
        
        //type 2 using
        
        $clients=$this->findChannel('it.backend')->getClients();
        foreach($clients as $client){
            $status=$this->getStatus($client);
            print_r($status);
        }
      
    }
}

### More

更多信息

现在,如果你想要添加频道广播和发送类,有一种方法可以做到这一点。例如,让我们为发送创建一个方法并使用它。

class Handler extends AbstractSocketClientHandler
{

    /**
     */
    #[\Override]
    public function run(): void
    {
        Send::method(
            'test',
            static fn(Socket $client) => socket_write($client, Message::encode('test'))
        );


        Channel::method(
            'empty',
            static fn() => $this->clients=[])
        );

        $this->sendTo()->test($this->getClient());
        $this->findChannel('it.backend')->empty();
    }
}

如果你注意到了,'test'和'empty'方法实际上并不存在,我们模拟了它们,好像它们存在一样。

待续...
[|||||||||||||||]