สร้างแอพ Chat ด้วย Node.js และ Socket.IO

socket.io

วันนี้เรามาลองทำแอพ chat แบบเรียลไทม์ด้วย Node.js กันครับ สมัยก่อนอาจจะดูยุ่งยาก แต่สมัยนี้เราสามารถทำแอพแนวนี้ได้ง่ายเอามากๆ เลย เพื่อไม่ให้เสียเวลา เรามาเริ่มกันเลยฮะ

แต่ก่อนเราทำอย่างไร ?

คาดว่าหลายๆ คน คงจะเคยทำเว็บที่มีการ refresh ข้อมูลอยู่เรื่อยๆ ใช่มั้ยครับ วิธีทำก็อาจจะใช้การตั้งเวลาดึงข้อมูลมาจาก server ทุกๆ กี่นาทีก็ว่าไป แต่ลองนึกดูครับว่าถ้าจะทำแอพ chat กันแบบเรียลไทม์ การส่ง request ไปหา server เพื่อจะถามว่ามีใครส่งข้อความอะไรมาให้เราแล้วหรือยังก็คงจะต้องถี่เอามากๆ เลย บางทีส่งไปแล้วปรากฏว่าไม่มีใครส่งอะไรมาหาเราเลยก็เท่ากับว่า request นั้นสูญเปล่าถูกมั้ยครับ แล้วที่สำคัญ ในแต่ละ request นั้น มันก็จะมี overhead ด้วย เผลอๆ overhead อาจมีขนาดใหญ่กว่าข้อความที่คุยกันด้วยซ้ำไป วิธีนี้จึงไม่น่าจะเวิร์คกับแอพของเราครับ

รู้จักกับ Web Socket

วิธีที่น่าสนใจกว่าก็คือการสื่อสารกันผ่านสิ่งที่เรียกว่า Web Socket ครับ เพื่อให้เห็นภาพชัดๆ ผมขอเปรียบเทียบว่ามันก็เหมือนกับการสร้าง “ท่อ” เชื่อมระหว่าง client กับ server เข้าด้วยกันครับ โดยจุดเด่นของ Web Socket ก็คือ มันจะเป็นการสื่อสารแบบ 2 ทางครับ คือใครจะส่งข้อมูลไปหาใครก็ได้ นั่นหมายความว่าเราจะไม่ต้องคอยถาม server แล้วว่ามีใครส่งอะไรมาให้เรามั้ย เพราะเราจะให้ server เป็นคนบอกเราเองครับ นอกจากนั้น เจ้าท่อที่ว่านี้มันจะต่อค้างเอาไว้ด้วยนะครับ คือไม่ได้หายไปเวลาส่งข้อมูลเสร็จ นั่นหมายความว่าการส่งแต่ละครั้งจะไม่มีปัญหาเรื่อง overhead แล้วล่ะครับ

ภาพรวมของแอพ

ทีนี้เรามาดูตัวแอพของเราบ้างครับ ผมมองว่ามันแบ่งออกเป็น 2 ส่วน ดังนี้

  • Server เอาไว้รับข้อความจาก client และกระจายข้อความนั้นไปยัง client ทุกๆ เครื่องที่เชื่อมต่ออยู่
  • Client เอาไว้ส่งข้อความที่จะคุยไปยัง server และรับข้อความของคนอื่นที่ server ส่งมา

เพื่อให้เห็นภาพมากขึ้น สมมติว่าในห้อง chat มีคนอยู่ 3 คน คือ A, B และ C แอพของเราจะต้องทำแบบนี้ได้ครับ

  1. A ส่งข้อความไปยัง server
  2. server ได้รับข้อความจาก A
  3. server ส่งข้อความของ A ไปยัง client ทั้งหมด ซึ่งก็คือ A, B และ C
  4. A, B และ C ได้รับข้อความของ A จาก server

จะสังเกตนะครับว่า A ไม่ได้คุยกับ B และ C ตรงๆ และนี่เป็นเหตุผลครับว่าทำไมเราต้องมี server

รู้จักกับ Socket.IO

Socket.IO เป็นโมดูลของ Node.js ที่เอาไว้เรียกใช้งาน Web Socket ครับ ส่วนวิธีใช้งานนั้นก็ไม่ยากเลย เพราะที่ใช้หลักๆ แล้วจะมีด้วยกัน 2 แบบ เท่านั้นเองครับ

  • socket.on(ชื่อท่อ, callback) เมื่อได้รับข้อมูลมาทางท่อนี้ให้ทำอะไร
  • socket.emit(ชื่อท่อ, ข้อมูลที่จะส่ง) ส่งข้อมูลไปยังทุกๆ client ที่เชื่อมต่อกับท่อนี้อยู่

