Курс Raku / Функційне, конкурентне, реактивне та веб-програмування / Веб-програмування / Основи Cro

WebSocket

Розмова через HTTP є одноразовою: клієнт питає, сервер відповідає, і з’єднання завершено. WebSocket підносить це до довготривалого двобічного каналу — будь-яка сторона може надіслати повідомлення будь-якої миті, а саме цього потребують чати, ігри та живі панелі. Cro підтримує WebSockets із коробки, і обробник WebSocket збудовано з реактивних засобів, які ви вже знаєте: потік повідомлень є потоком значень.

Ось крихітний сервер чату. Кожне повідомлення, яке надсилає будь-який клієнт, сервер переводить у верхній регістр і транслює всім приєднаним — тож те, що повертається, видимо не є тим, що виходило, і це доводить, що дані справді зробили повний оберт:

use Cro::HTTP::Router;
use Cro::HTTP::Router::WebSocket;
use Cro::HTTP::Server;

my $chat = Supplier.new;

my $application = route {
    get -> 'chat' {
        web-socket -> $incoming {
            supply {
                whenever $incoming -> $message {
                    $chat.emit(uc(await $message.body-text));
                    LAST { done }
                }
                whenever $chat -> $text {
                    emit $text;
                }
            }
        }
    }
}

my $server = Cro::HTTP::Server.new(
    :host('127.0.0.1'),
    :port(8080),
    :application($application),
);
$server.start;

say 'Chat server on ws://127.0.0.1:8080/chat — press Ctrl-C to stop';

react {
    whenever signal(SIGINT) {
        $server.stop;
        done;
    }
}

Погляньте на блок web-socket — це реактивна частина курсу в мініатюрі. Кожен приєднаний клієнт отримує supply із двома whenever: один стежить за $incoming, тобто повідомленнями, що надходять від цього клієнта, переводить кожне у верхній регістр і штовхає в спільний постачальник $chat; другий стежить за $chat і emit-ить кожен транслюваний текст назад до клієнта. Один Supplier, до якого підключається кожне з’єднання, і є всією чат-кімнатою.

Маленьке LAST { done } важить чимало. Це фазер, з яким ви зустрічалися в циклах; у блоці whenever він спрацьовує, коли потік, за яким стежать, завершується — тобто коли цей клієнт від’єднується. Виклик done там закриває весь потік з’єднання, відписуючи його від $chat. Без цього whenever $chat клієнта, що пішов, тинявся б і далі, а наступна трансляція спробувала б записати в закритий сокет і надрукувала б помилку на сервері.

Браузер розмовляє з ним кількома рядками JavaScript, але так само може й інша програма на Raku — Cro надає й клієнтський бік:

use Cro::WebSocket::Client;

my $conn = await Cro::WebSocket::Client.connect('ws://127.0.0.1:8080/chat');

my $got = Promise.new;

$conn.messages.tap(-> $message {
    say 'Received: ', await $message.body-text;
    $got.keep;
});

say 'Sending:  Hello, chat!';
$conn.send('Hello, chat!');

await $got;
await $conn.close;

.send вистрілює повідомлення й повертається одразу; відповідь приходить пізніше, в іншому потоці, у блоці .tap. Тому головна лінія має зачекати перед закриттям — і замість спати й сподіватися, вона чекає на Promise, яку підключення виконує тієї ж миті, коли надходить відповідь: засоби конкурентності з попередніх розділів цієї частини роблять рівно те, для чого їх створили.

Запустіть сервер в одному терміналі, а цей клієнт — в іншому. Клієнт друкує обидва напрямки розмови:

Sending:  Hello, chat!
Received: HELLO, CHAT!

Те, що вийшло малими літерами, повернулося великими — повідомлення помандрувало до сервера, було там перетворене й помандрувало назад тим самим з’єднанням. Ось двобічний канал у дії. Запустіть клієнт одразу в кількох терміналах — і кожне повідомлення з’явиться в усіх: робочий чат менш ніж у тридцяти рядках Raku.

Цим прикладам потрібен встановлений Cro (zef install cro).

Це доречне місце, щоб завершити курс. Сервіс, клієнт, що його викликає, і живий двобічний канал між ними стоять на всьому, що було до них, — на об’єктах і винятках, регексах і граматиках, а також на функційних, конкурентних та реактивних засобах цієї частини, — а код при цьому лишається коротким і каже саме те, що ви маєте на увазі. Від байтів у сокеті до чат-кімнати на один екран коду: оце і є Raku.

Навігація курсом

Запит до публічного API   |   Другий маршрут


💪 Або перейдіть одразу до вправ цього розділу.