集點送紅利 已發佈 2019-11-4

Node.js 實作 The F2E_ChatRoom (1) 環境建置

前言

筆者最近剛接觸後端,看到很多人範例做留言板,
就想說既然要做,就來做個比較特別的!
也剛好之前在 The F2E 做過一個聊天室,不如就來正式接一次後端吧!

image

另外,本文使用 Node.js + MySQL + Socket.IO 實作,如有做錯地方歡迎討論謝謝!

User Story

  • 不須註冊,輸入名字即可登入,並且不和線上使用者撞名。

  • 聊天室分為每日主題與偷偷說,使用者能新增偷偷說。

  • 我可以使用貼圖,可以上傳圖片。

本篇目錄

  1. 使用 Express 建立連線。

  2. 串接 SQL。

  3. 使用 Socket.IO 建立即時連線,傳送訊息。

建立連線

使用終端機建立資料夾並進入:

$ mkdir chatroom

$ cd chatroom

建立 package.json 檔案,輸入完點擊 ENTER 到底就可以了。
( author 地方可以輸入自己名字 )

$ npm init


This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help json` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
package name: (chatroom)
version: (1.0.0)
description:
entry point: (index.js)
test command:
git repository:
keywords:
author: Hiro <[email protected]>
license: (ISC)
About to write to /Users/hiro/Desktop/chatroom/package.json:

{
  "name": "chatroom",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "Hiro <[email protected]>",
  "license": "ISC"
}

Is this OK? (yes)

建立後,在資料夾內新增 index.js 檔案,
並且在終端機安裝 Express:

$ touch index.js

$ npm install express --save

此時,你的資料夾應該長的會是這樣:

image

接著我們在 index.js 引入 Express,建立基礎連線:

// 引入 Express 並使用
const express = require('express');
const app = express();

app.get('/', function(req, res){
  res.send('Hello World');
});

// 監聽本地端 3000 port
const port = process.env.PORT || 3000;

app.listen(port, () => {
  console.log(`Listening on port ${port}...`);
});

最後啟動連接,在本地端 localhost:3000 有出現 Hello World 就成功了哦!
( 為了方便之後會使用 nodemon 建立連線 )

$ node index.js

串接 SQL

在 SQL 上先建立一個表格:

image

安裝好 npm 上的 MySQL:

$ npm install mysql --save

再來建立個連接池,可以提高連接效率:

const mysql = require('mysql');

const pool = mysql.createPool({
connectionLimit: 10,
host: 'localhost',
user: 'root',
password: '****',
database: 'the_f2e-note'
});

// connectionLimit: 限制人數
// host: 連線主機
// user: 使用者
// password: 密碼
// database: 資料庫

建立好後,把剛剛的 get 改寫為以下,試試看連接狀態:

app.get('/', function(req, res){

  // 接上連接池
  pool.getConnection((err, connection) => {
    if (err) throw err;

    // 輸入 SQL 語法查詢
    connection.query('SELECT * FROM name ORDER BY rand() limit 1',   
    (err, rows, fields) => {
      if (err) throw err;

      // 送出查詢結果
      res.send(rows);

      // 斷開連結
      connection.release();
    });

  });
});

image

有出現就代表連接成功了哦!

Socket.IO

再來我們的主角終於可以進場了!
Socket.IO 是個可以建立即時性溝通的網頁應用程式,傳輸方式上是使用 WebSocket,另外還增加了許多方便的 API,最後才包裝出來的!
( 可參考延伸閱讀 )

而相較於留言板,聊天室是非常需要及時性的!
因此才說到 Socket.IO 是這次的主角。


接著,我們要來試著引入 Socket.IO:

$ npm install socket.io --save

把之前的監聽路由改為以下:

// app.listen(port, () => {
//   console.log(`Listening on port ${port}...`);
// });

const server = require('http').Server(app).listen(port, () => {
  console.log(`Listening on port ${port}...`);
});

const io = require('socket.io')(server);

加入 Socket.IO 監聽連線,
getMessage 的地方可自由變更,但要和前端做搭配,會在下方做補充說明!

io.on('connection', socket => {
  console.log('連接成功,上線ID: ', socket.id);

  // 監聽訊息
  socket.on('getMessage', message => {
    console.log('服務端 接收 訊息: ', message);

    //回傳 message 給客戶端
    socket.emit('getMessage', message);
  });

  // 連接斷開
  socket.on('disconnect', () => {
    console.log('有人離開了!, 下線ID: ', socket.id);
  });
});

現在服務端完成監聽連線了!
但…我們還需要客戶端來配合,所以在這邊會開始加入前端部分。
一般會使用 socket.io-client,但剛好之前實作前端是使用 Vue 框架的關係,

這次前端使用的套件會是 vue-socket.io


照著步驟一樣先安裝:

$ npm install vue-socket.io --save

在 main.js 檔引入套件並使用:

import VueSocketIO from 'vue-socket.io';

Vue.use(new VueSocketIO({
  debug: true,
  connection: 'http://localhost:3000',
}))

接著連線客戶端 8080 (前端)與 3000 (後端),
8080 在 console 中會看到 vue-socket.io 的 debug 訊息:

image

3000 的終端機上則會看到:

image

另外在 Socket.IO 上得默認機制為每 25 秒客戶端會發送一次 ping,服務端回復一次 pong,如果 60 秒內都沒收到 ping 的話,就會自動斷線。所以網頁掛一段時間,會看到客戶端重新連接 (下線又上線)。

接下來,要開始試做傳送訊息!
首先我們 客戶端 (前端) 要做一個雙向綁定:

export default {
  data() {
    return {
    };
  },
  created() {
    // 客戶端 接收 訊息
    this.sockets.subscribe('getMessage', (data) => {
      console.log(data);
    });
  },
  methods: {
    // 客戶端 傳送 訊息
    this.$socket.emit('getMessage', {
      text: 'test123',
    });
  },
};

剛剛說的 getMessage,就像是下圖這樣綁定的,
以此方式,就能做出不同房間個別傳送訊息!

image

傳送出去後,就可以看到 console 顯示了,
試著傳送不同的訊息吧!

image

image

另外,官方還提供了不同的傳送訊息方式,
分組傳送可以參考延伸閱讀。

// 只回傳給發送訊息的客戶端
socket.emit('getMessage', {
  text: 'test123',
});

// 除了自己之外的所有客戶端
socket.broadcast.emit('getMessage', {
  text: 'test123',
});

// 傳給所有客戶端
io.sockets.emit('getMessage', {
  text: 'test123',
});

參考資料

Vue js 如何使用 Socket IO ?

React | 在 React 中使用 WebSocket - feat. Socket.io 基本教學

socket.io emit的幾種用法解釋

關於筆者

暱稱:集點送紅利

介紹:我喜歡日文,也喜歡 coding

文章列表 文章列表