จะเห็นว่าโค้ดนั้นเข้าใจง่ายมากเลยใช่มั้ยครับ งั้นเรามาเริ่มลงมือเขียนแอพกันเลยดีกว่าครับ

Workshop – เขียนแอพ Instant Messaging

สำหรับ workshop นี้ ผมจะขอถือว่าเราคุ้นเคยกับการใช้ Node.js, Express และ Jade มาเป็นอย่างดีแล้วนะครับ เพราะจะได้เน้นไปที่เนื้อหาเกี่ยวกับการใช้งาน Socket.IO ได้เต็มที่ครับ แต่ถ้าใครอยากจะทบทวนความรู้ ก็สามารถอ่านจากบทความด้านล่างนี้ได้เลยครับ

ก่อนจะเริ่ม workshop หากเพื่อนๆ ยังไม่สะดวกที่จะเขียนโค้ดตามก็ไม่เป็นไรนะครับ แนะนำให้อ่านแล้วพยายามทำความเข้าใจโค้ดก็พอฮะ เพราะที่ท้ายบทความจะมี source code ให้ดาวน์โหลดไปลองเล่นอยู่แล้ว

1. ติดตั้ง Dependency

เริ่มต้นด้วย package.json ตามนี้ได้เลยครับ

จากนั้นก็ติดตั้ง dependency ต่างๆ ด้วยคำสั่ง

2. เตรียมไฟล์ต่างๆ

โครงสร้างของแอพจะเป็นแบบนี้ครับ ให้เราสร้างไฟล์เปล่าๆ มารอไว้ก่อนได้เลย

3. เขียนโค้ดที่ app.js

เริ่มกันที่ไฟล์ app.js ครับ ให้เราใส่โค้ดด้านล่างนี้ลงไปได้เลย

จะเห็นว่าเป็นโค้ดสำหรับสร้าง http server ธรรมดาๆ ที่ port 8081 ครับ นอกจากนั้นก็จะมีการกำหนดว่าให้ใช้ Jade เป็น template engine นะ กำหนดให้ Express อ่านไฟล์ static ที่โฟลเดอร์ public นะ แล้วก็ทำ route สำหรับหน้าแรกให้ไปอ่าน template ที่ไฟล์ index.jade ครับ

4. เขียนโค้ดที่ index.jade

จากนั้นมาดูที่ไฟล์ index.jade ครับ ให้เราใส่โค้ดด้านล่างนี้ลงไป

ไล่โค้ดดูก็จะพบว่ามี form ที่เราจะเอาไว้พิมพ์ข้อความสำหรับ chat นั่นเองครับ

5. เขียนโค้ดที่ main.css

เพื่อความสวยงามของแอพเรา ให้ใส่โค้ด css ด้านล่างนี้ ลงไปที่ไฟล์ public/css/main.css ครับ

6. ลองรัน

ให้เราลองรันดูเลยครับว่าหน้าตาของแอพเราในตอนนี้เป็นอย่างไร

เมื่อลองรันดูก็จะเห็นแอพหน้าตาสวยงาม มีช่องสำหรับพิมพ์ข้อความ แต่พอลองพิมพ์แล้ว Enter ดูก็จะพบว่ายังไม่มีอะไรเกิดขึ้นครับ

7. เรียกใช้ Socket.IO ที่ฝั่ง Server

ที่ไฟล์ app.js ให้เพิ่มโค้ดสำหรับใช้งาน Socket.IO เข้าไปต่อท้ายแบบนี้ครับ

เสร็จแล้วอย่าเพิ่งรันแอพใหม่นะครับ เพราะเราจะต้องไปเซท Socket.IO ที่ฝั่ง client ด้วย

8. เรียกใช้ Socket.IO ที่ฝั่ง Client

ให้ไปดูที่ไฟล์ index.jade แล้วไปเพิ่ม script ของ socket.io แบบนี้ครับ

9. ลองรันใหม่

เรามาลองรันแอพใหม่ดูอีกทีครับ คราวนี้เมื่อลองเข้าหน้าแอพผ่าน browser แล้วกลับไปดูที่ console เราก็จะเห็นข้อความ a user connected แล้วล่ะครับ หากลองรีเฟรช browser หลายๆ ที เราก็จะเห็นข้อความแสดงตามจำนวนครั้งที่เรารีเฟรชครับ

10. ส่งข้อมูลไปยัง Server

