# Web Server軟體 V.S. NodeJS
※本篇內容僅用以方便我自己記憶用,不一定符合業界習慣且無法保證100%正確。
## 本篇名詞解釋
### Web Server軟體
市面上常見的,用以開啟網頁伺服器連線服務的軟體,如[Apache HTTP Server](https://zh.wikipedia.org/zh-tw/Apache_HTTP_Server)、[Nginx](https://zh.wikipedia.org/zh-tw/Nginx)、[微軟IIS](https://zh.wikipedia.org/zh-tw/%E7%B6%B2%E9%9A%9B%E7%B6%B2%E8%B7%AF%E8%B3%87%E8%A8%8A%E6%9C%8D%E5%8B%99)。
### HTTP Server
定義大致上等同網頁伺服器(Web Server)。
接收來自HTTP protocol(且通常是通過80 port或443 port)的流量並進行處理、給予回應的伺服器。80 port為HTTP protocol常用的連接埠,443則為HTTPS(含確認安全憑證相關程序,為目前主流的網頁通訊協定)的常用連接埠。
### Node.js
JavaScript的框架(Framework),用來撰寫網路服務相關的功能,模組化、入門難度低,且**可以取代其他Web Server軟體,用來打開HTTP Server**。
<br>
## 實作 & 區別
以下皆以我本次實作經驗為例,作業系統為Ubuntu 22.04.1 LTS,使用Apache2來當HTTP Server,並讓Nodejs的WebSocket模組接管3000 port。
### Apache
**在預設config下,Apache會監聽80 port的流量,建立一個HTTP Server**。啟動Apache之後,一般使用者即可透過在瀏覽器輸入http://{IP-address}:80來存取你的網頁 (或http://{domain-name}:80,如果你有綁定域名的話)。網址後面的:80表示使用者透過80 port連線,由於80和443是最常用的HTTP port number,key網址時通常可以直接省略。
網頁要顯示的內容預設是編寫在/var/www/html底下的index.html中。
申請憑證且設定好SSL相關config後,可透過指令開啟https連線功能 (通常也就是443 port的對外連線,記得也要allow linux防火牆的443流量)。
(相關教學:[Apache 安裝 HTTPS 教學](https://medium.com/feveral%E7%9A%84%E7%A8%8B%E5%BC%8F%E7%AD%86%E8%A8%98/apache-%E5%AE%89%E8%A3%9D-https-%E6%95%99%E5%AD%B8-dd022e2be7b4))
```
# 撰寫SSL相關的config
sudo vim /etc/apache2/sites-enabled/ssl.conf
# 啟用剛剛寫好的ssl.conf
sudo a2enmod ssl
# 重開Apache
sudo service apache2 restart
# 允許443 port通過防火牆
sudo ufw allow 443
```
另外,透過編輯config檔案,可設定proxy,依照不同情況把client的request重新導向到指定的port做處理,或者做自動轉址(如:把 http:// 自動轉址到 https:// ,半強制使用者透過https連線,避免沒有經過SSL憑證驗證的流量)
(相關範例:[如何設定WebSocket轉導與分流](https://www.tpisoftware.com/tpu/articleDetails/947) 、 [How to automatically redirect HTTP to HTTPS on Apache servers?](https://stackoverflow.com/questions/16200501/how-to-automatically-redirect-http-to-https-on-apache-servers))
如果需要開啟其他port,同樣可透過編輯config檔案來達成。
###### > /etc/apache2/sites-enabled/000-default.conf (VirtualHost後面的*:80為port number)

###### > 開啟80與443 port之後,透過netstat指令確認這些port的監聽狀況

### Node.js
同樣可用來開啟server,監聽的port依照程式碼編寫內容而定,也可以一次開啟並監聽數個port。
如果監聽80 port或者443 port,大致上便是取代了Apache的功能、建立一般的HTTP Server。
以下範例程式是以nodejs的https套件來開啟HTTP server,且這裡指定的port number是3000 port而不是443 port,因為待會還有特殊用途。
(相關教學:[Day7 - Node.js 內建的 Web Server 介紹及使用](https://ithelp.ithome.com.tw/articles/10185302))
```javascript=
//import相關套件
const fs = require('fs')
const https = require('https')
const SocketServer = require('ws').Server
const PORT = 3000 //指定 port
const options = {
// SSL憑證相關檔案路徑
key: fs.readFileSync('/privkey.pem'),
cert: fs.readFileSync('/fullchain.pem'),
}
//建立server
const server = https.createServer(options);
server.listen(PORT, '0.0.0.0', () => {
console.log(`Listening on ${PORT}`);
});
```
到這邊一般使用者即可透過輸入https://{domain-name}:3000來連上你的網頁,但因為網頁內容是空白的,瀏覽器可能會回傳EMPTY RESPONSE錯誤訊息來告訴你它沒有東西可顯示。
###### > 透過netstat指令,可以看到在執行node之後,3000 port已經被監聽了。

承上述程式碼,接下來我將server交給ws套件,它將開啟WebSocket(另一種通訊協定)的相關服務。
```javascript=
//將 express 交給 SocketServer 開啟 WebSocket 的服務
const wss = new SocketServer({ server });
```
從這裡開始,一般使用者就無法再透過https://{domain-name}:3000連上你的網頁,因為3000 port目前已經被WebSocekt server監聽,得透過**wss**://{domain-name}:3000來連線 (但並不是直接輸入在瀏覽器的網址列,必須透過WebSocket協定來連線,這部份有一些線上測試工具可以用)
※因為前面使用的是**https**連線,所以WebSocket會強制規定Client端只能用同樣有通過SSL驗證程序的**wss://** (WebSockets over SSL/TLS)來連線,否則會報錯。如果前面用的是不需要安全憑證的**http**,則可以用**ws://** 開頭的網址進行連線。
WebSocket協定可以做到讓使用者與伺服器即時互動,甚至再透過伺服器broadcast來達到使用者與使用者即時互動的效果。以這支程式為例,呈現的就是簡易的即時聊天室功能。
(參考範例:[[WebSocket] 實作一個簡單的聊天室 (+ Node.js)](https://eudora.cc/posts/220105/))

```javascript=
//當有 client 連線成功時
wss.on('connection', ws => {
ws.send("This website is for test purpose, please do not leave any personal information here.");
console.log('Client connected')
// 當收到client消息時
ws.on('message', data => {
// 收回來是 Buffer 格式、需轉成字串
data = data.toString()
console.log(data) // 可在 terminal 看收到的訊息
/// 發送消息給client
ws.send(data)
/// 發送給所有client:
let clients = wss.clients //取得所有連接中的 client
clients.forEach(client => {
client.send(data) // 發送至每個 client
})
})
// 當連線關閉
ws.on('close', () => {
console.log('Close connected')
})
})
```
### 流程圖
