สร้างแอพแชตด้วย Node.js และ socket.io

สร้างแอพแชตด้วย Node.js และ socket.io Cover Image

บทความทำ App Chat ด้วย Node.js และ socket.io โดยบทความนี้ผมอ้างอิงจากต้นฉบับจากเว็บไซต์ของ socket.io ลิงค์นี้ครับ Get Started: Chat application มีการดัดแปลงนิดๆหน่อย โดยใช้ jade template และเปลี่ยนหน้าตาของ css นิดๆครับ ปรับแต่งสไตล์มั่วๆ หากใครอยากอ่านแบบรู้เรื่อง แนะนำต้นฉบับดีกว่า :)

ตัวอย่างแอพ Chat หลังจากเสร็จแล้ว

Demo App

เตรียมความพร้อม

ระบบปฎิบัติการและเวอร์ชั่นต่างๆที่ผมใช้ในบทความนี้คือ

  • Ubuntu 14.04
  • Node.js v0.10.33
  • npm v1.4.28
  • socket.io v1.3.3
  • socket.io client v1.3.3
  • express.js v4.11.2
  • jade v1.9.2

และสำหรับความรู้ที่ต้องใช้คือ Node.js และ Express.js สำหรับใครที่ยังไม่รู้ แนะนำให้อ่านบทความด้านล่างครับ

ภาพรวมของโปรแกรม

ตัวโปรแกรมแชต จะถูกแบ่งออกเป็น 2 ส่วน คือ

  • ส่วน Server : ซึ่งจะถูกรันด้วย Node.js
  • ส่วน Client : จะรันผ่าน Browser ปกติ

ที่นี้ฟังค์ชันสำคัญๆของ socket.io จะมีด้วยกัน 2 ตัวคือ

  1. socket.on(event, callback) : ใช้สำหรับรับข้อความ
  2. socket.emit(event, message) : ใช้สำหรับส่งข้อความ

Step 1 : สร้างโปรเจ็ค

เริ่มต้นสร้างโปรเจ็คด้วยคำสั่ง

npm init

จากนั้นทำการตั้งค่าโปรเจ็ค จะได้ไฟล์ package.json ประมาณนี้ (อันนี้ผมสร้างเอง เอาแค่ชื่อและเวอร์ชันเท่านั้น)

{
    "name": "simple-chat",
    "version": "0.0.1"
}

ติดตั้ง express, jade และ socket.io

npm install --save-dev express jade socket.io

จะได้ไฟล์ package.json เป็นดังนี้

{
  "name": "simple-chat",
  "version": "0.0.1",
  "devDependencies": {
    "express": "^4.11.2",
    "jade": "^1.9.2",
    "socket.io": "^1.3.3"
  }
}

Step 2 : สร้างไฟล์ app.js

ทำการสร้างไฟล์ app.js ขึ้นมา ไฟล์นี้จะเป็นไฟล์หลักในส่วนของ Server

var express = require('express');
var app = express();
var path = require('path');

// ตั้งค่า เพื่อให้ express ทำการ render view ที่โฟลเดอร์ views
// และใช้ template engine เป็น jade
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

app.get('/', function(req, res) {
    res.render('index');
});

app.listen(5555, function() {
    console.log('Program running on port : 5555');
});

จากนั้นสร้างไฟล์ index.jade ไว้ที่โฟลเดอร์ views ดังนี้

h1 Hello World

ตอนนี้ไฟล์ทั้งโปรเจ็คจะมีดังนี้

├── app.js
├── node_modules
│   ├── express
│   ├── jade
│   └── socket.io
├── package.json
└── views
    └── index.jade

ทดสอบรันโปรแกรม แล้วเปิดบราวเซอร์ http://localhost:5555/ เพื่อดูว่าโปรแกรมทำงานโอเคหรือไม่ (ต้องแสดงคำว่า Hello World)

Step 3 : แก้ไขไฟล์ index.jade

ต่อมาทำการแก้ไขไฟล์ index.jade โดยเพิ่มโค๊ดด้านล่างนี้ลงไป

doctype html
html
    head
        title Chat Application Example by DevAhoy.com
        link(rel='stylesheet', href='css/main.css')
    body
        div.container
            h1 Chat Application by DevAhoy.com
            div#chat-box
                ul
                form(action='')
                    input(type="text", id="message", autocomplete="off")
                    button Send

จากนั้นสร้างไฟล์ main.css และเซฟไว้ที่โฟลเดอร์ใหม่ชื่อ public/css/ ดังนี้