ก่อนหน้านี้เราก็แค่ดัก event ว่าพอมี client เข้ามาเขื่อมต่อ ก็ให้แสดงข้อความออกมาใช่มั้ยครับ ทีนี้เราจะมาลองเขียนโค้ดให้ client เป็นคนส่งข้อมูลไปหา server เองดูบ้าง ให้เข้าไปที่ไฟล์ index.jade อีกทีครับ แล้วเพิ่มโค้ด socket.emit(ชื่อท่อ, ข้อมูลที่จะส่ง) เข้าไปแบบนี้ครับ

จากนั้น ที่ไฟล์ app.js เราก็จะเพิ่มโค้ด socket.on(ชื่อท่อ, callback) เพื่อเอาไว้รอรับข้อมูลที่ client จะส่งมาครับ

เมื่อลองรันใหม่ เราก็จะเห็นข้อความ Hello ที่ client เป็นคนส่งมา แสดงผลออกมาทาง console ครับ

ชื่อของ “ท่อ” จะเป็นอะไรก็ได้ เพียงแต่ต้องตั้งชื่อให้เหมือนกันทั้งฝั่ง client และ server

11. เปลี่ยนมาส่งข้อมูลด้วย Form Input

ก่อนหน้านี้เราจะระบุข้อความที่จะส่งลงไปตรงๆ เลยใช่มั้ย ทีนี้เราจะเปลี่ยนมาใช้ form input ในการรับข้อความที่จะส่งจาก user กันดีกว่าครับ ให้เราเข้าไปแก้โค้ดที่ไฟล์ index.jade

จากนั้นก็ลองรันดูอีกทีครับ ให้เราลองพิมพ์ข้อความอะไรก็ได้ แล้วลอง Enter ดู จากนั้นให้ลองไปดูที่ console เราก็จะเห็นข้อความที่เราพิมพ์ แสดงผลออกมาครับ

12. กระจายข้อมูลไปยังทุกๆ Client

ทีนี้มาถึงตา server กันบ้างครับ เราจะเขียนโค้ดให้ server กระจายข้อความเมื่อกี้ไปยัง client ทั้งหมดที่มีการเชื่อมต่อกับท่อที่ชื่อ “chat” อยู่ ให้เราเข้าไปที่ไฟล์ app.js แล้วเปลี่ยนจาก console.log() มาเป็น io.emit() แทน แบบนี้เลยครับ

แต่แค่นั้นยังไม่พอนะครับ เราจะต้องไปเพิ่มโค้ดรับข้อมูลที่ทางฝั่ง client ด้วย ให้เราเข้าไปที่ไฟล์ index.jade  แล้วเพิ่มโค้ดด้านล่างนี้ลงไปครับ

เรียบร้อยแล้วครับ เรามาลองเล่นดูเลย จะเห็นว่าเวลาเราพิมพ์อะไรลงไป ข้อความที่เราพิมพ์ก็จะขึ้นมาด้วยครับ แนะนำให้ลองเปิดมาอีก browser นึง แล้วลอง chat กับตัวเองดู เราก็จะพบว่าข้อความที่คุยกันมันอัพเดทแบบเรียลไทม์เลยล่ะครับ

บทสรุป

จะเห็นว่าการใช้ Node.js ร่วมกับ Socket.IO นั้น ช่วยให้เราสามารถสร้างแอพ Instant Messaging ขึ้นมาได้ง่ายๆ เลยใช่มั้ยครับ จริงๆ แล้ว การใช้ Socket.IO หลักๆ ก็มีอยู่เท่านี้ฮะ เราก็แค่ไปตั้งชื่อท่อของทั้งสองฝั่ง หากท่อชื่อตรงกันก็จะคุยกันรู้เรื่องครับ

จะว่าไปแล้ว คำว่า “ท่อ” ที่ผมใช้บ่อยๆ ในบทความนี้นั้น มันก็เหมือนกับ “ช่อง” ในการออกอากาศของทีวีครับ เพราะสัญญาณทีวีนั้นกระจายอยู่ทั่วไปในอากาศ การที่เรารับภาพจากช่องนี้ได้ก็เป็นเพราะช่องที่เราเปิดรับสัญญาณไว้นั้นตรงกับสัญญาณที่ออกอากาศมานั่นเองครับ ในทำนองเดียวกัน แม้ว่าสัญญาณช่องหนึ่งจะถูกออกอากาศมา แต่ถ้าเราไม่ได้เปิดรับสัญญาณของช่องนั้นเอาไว้ เราก็จะไม่ได้รับภาพของช่องนั้นครับ

