Skip to content
This repository

Writing websocket chat using Mojolicious Lite 

kraih edited this page · 9 revisions

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.

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 following is the chat I made as a sample. / returns HTML chat page. /echo is the websocket endpoint. When it recieves a message, it sends the time and the recieved 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.

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

You can find more gorgeous samples using Mojolicious + websocket.

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.

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

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

http://nodejs.org/

Server-side JavaScript framework

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

Server for the websocket protocol

Something went wrong with that request. Please try again.