body {
    background: #2196f3;
}

.container {
    width: 100%;
    max-width: 960px;
    margin: 0 auto;
}

.container h1 {
    margin-top: 100px;
    text-align: center;
    color: #fff;
}

#chat-box {
  background-color: #bbdefb;
  padding: 15px;
}

ul {
    list-style: none;
    padding: 0;
}

ul li {
    background: #f5f5f5;
    padding: 10px;
}

ul li:nth-child(odd) {
    background: #fff;
}

form input {
    background: #f5f5f5;
    width: 85%;
    padding: 10px;
    border: 1px solid #d5d5d5;
}

form button {
    background: #ff4081;
    color: #fff;
    font-weight: bold;
    border: none;
    width: 10%;
    padding: 10px;
    margin-left: 20px;
}

ผมไม่พูดถึง stylesheet นะครับ สามารถไปปรับแต่งเพิ่มเอง ด้านบนผมก็ลองปรับสไตล์มั่วๆเอา :)

เพิ่มโค๊ดนี้ลงไปที่ไฟล์ app.js ก่อน app.get('/') เพื่อให้ express ทำการลิงค์ไฟล์ public/css/main.css ได้ถูกต้อง

app.use(express.static('public'));

โครงสร้างโปรเจ็ค เมื่อถึงขั้นตอนนี้จะเป็นดังนี้

├── app.js
├── node_modules
│   ├── express
│   ├── jade
│   └── socket.io
├── package.json
├── public
│   └── css
│       └── main.css
└── views
    └── index.jade

ลองสั่งรันโปรแกรมใหม่

node app.js

Step 4 : Integrate Socket.io

ต่อมาทำการเพิ่ม module socket.io ที่ไฟล์ app.js

var server = app.listen(5555, function() {
    console.log('Program running on port : 5555');
});
var io = require('socket.io').listen(server);

โดยให้ออปเจ็ค app.listen() เก็บไว้ที่ตัวแปร server เพื่อใช้สำหรับส่งเป็น argument ให้กับ socket.io

จากนั้นทำการเพิ่ม

io.on('connection', function(socket) {
    console.log('a user connected');
});

เพื่อให้ socket ทำการรับ event ที่ชื่อ connection

ตอนนี้ตัวไฟล์ app.js จะเป็นดังนี้

var express = require('express');
var app = express();
var path = require('path');

var server = app.listen(5555, function() {
    console.log('Program running on port : 5555');
});
var io = require('socket.io').listen(server);

// ตั้งค่า เพื่อให้ express ทำการ render view ที่โฟลเดอร์ views
// และใช้ template engine เป็น jade
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

app.use(express.static('public'));

app.get('/', function(req, res) {
    res.render('index');
});

io.on('connection', function(socket) {
    console.log('a user connected');
});

จากนั้นที่ไฟล์ index.jade ทำการเพิ่ม script ของ socket.io.js ฝั่ง client และทำโหลด socket.io ดังนี้

script(src="https://cdn.socket.io/socket.io-1.3.3.js")
script.
    var socket = io();

ตอนนี้ไฟล์ index.jade จะเป็นแบบนี้

doctype html
html
    head
        title Chat Application Example by DevAhoy.com
        link(rel='stylesheet', href='css/main.css')
        script(src="https://cdn.socket.io/socket.io-1.3.3.js")
    body
        div.container
            h1 Chat Application by DevAhoy.com
            div#chat-box
                ul
                form(action='')
                    input(type="text", id="message", autocomplete="off")
                    button Send
        script.
            var socket = io();

io(); ที่ฝั่ง client โดย default แล้วคือการ connect ไปที่ server ฉะนั้นเราก็ไม่จำเป็นต้องระบุ event

ทดสอบรันโปรแกรม ทีนี้ลองเปิดบราวเซอร์ แล้วดูที่ console จะเห็นว่า มีขึ้นว่า a user connected ตามจำนวนครั้งที่เราเปิดบราวเซอร์

a user connected

Step 5 : ส่ง Event ไปที่ Server

จุดเด่นของ socket.io คือเราสามารถที่่จะส่งและรับข้อความได้ทุกๆ event ที่เราต้องการ สามารถส่งอะไรไปก็ได้ ไม่ว่าจะเป็น String, JSON หรือแม้แต่ binary data

ทีนี้ลองมาแก้ไขไฟล์ index.jade โดยการเพิ่ม socket.emit(event, message) ดูกันครับ พิมพ์ที่ส่วน script ล่างสุดของไฟล์

