Real-time chat (Mojolicious::Lite)
This is a sample of real-time chat using WebSocket. Even if you open multiple screens, the writing will be reflected immediately on the other screens.
[f: id: perlcodesample: 20100418234248p: image]
Source code
use strict;
use warnings;
use Mojolicious::Lite;
my $clients = {};
websocket'/' => sub {
my $self = shift;
#Client id
my $cid = "$self";
#Resistance controller
$clients->{$cid} {controller} = $self;
#Receive message
$self->on('message' => sub {
my ($self, $message) = @_;
#Send message to all clients
foreach my $cid (keys%$clients) {
$clients->{$cid} {controller}->send($message);
}
});
# Finish
$self->on('finish' => sub {
# Remove client
delete $clients->{$cid};
});
};;
get'/' =>'index';
app->start;
__DATA__
@@index.html.ep
%my $url = $self->req->url->to_abs->scheme($self->req->is_secure?'Wss':'ws')->path('/');
<! doctype html>
<html>
<head>
<meta http-equiv = "Content-Type" content = "text / html; charset = UTF-8">
<title> Mojo Websocket Demo </title>
<script type = "text / javascript">
// only load the flash fallback when needed
if (! ('WebSocket' in window)) {
document.write ([[
'<scr' +'ipt type = "text / javascript" src = "web-socket-js / swfobject.js"> </scr'+'ipt>',
'<scr' +'ipt type = "text / javascript" src = "web-socket-js / FABridge.js"> </scr'+'ipt>',
'<scr' +'ipt type = "text / javascript" src = "web-socket-js / web_socket.js"> </scr'+'ipt>'
] .join(''));
}
</script>
<script type = "text / javascript">
if (WebSocket.__initialize) {
// Set URL of your WebSocketMain.swf here:
WebSocket.__swfLocation ='web-socket-js / WebSocketMain.swf';
}
// example copied from web-socket-js / sample.html
var ws, input, clock;
function init () {
// Connect to Web Socket.
ws = new WebSocket ('<%= $url%>');
// Receive message
ws.onmessage = function (e) {
// Write message
var message = document.createElement ('div');
message.appendChild (document.createTextNode (e.data));
var display = document.getElementById ('display');
display.appendChild (message);
};;
}
function sendChatMessage () {
var input = document.getElementById ('message-box');
var message = input.value;
// Send message
ws.send (message);
input.value = "";
}
window.onload = init;
</script>
</head>
<div id = "display" style = "width: 500px; height: 200px; border: 1px solid black"> </div>
<form onsubmit = "sendChatMessage (); return false;">
<input size = "60" type = "text" id = "message-box">
<input type = "submit" onclick = "sendChatMessage (); return false;" value = Send>
</form>
</html>
Real-time chat version using Redis that also supports production environment + WebSocket reverse proxy
I will also introduce the logic of the real-time chat of the version using Redis that also supports the production environment WebSocket reverse proxy.
A version that runs on Mojolicious's production server hypnotoad, using Apache and nginx WebSocket reverse proxies.
It is realized by combining the pub / sub function of Redis.
Please modify the path part from "/" to the required one if necessary. Please also correct the path "/" on the first line of index.html.ep. For Apache, you need to separate the paths for WebSockets.
use strict;
use warnings;
use Mojolicious::Lite;
websocket'/' => sub {
my $self = shift;
my $tx = $self->tx;
my $redis = Mojo::Redis->new;
my $pub = $redis->pubsub;
# message from Redis
my $sub = $pub->listen('messages', sub {
my ($sub, $message) = @_; # $channel == messages
$tx->send($message);
});
# message from websocket
$self->on(message => sub {
my ($self, $message) = @_;
$pub->notify(messages => $message);
});
#need to clean up after websocket close
$self->on(finish => sub {
undef $redis;
undef $pub;
undef $sub;
undef $tx;
});
};;
get'/' =>'index';
app->start;
Mojolicious Tutorial