NAME
Punk::WebSocket::Room - pub/sub groups of WebSocket connections
SYNOPSIS
use Punk::WebSocket::Room;
sub join_chat {
my ($c, $ws) = @_;
my $room = Punk::WebSocket::Room->named('lobby');
$ws->on(open => sub { $room->join($_[0]) });
$ws->on(message => sub {
my ($ws, $text) = @_;
$room->broadcast($text, $ws); # everyone except the sender
});
$ws->on(close => sub { $room->leave($_[0]) });
}
DESCRIPTION
A named group of connections, with one encode per broadcast: the frame is built once and the same bytes are queued to every member.
Membership is held weakly and pruned on every access, so a connection that goes away leaves its rooms by itself - calling "leave" from a close handler is tidy but not required.
A broadcast reaches the whole pool. Each worker holds only the connections it accepted, so a broadcast is published on Hyperman's cross-worker message bus and every worker fans the frame out to its own members. The frame is encoded once, by the worker that broadcast it, and the same bytes travel - so a room of a thousand people across four workers does one encode, not four.
There is one delivery path. The publishing worker does not also send locally; it receives its own publication like everybody else. Sending locally and publishing would deliver twice to the members in front of you, and would leave two paths to drift apart - so a bug in the shared one would be invisible to anyone testing on a single worker.
Where there is no pool - a server that is not Hyperman, a Hyperman older than 0.28, Windows, or a compiler without the atomics the shared ring needs - a room is local, which is what it always was. It does not pretend otherwise.
The count a broadcast returns is what this worker delivered. A total across the pool is not knowable at the moment of the call without waiting for it, and a plausible number that is not the truth is precisely the fault this replaced.
METHODS
named($name)
The worker's room of that name, created on first use.
name
The name the room was created with.
join($ws) / leave($ws) / has($ws)
clients
The live members, as a list. List context only: like any list-returning XS call, scalar $room->clients yields the last member, not a count - that is what count is for.
count
How many.
broadcast($text, $except?)
broadcast_binary($bytes, $except?)
Send to every open member, optionally skipping one connection (usually the sender). Returns the number sent by this worker.
Under several workers the broadcast reaches the members every worker holds, with or without $except: the skipped connection belongs to one worker, which leaves it out, and the others deliver to everyone they have.
close_all($code = 1000, $reason = '')
Close every member and empty the room.
clear
Empty the room without closing anything.
AUTHOR
LNATION <email@lnation.org>
LICENSE AND COPYRIGHT
This software is Copyright (c) 2026 by LNATION <email@lnation.org>.
This is free software, licensed under:
The Artistic License 2.0 (GPL Compatible)