var socket = io();
socket.emit('chatter', 'Hello from client');

และไฟล์ app.js แก้ไขโดยให้ socket ทำการรับ event ที่ชื่อ chatter ชื่อเดียวกันกับฝั่ง client

io.on('connection', function(socket) {
    socket.on('chatter', function(message) {
        console.log('message : ' + message);
    });
});

message

จะเห็นว่าเราต้องทำการระบุชื่อของ event เป็นอะไรก็ได้ แต่ต้องให้เหมือนกับ กับฝั่ง client และ server เพื่อให้รับส่งข้อมูลกันได้ถูกต้อง ลองนึกถึงเวลาเราแชตกับเพื่อน คนนึงใช้ LINE คนนึงใช้ Facebook, event ไม่เหมือนกัน ข้อความก็ไปไม่ถึง :)

ทีนี้ที่ไฟล์ index.jade เราระบุข้อความไปโต้งๆเลย ซึ่งจริงๆแล้วเรามี form อยู่นี่นา ก็ให้ user ทำการพิมพ์ข้อความ แล้วกด Send ถึงส่งข้อความไปที่ server ก็ทำการเพิ่มโค๊ดนี้ลงไป (ใช้ jquery)

script(src="http://code.jquery.com/jquery-1.11.2.min.js")
var socket = io();
$('form').submit(function() {
    socket.emit('chatter', $('#message').val());
    $('#message').val('');
    return false;   
});

Step 6 : Broadcasting

ทีนี้เมื่อ client สามารถที่จะส่ง message ผ่าน event ที่ระบุไว้ไปที่ server ได้แล้ว จะทำยังไงให้ server ส่ง event กลับมาที่ client ได้ (เราไม่ได้พูดถึง client เฉพาะที่ส่ง message ไปที่ server นะครับ แต่พูดถึง client ทุกๆ client เลย นึกถึงกรณีที่คนนึงพิมพ์ข้อความ สมมติชื่อ client1

  1. ข้อความจะถูกส่งจาก client 1 ไปที่ server
  2. จากนั้น server ก็จะส่งข้อความกลับไป ทุกๆ client โดยระบุชื่อ event ผ่านเมธอด io.emit('event', 'message')
  3. client อื่นๆ รวมถึง client1 ก็จะรับ message ผ่าน event ด้วยเมธอด io.on('event', callback)

มาที่ไฟล์ app.js ทำการเพิ่ม io.emit() ลงไปในฟังค์ชัน socket.io แบบนี้

io.on('connection', function(socket) {
    socket.on('chatter', function(message) {
        console.log('message : ' + message);

        io.emit('chatter', message);
    });
});

อธิบายคือ เมื่อทางฝั่ง server ได้รับข้อความจาก event chatter แล้ว ก็จะทำการส่งไปกลับไปให้กับทุกๆ client ผ่าน io.emit()

จากนั้นที่ไฟล์ index.jade ทางฝั่ง client ก็ทำการเพิ่มโค๊ดเพื่อทำการรอรับข้อความจาก server ดังนี้

script.
    socket.on('chatter', function(message) {
        $('#chat-box ul').append($('<li>').text(message));
    });

ทดสอบรันโปรแกรม ก็จะได้ดังภาพ เป็นอันเรียบร้อย :)

Demo App

ดาวน์โหลดหรือดูตัวอย่าง source code ประกอบได้บน Github ครับ

Source Code

แหล่งศึกษาเพิ่มเติม

เนื่องจากก่อนเขียนบทความนี้ผมก็เคยลองค้นหาดูแล้ว แล้วพบว่ามีคนเขียนบทความไว้เยอะพอสมควร ทีแรกก็กะไม่ทำ สุดท้ายพอดีว่าจะทำบทความ socket.io บน Android พอดี ก็เลยทำบทความนี้ขึ้นมาด้วยซะเลย อย่างน้อยก็เป็นอีกหนึ่งทางเลือกให้อ่านเพิ่มเติมนะครับ สุดท้ายลิงค์เหล่านี้น่าจะมีประโยชน์ต่อผู้อ่านนะครับ หลายๆลิงค์อธิบายและยกตัวอย่างไว้ดีมาก

Chai Chai Phonbopit : MEAN Stack @Nextzy • ผู้ชายธรรมดาๆ ที่ชื่นชอบ Android, JavaScript (Node.js) และ Open Source มีงานอดิเรกเป็น Acoustic Guitar และ Football

บทความล่าสุด