Skip to content

Writing websocket chat using Mojolicious Lite

William Lindley edited this page Sep 17, 2019 · 11 revisions

Writing websocket chat using Mojolicious::Lite

Note: This page is a translation of http://d.hatena.ne.jp/naoya/20101011/1286778922.

I read the article (real time web with websocket http://www.atmarkit.co.jp/fcoding/articles/websocket/01/websocket01a.html) The article says, you can easily write event driven web server when you use Javascript framework, which is using V8 and named node.js. Additionally, using node-websocket-server.js, you can easily implement websocket server with node.js. The article compares architecture of websocket, polling with Ajax and long polling.

(Please look at the reference material at the end of this article for links)

Websocket enables us to write server-pushing application easily. But the problem is how to prepare the HTTP server which processing websocket. It is interesting that node.js is one of the answer.

Websocket server with Perl ... Mojolicious::Lite

The article provoked me into writing server-side program in Perl, not Javascript. I wrote a sample using Mojolicious::Lite. Mojolicious includes an implementation of a server that can handle the WebSocket protocol.

So you can write it like below.

use Mojolicious::Lite;

websocket '/echo' => sub {
    my $self = shift;
    $self->on(message => sub {
        my ($self, $message) = @_;
        $self->send("echo: $message");
    });
};

app->start;

Then, starting the server,

% perl app.pl daemon

ws://localhost:3000/echo is the end point of websocket. The callback passed to receive_message() will be kicked on every event from client-side. Other processes like handshaking are treated appropriately by Mojo. It's offhandedness.

The code of websocket chat sample

The following is the chat I made as a sample. / returns HTML chat page. /echo is the websocket endpoint. When it receives a message, it sends the time and the received message to all connected clients.

#!/usr/bin/env perl
use utf8;
use Mojolicious::Lite;
use DateTime;

get '/' => 'index';

my $clients = {};

websocket '/echo' => sub {
    my $self = shift;

    app->log->debug(sprintf 'Client connected: %s', $self->tx);
    my $id = sprintf "%s", $self->tx;
    $clients->{$id} = $self->tx;

    $self->on(message => sub {
        my ($self, $msg) = @_;

        my $dt   = DateTime->now( time_zone => 'Asia/Tokyo');

        for (keys %$clients) {
            $clients->{$_}->send({json => {
                hms  => $dt->hms,
                text => $msg,
            }});
        }
    });

    $self->on(finish => sub {
        app->log->debug('Client disconnected');
        delete $clients->{$id};
    });
};

app->start;

__DATA__
@@ index.html.ep
<html>
  <head>
    <title>WebSocket Client</title>
    <script
      type="text/javascript"
      src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"
    ></script>
    <script type="text/javascript" src="/js/ws.js"></script>
    <style type="text/css">
      textarea {
          width: 40em;
          height:10em;
      }
    </style>
  </head>
<body>

<h1>Mojolicious + WebSocket</h1>

<p><input type="text" id="msg" /></p>
<textarea id="log" readonly></textarea>

</body>
</html>

The client side js code is below. Connecting to /echo with websocket API, processes input and output appropriately.

$(function () {
  $('#msg').focus();

  var log = function (text) {
    $('#log').val( $('#log').val() + text + "\n");
  };
  
  var ws = new WebSocket('ws://localhost:3000/echo');
  ws.onopen = function () {
    log('Connection opened');
  };
  
  ws.onmessage = function (msg) {
    var res = JSON.parse(msg.data);
    log('[' + res.hms + '] ' + res.text); 
  };

$('#msg').keydown(function (e) {
    if (e.keyCode == 13 && $('#msg').val()) {
        ws.send($('#msg').val());
        $('#msg').val('');
    }
  });
});

Access to localhost:3000 with Google Chrome. It works fine.

Screenshot

Screenshot: http://f.hatena.ne.jp/naoya/20101011153308

In this way you can implement a WebSocket server with Perl. As I end on that, there is a wealth of existing information to make use of which give rise to a number of different ideas. Such as implementing on a server in Perl something like the Activity Monitor that article mentioned. I'm never out of ideas, such as displaying a remote server's status graphically.

More Mojolicious WebSocket example / No time to wait

http://vti.showmetheco.de/articles/2010/05/more-mojolicious-websocket-examples.html

You can find more gorgeous samples using Mojolicious + websocket.

Twiggy sample

http://cpansearch.perl.org/src/MIYAGAWA/Twiggy-0.1007/eg/chat-websocket/chat.psgi

When I search another server-side example, I found Twiggy (AnyEvent + PSGI based) sample. It's happy to process websocket with Plack/PSGI.

"The Realtime Web"

http://www.atmarkit.co.jp/fcoding/articles/websocket/01/websocket01a.html

An eye on WebSocket "The Realtime Web" - original article (jp)

node.js

http://nodejs.org/

Server-side JavaScript framework

node-websocket-server.js

http://github.com/miksago/node-websocket-server

Server for the websocket protocol

Clone this wiki locally