Node.js

簡易聊天室實作

今日範例

那我們會用到什麼

  • Node.js(廢話
  • npm
  • socket.io

Nodejs是什麼

Node.js 是 Ryan Dahl 基於 Google 的 V8 引擎於 2009 年釋出的一個 JavaScript 開發平台,主要聚焦於 Web 程式的開發,通常用被來寫網站。

  • 開源
  • 跨平台
  • 基於Javascript
  • 非同步

Nodejs的特色

如何安裝

NPM

(Node Package Manager)

Javascript編寫的軟體套件管理系統,預設環境為Node.js

從 Node.js 0.6.3 版本開始,npm被自動附帶在安裝包中

# 安裝套件
$ npm install <moduleName>

# 安裝全域套件
$ npm install <moduleName> -g

# 移除套件
$ npm uninstall <moduleName>

# 移除全域套件
$ npm uninstall <moduleName> -g

npm 預設的模組可以從 http://npmjs.org 維護的public package registry 取得,模組可以使用 npm install 安裝:

npm 常用指令

# 檢查npm版本
$ npm --version

# 列出全域套件
$ npm ls -g

# 更新全域套件
$ npm update -g

# 更新專案套件
$ npm update

Socket.io

先別提socket你聽過websocket嗎

WebSocket 是 HTML5 開始提供的一種瀏覽器與伺服器之間,一個雙向通訊的網路技術

  1. client端(瀏覽器)發出HTTP訊息詢問server端能否使用websocket來溝通
  2. 若server端表示OK,則跳至Step 3
  3. client端使用websocket API中的onopen() 來建立websocket通道
  4. 建立通道後,server端則透過onmessage() 來傳遞訊息給client端
  5. client斷開連結後,送出onclose() 通知server端此次websocket的連線中斷

上面說了這麼多,說穿了其實就是瀏覽器在和伺服器互戳

那它的支援度如何呢?

貌似支援度挺高的呢(⁰▿⁰)

有什麼應用

安裝 Socket.IO

$ npm intall socket.io

建立基本 Server

var http = require('http');

var server = http.createServer();
server.listen(3000);

將這段程式碼儲存成 "server.js" 然後執行

node server.js

建立基本 Server

var http = require('http');

var server = http.createServer(function(req, res) {
  res.writeHead(200, {'Content-Type': 'text/html'});
  res.write('Hello, World!');
  res.end();
});
console.log('3000');
server.listen(3000);

讓我們來看看剛剛那串咒文在做甚麼

// 載入http模組
var http = require('http');

// 利用內建的createServer架設一個基本的HTTP伺服器
// 用一個call back function接收HTTP伺服器的 request & response
// 最後HTTP伺服器需要設定listen的port,監聽所有HTTP伺服器行為
// 並同時將server收到的訊息顯示在終端上(console.log)
var server = http.createServer(function(req, res) {
  //  接著使用 response.writeHead() 設定 HTTP 回應的標頭資訊
  res.writeHead(200, {'Content-Type': 'text/html'});
  // 接著設定網頁內容
  res.write('Hello, World!');
  // 最後結束整個定義過程
  res.end();
});
console.log('3000');
server.listen(3000);

不過不可能都用 res.write 來寫網頁

var http = require("http");
var url = require('url');
var fs = require('fs');


var server = http.createServer(function(request, response) {
  // 當伺服器接收到請求時,使用 url.parse() 函數解析出網址中所指定的路徑
  // 可用 console.log(path) 來看看實際路徑解析的情形
  var path = url.parse(request.url).pathname;

  // 依照解析的路徑決定動作
  switch (path) {
    case '/':
      response.writeHead(200, {'Content-Type': 'text/html'});
      response.write('Hello, World.');
      response.end();
      break;
    case '/socket.html':
    // 若路徑為 /socket.html 則用fs 模組讀取socket.hthml檔案
      fs.readFile(__dirname + path, function(err, data) {
        // cheak讀取連結過程是否有誤
        if (err){
          //若連結過程有誤則回應404
          response.writeHead(404);
          response.write("opps this doesn't exist - 404");
        } else {
          //成功讀取則載入連結
          response.writeHead(200, {"Content-Type": "text/html"});
          response.write(data, "utf8");
        }
        // 最後一樣要記得加上 response.end() 結束整個處理流程的定義,讓它開始執行
        response.end();
      });
      break;
    default:
      response.writeHead(404);
      response.write("opps this doesn't exist - 404");
      response.end();
      break;
  }
});

console.log('3000');
server.listen(3000);

那我們來對原本的程式修改"一點"

說好的一點哩Σ(;゚д゚)

莫驚慌聽我解釋

var url = require('url');
var fs = require('fs');

先來看看我們新引入的兩個小夥伴

url 是用來解析 URL 網址模組

fs 則是處理檔案模組

var path = url.parse(request.url).pathname;

當伺服器接收到請求時,使用 url.parse() 函數解析出網址中所指定的路徑

Input: 127.0.0.1:3000

Output: /

Input: 127.0.0.1:3000/socket.html

Output: /socket.html

可以加上 console.log(path) 試試看結果

定義檔案在讀取之後要進行什麼動作

fs.readFile(__dirname + path, function(error, data){...});
// error: 錯誤代碼,如果在讀取檔案出問題時(如檔案不存在),這個錯誤代碼就會被設定
// data: 檔案的內容
if (error){
  response.writeHead(404);
  response.write("opps this doesn't exist - 404");
}
else {
  response.writeHead(200, {"Content-Type": "text/html"});
  response.write(data, "utf8");
}

這裡我們先檢查讀取的過程是否有發生錯誤

如果檔案讀取成功的話,就輸出檔案的內容

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>SocketIO</title>
  </head>
  <body>This is our socket.html file</body>
</html>

建立一個 socket.html 檔案,內容如下

執行看看 server.js ,看看結果

var io = require('socket.io');

引入今天的主角 socket.io 模組

幫socket.html加入socket.io連線功能

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>SocketIO</title>
  </head>
  <body>
    <div>This is our socket.html file</div>
    <script src="/socket.io/socket.io.js"></script>
    <script>
      var socket = io.connect();
    </script>
  </body>
</html>
var serv_io = io.listen(server);

// on() 函數將特定的事件連接到指定的匿名函數
// 若websocket一建立連線,就會使用 emit() 函數來傳送資料
serv_io.sockets.on('connection', function(socket) {
  console.log('hi~');
  socket.emit('message', {'message': 'hello world'});
});

傳送資料至瀏覽器

這樣當瀏覽器呼叫 io.connection() 時就會呼叫這個匿名函數,重整網頁看看,terminal應該會跑出console.log的內容

並且使用 emit() 函數來傳送資料

socket.emit('message', {'message': 'hello world'});

emit() 會產生一個事件,格式如下:

socket.emit('事件名稱', '資料(Json)');

在伺服器建立連線後,瀏覽器也需要接收資料,所以需要在 socket.html 新增點東西

var socket = io.connect();

socket.on('message', function(data){
  console.log(data.message);
});

到瀏覽器看看 console 內從server收到的東西

現在讓我們看看websocket厲害的地方

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>SocketIO</title>
  </head>
  <body>
    <div id="date"></div>
    <script src="/socket.io/socket.io.js"></script>
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
    <script>
      var socket = io.connect();
      socket.on('date', function(data) {
        $('#date').text(data.date);
      });
    </script>
  </body>
</html>

更改一下 socket.html

serv_io.sockets.on('connection', function(socket) {
  // 傳送時間訊息給瀏覽器
  setInterval(function() {
    socket.emit('date', {'date': new Date()});
  }, 1000);
});

修改一下 service.js

setInterval() 中時間的換成100(0.1sec)或是更短的時間,看看會發生甚麼事情

沒錯!它還是能運作ヽ(✿゚▽゚)ノ

Socket.IO 所擅長的其實是持續性的資料傳遞,是不是很棒棒

傳送資料至伺服器

<!DOCTYPE html>
<html lang="en">
 <head>
    <meta charset="UTF-8">
    <title>socket.io</title>
  </head>
  <body>
    <div id="date"></div>
    <textarea id="text"></textarea>
    <br>
    <button id="btn">send</button>
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
    <script src="/socket.io/socket.io.js"></script>
    <script>
      var socket = io.connect();
      socket.on('date', function(data) {
        $('#date').text(data.date);
      });
      socket.on('message', function(data){
        console.log(data.message);
      })
      $(document).ready(function(){
        $('#btn').click(function() {
          var msg_text = $('#text').val();
          socket.emit('client_data', {
            'letter': msg_text
          });
          $('#text').val('');
        });
      });
    </script>
  </body>
</html>

在網頁做點修改,增加textarea

傳送資料至伺服器

serv_io.sockets.on('connection', function(socket) {
  setInterval(function() {
      socket.emit('date', {'date': new Date()});
    }, 1000);
  socket.on('client_data', function(data) {
    console.log(data.letter);
    socket.emit('message', {'message': new Date() + " : " + data.letter})
  })
});

在server.js做點修改讓伺服器能接收到來自瀏覽器的資料

現在你會聊天室應該要有的功能了

那一個聊天室應該會有什麼?

  • 一個訊息輸入框
  • 發送訊息按鈕
  • 一個放大家留言的地方
  • 暱稱欄位(?
  • 一個訊息輸入框
  • 發送訊息按鈕
  • 一個放大家留言的地方
  • 暱稱欄位(?

我在課程的github上有附了一個非常簡易的範例可以參考

投影片結束啦ヽ(✿゚▽゚)ノ

Made with Slides.com