Sunday, June 22, 2014

Aplikasi chat berbasis web menggunakan Socket.io dan NodeJS - Part 2

Pada kesempatan kali ini saya akan melanjutkan pengembangan aplikasi yang tertunda pada Part 1.

Server Logic


Seperti yang sudah saya jelaskan pada postingan sebelumnya, fungsionalitas server terdapat pada file "server.js".

Pada file ini kita akan menambahkan kode untuk menghandle request Socket.io dari client (seperti yang telah di jelaskan sebelumnya Socket.io juga berjalan di client - pada kasus ini web browser)

Integrasikan Socket.io dengan meng-declare nya ke dalam kode sumber:

var express = require('express'), 
    socketIO = require('socket.io'), 
    app = express(), 
    port = 3000 
    ;

Cari baris berikut:

app.listen(port, function(){ 
    console.log('listening on *:' + port); 
});

dan rubah menjadi:

var server = app.listen(port, function(){ 
    console.log('listening on *:' + port); 
}); 

/** 
 * Setup socket.io server untuk meng-handle request 
 * dari library socket.io yang berjalan pada browser 
 */ 
var io = socketIO.listen(server);

Saat ini server Socket.io sudah siap menerima koneksi dari client. Socket.io adalah system yang
berbasis event (event-based) jadi untuk menerima koneksi dari client kita hanya harus membuat
event handler untuk event yang bernama "connection".

io.on('connection', function (socket) {

 // Disini kita akan meng-handle client yang melakukan koneksi 

});

Selanjutnya tambahkan kode yang akan menerima pesan dari client dan mem-broadcast nya
ke semua client:

var users = []; 
var sockets = []; 

io.on('connection', function (socket) {

    socket.on('message', function(message){ 
    
        if (message.type === 'text') {
            console.log('Text Message: ' + message.text); 
            io.emit('message', message); 
        } 

    }); 

});

Jika anda perhatikan saya menggunakan method emit pada object io bukan pada object socket, ini bertujuan untuk mem-broadcast pesan ke semua client tidak hanya pada client yang mengirim pesan saja (sender).

Aplikasi juga harus menyimpan daftar user yang sedang online, dan mem-broadcast nya ke semua client. Untuk itu kita akan merubah sedikit kode di atas menjadi:

io.on('connection', function (socket) {

    socket.on('message', function(message){ 
    
        if (message.type === 'user') {
            console.log('User connected: ' + message.user.name);    
            users.push(message.user); 
            sockets.push(socket); 
            for (var i = 0; i < users.length; ++i) {
                io.emit('connected', users[i]);   
            }               
        } 
        else if (message.type === 'text') {
            console.log('Text Message: ' + message.text); 
            io.emit('message', message); 
        } 

    }); 

});

Ok, Selesai...

Not so fast :), ingat bahwa aplikasi ini terdiri dari dua bagian, kita juga harus menambahkan fungsionalitas dari sisi client.

Client Logic


Pertama-tama kita akan men-setup library Socket.io pada client.
Masukkan (include) library Socket.io pada file "index.html":

<script src="/socket.io/socket.io.js" type="text/javascript"></script>

Dan tambahkan kode berikut di bagian paling bawah body tag (sebelum </body>):

<script src="/socket.io/socket.io.js<script type="text/javascript"> 
        var socket = io(); 
</script>

Yup, hanya ini yang di butuhkan untuk meng-setup library Socket.io di client, sangat mudah bukan!
Anda pasti bertanya-tanya kenapa saya me-refer ke "/socket.io/socket.io.js" pada include tag di atas,
sementara file tersebut tidak dapat di temukan pada root folder dari server. Ini karena Socket.io pada
sisi server akan otomatis membuat file tersebut (generated on-the-fly) saat server berjalan.

Untuk tujuan identifikasi client harus mengirim "username" pada saat pertama kali berjalan.
Tambahkan code berikut pada <script> tag:

var current_user = null;        
var socket = io(); 

function User(name) { 
    this.name = name; 
} 

function createUserMessage(user) { 
    return { type: 'user', user: user };
} 

if (!current_user) { 
    var text = prompt('Your username: ');
    if (!text) { 
        window.location = '/'; 
    } 
    current_user = new User(text); 
} 

// kirim username ke server 
socket.emit('message', createUserMessage(current_user)); 

Kode tersebut akan mengecek apakah user sudah memberikan username, bila belum browser akan
meminta memasukkan terlebih dahulu. Ini akan terus terjadi selama user tidak memberikan
username yang valid (kosong/empty). Jika username yang di berikan adalah valid akan di kirim
ke server.

Jika anda lihat pada file "index.html", terdapat tag yang berfungsi untuk men-display
list dari user yang sedang online:

<div id="side"> 
    <p class="header">Online</p> 
    <ul id="user-list"> 
        <!--<li class="current-user">Fani</li>--> 
    </ul> 
</div>

Ketika ada client yang baru server akan mem-broadcast event "connected", kita dapat men-dengar (listen) event ini dan mem-populate list pada html dengan data yang di return oleh server.

function addToList(id, value, clazz, distinct) {  
    var ul = document.getElementById(id);  
    if (distinct) {  
        if (indexOfList(id, value) > -1) {  
            return;  
        }  
    }  
    var li = document.createElement('li');
    if (clazz) li.className = clazz;  
    li.innerHTML = value;  
    ul.appendChild(li);  
} 

function indexOfList(id, value) { 
    var ul = document.getElementById(id); 
    for (var i = 0; i < ul.children.length; ++i) {
        if (ul.children[i].innerHTML === value) { 
            return i; 
        } 
    } 
    return -1; 
} 

// update list user yang sedang online 
socket.on('connected', function(user) { 
    addToList('user-list', 
              user.name, 
              user.name == current_user.name ? 'current-user' : null, 
              true); 
});

Dan tentunya kita juga harus meng-handle pesan text yang di masukkan oleh user:

function createTextMessage(text, user) { 
    return { type: 'text', text: text, user: user };
} 

// kirim message ke server saat user menekan tombol Enter 
document.forms[0].messageText.onkeypress = function onKeyUp(evt) {
     if (evt.keyCode == 13) {
          socket.emit('message', createTextMessage(this.value, current_user));
          this.value = null;
          evt.preventDefault(); 
      }           
}

Dan men-display pesan text dari semua user:

// handle message yang diterima dari server 
socket.on('message', function(message){ 
          
    if (message.type == 'text') {
        addToList('messages', message.user.name + ': ' + message.text,
            message.user.name === current_user.name ? 'my-message' : null); 
    }   
          
});

Aplikasi sudah fungsional, untuk mencoba jalankan server:

$ node server.js

dan buka alamat berikut di dua browser window/tab: http://localhost:3000

Pada masing-masing window, login dengan dua username yang berbeda, dan mulai chatting.
Anda dapat melihat bahwa saat terdapat perubahan pada halaman di satu browser window, halaman pada browser window yang lain juga mengalami perubahan.

Untuk meng-handle user yang sudah tidak online (meng-hapus nya dari list), saya serahkan kepada anda sebagai bahan pembelajaran. Saya sudah menyiapkan source code nya di sini sebagai bahan referensi.

Sekian, semoga bermanfaat!

Demo

3 comments:

  1. mantap gan, oh iya apa ini gak akan membebani si server sendiri gan jika yang menggunakan aplikasi chat ini banyak client thanks gan sebelumnya =)

    ReplyDelete