ผมอยากให้เพื่อนๆ ไปลองต่อยอดแอพนี้ให้มันใช้งานได้จริงๆ นะครับ หรือจะลองใช้ Socket.IO ทำแอพแนวอื่นดูก็ได้ฮะ แอพไหนซับซ้อนหน่อยก็อาจจะใช้หลายๆ ท่อเข้ามาช่วย แล้วแต่เราจะออกแบบเลยครับ ส่วนตัวผมเองก็ได้ลองต่อยอดแอพนี้ไปนิดหน่อยครับ คือจะแยกสีข้อความของตัวเองออกจากข้อความของคนอื่นด้วย เพื่อนๆ คนไหนทำแอพเสร็จแล้ว อย่าลืมเอามาให้ลองเล่นกันบ้างนะครับ

Instant Messaging Screenshot

Download source
(Visited 20,854 times, 27 visits today)

14 Responses to “สร้างแอพ Chat ด้วย Node.js และ Socket.IO”

  1. Tab ไม่ถูกมี Error นะครับ *-*

  2. ขอบคุณมากครับ เยี่ยมเลย

  3. สุดยอดมากๆ อ่านของพวกต่างชาติ ไม่มีขั้นตอนละเอียด ของคุณเป็นขั้นตอน กระจ่างเลยครับ ขอบคุณมากๆ ทำให้ผมสนุกไปกับพวก js เพิ่มมากขึ้น เขียนต่่ออีกครับ ยกนิ้วโป้งให้ 2 นิ้วเลย

  4. อ่านครั้งเดียวรู้เรื่องครับ แต่มันจะมีวิธีส่งข้อมูลไปเก็บที่ database อีกทีสิน่ะครับ

  5. ทำไมของผม มันเตือน jQuery undefined สรุป ต้อง include jQuery เอง ไช่ไหมครับ ???

    /code
    Uncaught ReferenceError: $ is not defined

    /edit เพื่อความสมบูรณ์ 555 ผมขอแก้ไข
    script(src="https://cdn.socket.io/socket.io-1.3.3.js")
    script(src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js")

  6. นำไปประยุกใช้กับการเขียนเกมออนไลน์ใน canvas ได้ป่าวครับ

  7. เหมือนกันครับ ผมก็เจอ ไม่รู้ว่าจะแก้ยังไงดี

  8. ได้ครับ สั่งผ่าน socket เหมือนกัน

  9. อยากทำไรก็ข้างในนี้ไงครับ
    socket.on(ชื่อท่อ, callback)
    เมื่อได้รับข้อมูลมาทางท่อนี้ให้ทำอะไร
    socket.emit(ชื่อท่อ, ข้อมูลที่จะส่ง)
    ส่งข้อมูลไปยังทุกๆ client ที่เชื่อมต่อกับท่อนี้อยู่ สั่ง insert ก็ทำในนี้ครับ แล้วแต่ตามที่ออกแบบไงครับ- -

  10. เยี่ยมยอดมากๆครับ เป้นประโยชน์มาก ขอบคุณครับ

  11. ของผมมัน error แบบครับ ต้องแก้ยังไงครับ
    ————————————————————————————————————
    Error: E:node_JSchatviewsindex.jade:28
    26|
    27| socket.on('chat', function(message) {
    > 28| $('#chat-history').append($('<li class="message">').text(message));
    29|
    30| });

    Invalid indentation, you can use tabs or spaces but not both
    at Object.Lexer.indent (E:node_JSchatnode_modulesjadeliblexer.js:790:15)
    at Object.Lexer.next (E:node_JSchatnode_modulesjadeliblexer.js:941:15)
    at Object.Lexer.lookahead (E:node_JSchatnode_modulesjadeliblexer.js:113:46)
    at Parser.lookahead (E:node_JSchatnode_modulesjadelibparser.js:102:23)
    at Parser.peek (E:node_JSchatnode_modulesjadelibparser.js:79:17)
    at Parser.block (E:node_JSchatnode_modulesjadelibparser.js:725:30)
    at Parser.tag (E:node_JSchatnode_modulesjadelibparser.js:838:24)
    at Parser.parseTag (E:node_JSchatnode_modulesjadelibparser.js:759:17)
    at Parser.parseExpr (E:node_JSchatnode_modulesjadelibparser.js:211:21)
    at Parser.block (E:node_JSchatnode_modulesjadelibparser.js:729:25)
    ———————————————————————————————————–

  12. ขอบคุณครับ เข้าใจขึ้นเยอะเลย

  13. ผมต้องใช้ $(document).ready() เข้ามาช่วยไม่งั้น มันbind submitไม่ทัน

Leave a Reply