 
 Last Updated : 16 September 2017 : ปรับปรุงเนื้อหา และทำตัวอย่าง source code ใหม่ทั้งหมด
เนื่องจากบทความนี้เขียนไว้ค่อนข้างนานแล้ว ช่วงต้นปี 2015 และทุกวันนี้เทคโนโลยีเว็บก็ไปเร็วมากๆ ปัจจุบันมีทั้ง Firebase Database Realtime, Pusher, RethinkDB หรืออื่นๆมากมาย ที่ช่วยให้เรา implement เรื่อง Real time ทั้งใช้ socket.io, web socket อะไรก็แล้วแต่ และบทความนี้ก็ถือว่ามีคนเข้ามาอ่านกันพอสมควร ก็เลยถือโอกาสนี้ revision มันเล็กน้อย แต่ก็ยังคงเนื้อหาเดิมไว้นะครับ
บทความทำ App Chat ด้วย Node.js และ socket.io โดยบทความนี้ผมอ้างอิงจากต้นฉบับจากเว็บไซต์ของ socket.io ลิงค์นี้ครับ Get Started: Chat application มีการดัดแปลงนิดๆหน่อย โดยใช้ Plug template(Jade) และปรับแต่ง stylesheet โดยใช้ Bulma
ตัวอย่างแอพ Chat หลังจากเสร็จแล้ว

