Курс по 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 | Втори маршрут →
💪 Или преминете направо към упражненията в този
раздел.