Message processing

Swoft provides flexible websocket usage, support for customization and handling of messages by framework hosting.

  • If you don't add the @OnMessage handler in the ws module class, the framework will automatically host this phase, parse the message and distribute it to different methods depending on the route.
  • If you bind the @OnMessage handler in the ws module class, Swoft thinks that you want to handle this phase yourself, the framework will not process it.

Note: The use of this document is based on the routing of messages hosted by the framework.

annotation

WsController

@WsController message controller annotation tag @WsController

  • Annotation class: Swoft\WebSocket\Server\Annotation\Mapping\WsController
  • Scope: CLASS
  • Have attributes:
    • prefix String message routing prefix

MessageMapping

Method annotation @MessageMapping marks the specific message processing method, similar to the action in the http controller.

  • Annotation class: Swoft\WebSocket\Server\Annotation\Mapping\MessageMapping
  • Scope of action: METHOD
  • Have attributes:
    • command string message command name

The complete message routing path is the above preifx and command stitched together by point PREFIX.COMMAND

Message parsing

The data format used for ws communication may be different for different users or usage scenarios. Therefore, when writing a ws module, you need to bind the message parser.

Built-in parser

  • Swoft\WebSocket\Server\MessageParser\RawTextParser simple string
  • Swoft\WebSocket\Server\MessageParser\TokenTextParser simple token string protocol ( for testing purposes )
  • Swoft\WebSocket\Server\MessageParser\JsonParser simple JSON data protocol

JSON protocol communication data structure:

 {
    "cmd": "message route path. eg: home.index", // type: string
    "data": "message data", // type: mixed(array|string|int)
    "ext": {"ip": "xx", "os": "mac"}, // optional, type: array
} 

Example

Defining the ws module

Note To bind a message processing controller, you usually need to bind your message parser, you can use several simple parsers built in, or you can customize it as needed.

 <?php declare(strict_types=1);

namespace App\WebSocket;

use App\WebSocket\Chat\HomeController;
use Swoft\Http\Message\Request;
use Swoft\WebSocket\Server\Annotation\Mapping\OnOpen;
use Swoft\WebSocket\Server\Annotation\Mapping\WsModule;
use Swoft\WebSocket\Server\MessageParser\TokenTextParser;
use function server;

/**
 * Class ChatModule
 *
 * @WsModule(
 *     "/chat",
 *     messageParser=TokenTextParser::class,
 *     controllers={HomeController::class}
 * )
 */
class ChatModule
{
    /**
     * @OnOpen()
     * @param Request $request
     * @param int     $fd
     */
    public function onOpen(Request $request, int $fd): void
    {
        server()->push($request->getFd(), "Opened, welcome!(FD: $fd)");
    }
} 
  • The defined ws module path is /chat
  • The bound controllers are: HomeController::class
  • Bind a built-in message parser

Note When defining the Ws module here, it is bound to a message parser that comes with the framework. TokenTextParser::class has a decode method built in to parse the data.

     // 默认为字符串解析,消息路由格式 `控制器.方法:数据`
    public function decode(string $data): Message
    {
        // use default message command
        $cmd = '';
        if (strpos($data, ':')) {
            [$cmd, $body] = explode(':', $data, 2);
            $cmd = trim($cmd);
        } else {
            $body = $data;
        }

        return Message::new($cmd, $body);
    } 

Message controller

Note You must use the annotation @WsController and @MessageMapping

 <?php declare(strict_types=1);

namespace App\WebSocket\Chat;

use Swoft\Session\Session;
use Swoft\WebSocket\Server\Annotation\Mapping\MessageMapping;
use Swoft\WebSocket\Server\Annotation\Mapping\WsController;

/**
 * Class HomeController
 *
 * @WsController()
 */
class HomeController
{
    /**
     * Message command is: 'home.index'
     *
     * @return void
     * @MessageMapping()
     */
    public function index(): void
    {
        Session::mustGet()->push('hi, this is home.index');
    }

    /**
     * Message command is: 'home.echo'
     *
     * @param string $data
     * @MessageMapping()
     */
    public function echo(string $data): void
    {
        Session::mustGet()->push('(home.echo)Recv: ' . $data);
    }

    /**
     * Message command is: 'home.ar'
     *
     * @param string $data
     * @MessageMapping("ar")
     *
     * @return string
     */
    public function autoReply(string $data): string
    {
        return '(home.ar)Recv: ' . $data;
    }
} 

Note that since v2.0.6 , when receiving websocket raw data through parameter injection, you need to add the type string . For example: public function echo(string $data)

retrieve data

There are several ways to get the data information requested by a message.

  • Method 1 through parameter injection
 use Swoft\WebSocket\Server\Message\Message;
use Swoft\WebSocket\Server\Message\Request;

...

// inject Message object
public function autoReply(Message $msg): string
{
    return $msg->toString();
}

// inject Request object
public function autoReply(Request $req): string
{
    return $req->getMessage()->toString();
} 
  • Method 2 is obtained through context
 use Swoft\WebSocket\Server\Message\Message;

...

public function autoReply(): string
{
    $msg = context()->getMessage();
} 

More ways to get it:

 use Swoft\WebSocket\Server\Message\Request;

$req = context()->getRequest();

/** @var \Swoft\WebSocket\Server\Message\Message $msg */
$msg = $req->getMessage();

/** @var \Swoole\WebSocket\Frame $msg */
$frm = $req->getFrame(); 

Note that the Request here refers to the request object of the message phase, which is different from the request object when opening the connection.

Access service

Start our service according to the Ws模块 , 消息解析器 , 消息控制器 etc. defined above. Then open the webscoket debugging tool, link the Ws address: ws://localhost:port/chat and test to send a content

     Send: testWS
    Recv: hi, this is home.index
    Send: home.echo:这是数据
    Recv: (home.echo)Recv: 这是数据 
/docs/2.x/en/websocket-server/message-route.html
progress-bar