Prerequisite
- NodeJS แนะนำเวอร์ชั่น v6 ขึ้นไป
- socket.io v2
- socket.io client v2
- Express
- Pug
- Bulma (สำหรับ stylesheet)
และสำหรับความรู้ที่ต้องใช้คือ Node.js และ Express.js สำหรับใครที่ยังไม่รู้ แนะนำให้อ่านบทความด้านล่างครับ
- มาทำ RESTFul API ด้วย Node.js กับ Express กันดีกว่า
- สอนวิธีทำเว็บไซต์ด้วย Express
- Jade คืออะไร ? + สอนวิธีใช้ร่วมกับ Express
- สอนวิธีใช้ Jade Template Engine
Overview
ตัวโปรแกรมแชต จะถูกแบ่งออกเป็น 2 ส่วน คือ
- ส่วน Server : ซึ่งจะถูกรันด้วย Node.js
- ส่วน Client : จะรันผ่าน Browser ปกติ
ทีนี้ฟังค์ชันสำคัญๆของ socket.io จะมีด้วยกัน 2 ตัวคือ
- socket.on(event, callback): ใช้สำหรับรับข้อความ
- socket.emit(event, message): ใช้สำหรับส่งข้อความ
Step 1 : Create a project
เริ่มต้นสร้างโปรเจ็คด้วยคำสั่ง
npm initจากนั้นทำการตั้งค่าโปรเจ็ค จะได้ไฟล์ package.json ประมาณนี้ (อันนี้ผมสร้างเอง เอาแค่ชื่อและเวอร์ชันเท่านั้น)
{  "name": "simple-chat",  "version": "2.0.0"}ติดตั้ง express, pug และ socket.io
npm install --save express pug socket.io
# หรือหากใช้ yarn ก็สั่งyarn add express pug socket.ioจะได้ไฟล์ package.json เป็นดังนี้
{  "name": "simple-chat",  "version": "2.0.0",  "dependencies": {    "express": "^4.15.4",    "pug": "^2.0.0-rc.4",    "socket.io": "^2.0.3"  }}Step 2 : Create app.js
ทำการสร้างไฟล์ app.js ขึ้นมา ไฟล์นี้จะเป็นไฟล์หลักในส่วนของ Server
const express = require('express')const app = express()const path = require('path')
const APP_PORT = 5555
// // ตั้งค่า เพื่อให้ express ทำการ render view ที่โฟลเดอร์ views// // และใช้ template engine เป็น pugapp.set('views', path.join(__dirname, 'views'))app.set('view engine', 'pug')
app.get('/', (req, res) => {  res.render('index')})
app.listen(APP_PORT, () => {  console.log(`App running on port ${APP_PORT}`)})เนื่องจากว่าผมใช้ JavaScript แบบ Standard เลยไม่ได้ใส่ semicolon นะครับ
จากนั้นสร้างไฟล์ index.pug ไว้ที่โฟลเดอร์ views ดังนี้
h1 Hello Worldตอนนี้ไฟล์ทั้งโปรเจ็คจะมีดังนี้
├── app.js├── node_modules│   ├── express│   ├── pug│   └── socket.io├── package.json└── views    └── index.pugทดสอบรันโปรแกรม แล้วเปิดบราวเซอร์ http://localhost:5555/ เพื่อดูว่าโปรแกรมทำงานโอเคหรือไม่ (ต้องแสดงคำว่า Hello World)
Step 3 : Update file index.pug
ต่อมาทำการแก้ไขไฟล์ index.pug โดยเพิ่มโค๊ดด้านล่างนี้ลงไป
doctype htmlhtml  head    title Chat Application Example by DEVAHOY    link(rel='stylesheet', href='https://cdnjs.cloudflare.com/ajax/libs/bulma/0.5.2/css/bulma.css')    link(rel='stylesheet', href='css/main.css')    script(src='https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.3/socket.io.js')    script(src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js')  body    #chat-app      #chat.has-text-centered        section.hero.is-success          .hero-body            .container              h1.title Chat              h2 Chat Application with Node.js + socket.io        section.section.chat-container          .container            .columns              .box.column.is-8.is-offset-2                h2.title Chatbox                .chat-messages.has-text-left                  ul#chat-messages                form                  .field.has-addons                    p.control                      input(type='text', class='input', value='Chuck Norris', id='name')                    p.control.is-expanded                      input(type='text', class='input', placeholder='Try to say something', id='message')                    p.control                      input(type='submit', class='button is-success', value='Post')        footer          .container            .content              p                | Powered by                a(href='https://devahoy.com') DEVAHOYจากนั้นสร้างไฟล์ main.css และเซฟไว้ที่โฟลเดอร์ใหม่ชื่อ public/css/ ดังนี้ (CSS ใครไม่ใส่ก็ได้นะครับ ผมเพิ่ม custom นิดเดียวเอง เพราะว่าใช้ Bulma ไป
body {  font-family: Avenir, Helvetica, Arial, sans-serif;}
form {  margin-top: 12px;}
#chat-messages li {  padding: 12px;}เพิ่มโค๊ดนี้ลงไปที่ไฟล์ app.js ก่อน app.get('/') เพื่อให้ express ทำการลิงค์ไฟล์ public/css/main.css ได้ถูกต้อง
app.use(express.static('public'))โครงสร้างโปรเจ็ค เมื่อถึงขั้นตอนนี้จะเป็นดังนี้
├── app.js├── node_modules│   ├── express│   ├── pug│   └── socket.io├── package.json├── public│   └── css│       └── main.css└── views    └── index.jadeลองสั่งรันโปรแกรมใหม่
node app.jsStep 4 : Integrate Socket.io
ต่อมาเราจะทำการเชื่อมต่อกับ socket.io ทำการเพิ่ม module socket.io ที่ไฟล์ app.js (ต้องแยกระหว่าง socket.io ที่อยู่ฝั่ง server (node.js) เราจะใช้ตัวที่ติดตั้งผ่าน npm ส่วน socket ผั่ง client(browser) จะเห็นว่าผมใช้เรียกจาก cdn เอาเลย
const server = app.listen(APP_PORT, () => {  console.log(`App running on port ${APP_PORT}`)})
const 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 จะเป็นดังนี้
const express = require('express')const app = express()const path = require('path')
const APP_PORT = 5555
const server = app.listen(APP_PORT, () => {  console.log(`App running on port ${APP_PORT}`)})
const io = require('socket.io').listen(server)
// ตั้งค่า เพื่อให้ express ทำการ render view ที่โฟลเดอร์ views// และใช้ template engine เป็น pugapp.set('views', path.join(__dirname, 'views'))app.set('view engine', 'pug')
app.use(express.static('public'))
app.get('/', (req, res) => {  res.render('index')})
io.on('connection', (socket) => {  console.log('a user connected')})จากนั้นที่ไฟล์ index.pug ทำการเพิ่ม script ของ socket.io.js ฝั่ง client และทำโหลด socket.io ดังนี้
script(src='https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.3/socket.io.js')script.  const socket = io();ตอนนี้ไฟล์ index.pug จะเป็นแบบนี้
doctype htmlhtml  head    title Chat Application Example by DEVAHOY    link(rel='stylesheet', href='https://cdnjs.cloudflare.com/ajax/libs/bulma/0.5.2/css/bulma.css')    link(rel='stylesheet', href='css/main.css')    script(src='https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.3/socket.io.js')  body    #chat-app      #chat.has-text-centered        section.hero.is-success          .hero-body            .container              h1.title Chat              h2 Chat Application with Node.js + socket.io        section.section.chat-container          .container            .columns              .box.column.is-8.is-offset-2                h2.title Chatbox                .chat-messages.has-text-left                  ul#chat-messages                form                  .field.has-addons                    p.control                      input(type='text', class='input', value='Chuck Norris', id='name')                    p.control.is-expanded                      input(type='text', class='input', placeholder='Try to say something', id='message')                    p.control                      input(type='submit', class='button is-success', value='Post')        footer          .container            .content              p                | Powered by                a(href='https://devahoy.com') DEVAHOY      script.        const socket = io();io(); ที่ฝั่ง client โดย default แล้วคือการ connect ไปที่ server ฉะนั้นเราก็ไม่จำเป็นต้องระบุ event
ทดสอบรันโปรแกรม ทีนี้ลองเปิดบราวเซอร์ แล้วดูที่ console จะเห็นว่า มีขึ้นว่า 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)  })})จะเห็นว่าเราต้องทำการระบุชื่อของ event เป็นอะไรก็ได้ แต่ต้องให้เหมือนกับ กับฝั่ง client และ server เพื่อให้รับส่งข้อมูลกันได้ถูกต้อง ลองนึกถึงเวลาเราแชตกับเพื่อน คนนึงใช้ LINE คนนึงใช้ Facebook, event ไม่เหมือนกัน ข้อความก็ไปไม่ถึง :)
ทีนี้ที่ไฟล์ index.pug เราระบุข้อความไปโต้งๆเลย ซึ่งจริงๆแล้วเรามี form อยู่นี่นา ก็ให้ user ทำการพิมพ์ข้อความ แล้วกด Send ถึงส่งข้อความไปที่ server ก็ทำการเพิ่มโค๊ดนี้ลงไป (ใช้ jquery)
script.  const socket = io();  $('form').submit(function() {    const name = $('#name').val();    const message = $('#message').val();
    socket.emit('chatter', `${name} : ${message}`);    $('#message').val('');    return false;  });อธิบายคือ
