Курс по 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   |   Втори маршрут


💪 Или преминете направо към упражненията в този раздел.