- เมื่อเราทำการ submit form ก็จะทำการอ่านค่า จาก input เก็บไว้ที่ตัวแปร nameและmessage
- socket.emit('chatter', value): คือการส่งค่าผ่าน socket ผ่านท่อชื่อ- chatterโดยส่ง name และ message ไป
- จากนั้นเคลียร์ค่า form input ซะ ถ้าส่งเสร็จแล้ว
Step 6 : Broadcasting
ทีนี้เมื่อ client สามารถที่จะส่ง message ผ่าน event ที่ระบุไว้ไปที่ server ได้แล้ว จะทำยังไงให้ server ส่ง event กลับมาที่ client ได้ (เราไม่ได้พูดถึง client เฉพาะที่ส่ง message ไปที่ server นะครับ แต่พูดถึง client ทุกๆ client เลย)
นึกถึงกรณีที่คนนึงพิมพ์ข้อความ สมมติชื่อ client1
- ข้อความจะถูกส่งจาก client1ไปที่ server
- จากนั้น server ก็จะส่งข้อความกลับไป ทุกๆ client โดยระบุชื่อ event ผ่านเมธอด io.emit('event', 'message')
- client อื่นๆ รวมถึง client1ก็จะรับ message เดียวกัน ผ่าน event ด้วยการ implement methodio.on('event', callback)
มาที่ไฟล์ app.js ทำการเพิ่ม io.emit() ลงไปในฟังค์ชัน socket.io แบบนี้
io.on('connection', (socket) => {  socket.on('chatter', (message) => {    console.log('message : ', message)    io.emit('chatter', message)  })})อธิบายคือ เมื่อทางฝั่ง server ได้รับข้อความจาก event chatter แล้ว ก็จะทำการส่งไปกลับไปให้กับทุกๆ client ผ่าน io.emit()
จากนั้นที่ไฟล์ index.pug ทางฝั่ง client ก็ทำการเพิ่มโค๊ดเพื่อทำการรอรับข้อความจาก server ดังนี้
script.  socket.on('chatter', function(message) {    $('#chat-messages').append($('<li>').text(message));  });- socket.on('chatter', fn): เมื่อได้รับข้อความจาก server ก็ทำการ- appendค่าไปใส่ ช่อง- ul#chat-messagesนั่นเอง
ทดสอบรันโปรแกรม ก็จะได้ดังภาพ เป็นอันเรียบร้อย :)

ดาวน์โหลดหรือดูตัวอย่าง source code ประกอบได้บน Github ครับ
Reference
- Authors
-    Chai Phonbopitเป็น Web Dev ในบริษัทแห่งหนึ่ง ทำงานมา 10 ปีกว่าๆ ด้วยภาษาและเทคโนโลยี เช่น JavaScript, Node.js, React, Vue และปัจจุบันกำลังสนใจในเรื่องของ Blockchain และ Crypto กำลังหัดเรียนภาษา Rust 
![[Workshop] ทำ Chat Application ด้วย Express + Socket.io และ React.js](/_vercel/image?url=%2Fimages%2F2023%2F10%2Fapp-chat-react.png&w=828&q=80) 
 ![[Workshop] ทำ Chat Application ด้วย Node.js, Express และ Socket.io](/_vercel/image?url=%2Fimages%2F2023%2F10%2Fchat-app-nodejs-socketio.png&w=828&q=80) 
 