在2026新年第一篇PO這篇真的蠻瘋的
不過蠻懷念也蠻有趣的
總之在與Gemini進行對話時
標題這念頭就像突然被電波打到一樣
出現在我的腦中
稍稍想想,可行性應該是有的
就向Gemini繼續詢問其可行性後
以玩樂性質執行下去
![]() |
| 這次使用夢之大地版本架設 |
談到BBS啊,對我來說有很多回憶
(↓↓↓以下話多可以跳過↓↓↓)
在2000年左右,在大學中架設BBS站可說是一股熱潮
不只每間學校都會有,個自的系所也會各自架設
更有不少是私人自行架設的
只是架設雖然不算太難,後續維護真的是不簡單的一件事
而且只有文字這種架構真的太老舊到不符合現在的需求
那被時代所淘汱也是很正常的
台大的ptt靠著超強人氣還是能活到現在也算個異數了
大學時期我也有被學長拜託架設BBS系站過
只是時間太久了,忘了是整個系站掛了,完全推倒重來
還是我把系統弄起來後,學長再把備份還原回去
但總之,我有從頭到尾架設過一個BBS站的經驗
這也是我實際接觸Unix系統的開端
之前都是從書籍雜誌上看到這不同於Windows的系統而已
不過不是現在常見的Linux,而是更少見的FreeBSD
也從那時理解了Unix驅動難搞的一面
記得那台舊主機是配3com的ISA介面網路卡
然後我手上剛好有張多的D-link網卡,PCI介面的
本著PCI速度應該比ISA快,加上PCI通常支援Plug and play(隨插即用)
我就試著改用D-link看看
唉~~那個plug and play不適用FreeBSD啊,驅動還是要另外灌,還不知道哪一版對
弄了好久,好不容易看似搞定
結果一開始網路很順,主機上線一陣子,D-link就掛點了
重開機又復活,可是又是一陣子就斷線...
最後還是又換回3com這片老爺級網卡,速度上,反正系站沒兩三隻貓也撐得住
重點是穩到不行,伺服器最重要的就是穩啊
(↑↑↑回憶結束↑↑↑)
回到主題,在樹莓派1代上架設使用WSS(WebSocket Secure)連線的BBS站這件事
一口氣混了兩個大主題,有點複雜
縮小一下範圍,先來談主體的部分,在樹莓派1上架設BBS站
樹莓派1代以現在說來弱歸弱,其CPU跟記憶體與當年相比絕對不差
樹莓派1代的CPU是700Mhz的速度
而且至少有個256MB的記憶體是跑不掉的
當年CPU普通的P2也才266Mhz,而記憶體有個128MB算頂級配備來說
樹莓派的這台伺服器絕對夠力,而且我記憶體是512MB版本,更強
重點其實在於,編譯會不會過這件事
畢竟就算是當年的BBS,在make時也是可以選Solars、Sun,BSD,Linux這些作業系統
不同的系統,編譯參數就需要掛不同,樹莓派雖然是Linux的一支,但也不一定完全相容
更何況gcc的版本與當年不一樣了,還有Big編碼的問題,能不能過實在是未知數
所以我就把後續交給了Gemini
詢問Gemini後,給我的答案只有一條路
「可以架,建議要用最新的ptt的開源版架站
這套有持續修訂符合現在環境
不過因為樹莓派1代是單核CPU,速度不快,加上記憶體不大
所以要SWAP硬碟(SD卡)空間當記憶體用才會編譯得起來
而且會跑很久,要數小時以上
然後再來現在ptt可以容納人數非常高,可能把資源全部吃完
所以記得初始設定時,要把人數設小一點,才不會出事」
看到這裡就覺得,那還是不要用ptt版的
更何況後面的實作Gemini又說
要用SWAP以硬碟空間建置足夠大小的記憶體來因應編譯
看著這行想要到要執行所花的時間,頭就很痛
就算編譯出來了,用一1代這貧弱規格去跑的話,可能真的跑不太動
接著就逼迫AI往使用比較舊版的選項前進
Gemini一開始是建議我找所謂的itoc版
不過原始網站(http://processor.tfcis.org/~itoc/)已經陣亡,一時找不到這版本
找了一個比較新2005年的wake/MapleBBS,號稱支援64bit
https://github.com/wake/MapleBBS
就努力地讓它跑起來,算成功了一半,可以連上去
不過無法註冊使用者,什麼事都不能做,跟廢站沒兩樣
後來總算找到夠新的(2012年)itoc版原始碼的github
https://github.com/xeonchen/maplebbs-itoc/
可惜這版不知道出問題在哪裡,一開始能註冊能使用,就站長功能無法使用
後來試著重裝又變成可以註冊但一連線就斷線的奇怪狀況
最後是用夢之大地的版本
https://github.com/ccns/dreambbs/
不得不說,不虧是持續有更新的版本,要在樹莓派上運行修改幅度是最少的
而且有很完整的架站文件可以參考了
所以整理後的架站流程如下:
1.安裝系統套件
首先,要照著安裝手冊需求https://github.com/ccns/dreambbs/wiki/INSTALL
我們要這幾個套件
gitmakecmakegccvim
sudo apt update
然後進行下面這幾個套件的安裝
sudo apt install git gcc make cmake vim libncurses-dev
多了一個libncurses-dev,這是因為這算是終端機顏色開發用庫
Gemini說MapleBBS系應該都會用到,所以我也放進來
另外如果習慣用inetd進行開機後自動執行,可以自行安裝openbsd-inetd套件來用
但是樹莓派的服務主要都是用Systemd管理
夢之大地也提供這方面的參考文件可以直接套用
所以我就用這個處理,不額外裝openbsd-inetd
2.建立BBS帳號
指令就是這兩個:
sudo groupadd -g 99 bbs
sudo adduser --uid 9999 --gid 99 --disabled-password --gecos "" bbs
夢之大地是用一行,不過那個沒加sudo有問題外,也還要輸入一堆資訊
有加--gecos就不用囉嗦了,直接不輸入
然後使用--disabled-password,表示不使用密碼
不使用密碼不代表可以不用密碼登入,相反的,必須透過可以使用sudo權限的帳號進行登入
也不能直接從外面登錄,除非有另外設定,這比較安全
這邊也順便提一樣為什麼要設定這兩個數字
因為古早bbs為了高效管理記憶體與檔案權限
預設在程式碼裡的使用者ID(UID)與群組ID(GID)會依這兩組數字
去進行記憶體管理與檔案權限確認
如果程式裡是跑這兩組數字,結果執行程式的使用者ID不是這數字
就會發生一連就斷線與無法存取文章的狀況
之前Wake版的MapleBBS架失敗應該就是這邊沒改到所以無法註冊
但itoc版明明都正確了,卻還是有問題就搞不懂狀況了
3.下載原始碼
接下來就是進入/home/bbs裡下載原始碼了
先登入為bbs,進入/home/bbs中,再用git下載到dreambbs中
sudo su bbs
cd
git clone https://github.com/ccns/dreambbs
cd dreambbs
就可以準備進行下一步了
4.編輯原始碼內容與部署
這是最麻煩的部分了
首先,從dreambbs裡的sampel取出dreambbs.conf到dreambbs底下
cp sample/dreambbs.conf ./
然後修改這個檔案裡的資料將這個站變成自己的東西
不過麻煩的事,這是big5編碼的文件,如果使用樹莓派常用的nano下去編輯
根本沒辦法處理,所以需要用vim來處理
可以使用這一行指令讓vim以big5的編碼方式打開這個檔案
vi -c "e ++enc=big5" dreambbs.conf
替換完想換的資訊後,就可以進行下一步
我是把站名換成"BBS小站",簡稱"BBS",學校就改叫"社會大學"
站長就是"站長",ip顯示拿掉等等
接著在dreambbs裡CMakeLists.txt裡找到這一行(第66行)
add_compile_options(-Wall -Wpointer-arith -Wcast-qual -Wwrite-strings -Werror=format)
在上面加上
add_compile_options(-fsigned-char -fcommon)
再來找這一行(第255行)
add_compile_options(-fPIC)
然後把上下的IF條件拿掉,或註解起來,讓這個必定執行
修改就告一段格了
接著就是照安裝手順說的,建立build的資料夾
mkdir build
然後進入這個資料夾中,執行cmake
cd build
cmake ..
那個cmake後的點點記得要打
這樣編譯的初始工作就差不多了
5.下載BBS資料結構
如安裝手冊所說,下載了dreambbs其實只有原始碼
還欠缺了整個運行所需要的基本資料與目錄結構
這邊就照著安裝手順裡提的,下載dreambbs_snap後
再將資料放/home/bbs的資料下
git clone https://github.com/ccns/dreambbs_snap.git bbs
cp -r ./bbs/* /home/bbs/
6.編譯BBS執行檔
這邊就可以回到dreambbs底下的build資料夾中
去進行建立與安裝
make all install
7.執行測試與後續建置
編譯完成後,我們先試運作BBS服務看看
移動到/home/bbs/bin底下
或是乾脆直接跑絕對路徑的這幾程式
/home/bbs/bin/camera
/home/bbs/bin/account
/home/bbs/bin/acpro
/home/bbs/bin/makefw
/home/bbs/bin/bbsd 3023
執行正常的話,用telnet://xxx.xxx.xxx.xxx:3023
應該就可以連上這個站了
雖然進站畫面怪怪的
那接著趕快註冊站長權限的SYSOP帳號(這個帳號註冊畫面也怪怪的)
雖然剛註冊完進來,不但看到要認證,還不會看到站長選單
退出去後再進來就沒有問題了,可以看到多了系統維護區
然後就可以管站了
後續建置方面,先照安裝說明裡講的把一些routine工作丟進crontab中
在/home/bbs/dreambbs/build的資料夾下,執行
crontab sample/crontab
注意,是在build底下的sample,因為這邊才有cmake依環境變數過的檔案
第一次執行,我錯誤地往dreambbs底下的sample抓,沒抓到
進目錄只看到帶.in的範例檔,這邊是錯的,請進build底下的sample抓
接著因為選擇用Systemd服務,dreambbs有提供這方面的檔案,所以更簡單了
正常的話,把sample裡(一樣是buildl底下的)bbsd.service檔案
丟入/etc/systemd/system的資料夾中就行了
另外也請丟入startbbs.serice與xchatd.service
!!!注意!!!
這邊請用離開bbs使用者
改用原本能使用sudo的帳號執行,不要開啟bbs的sudo功能
如果要長期使用的話,建議把bbs的sudo權限整個關掉
然後輸入
cp /home/bbs/dreambbs/build/sample/bbsd.service /etc/systemd/system
cp /home/bbs/dreambbs/build/sample/startbbs.service /etc/systemd/system
cp /home/bbs/dreambbs/build/sample/xchatd.service /etc/systemd/system
接著再輸入
sudo systemctl enable bbsd
將bbsd的服務正式打開
!!!注意!!!
這樣重開機後,就會看到BBS站正常上線了
不過要注意,重開機與啟用服務後,這時使用的port是預設的23囉
請用telnet://xxx.xxx.xxx.xxx:23連線
想要改其它port請修改bbsd.service檔中的23為其它數字
=========BBS與 WSS分隔線=========
接下來就換到加密WebSocket連線的部分了
這個其實夢之大地也有實作,不過它是靠Lua處理
然後似乎要架設nginx(因為有提到要確認nginx.conf設定)
不過我就沒深入研究,我也不用這個,因為一開始我就是想靠python來處理這塊
也在前面架設itoc版的MapleBBS時就實作成功了
(那時架站失敗只是不能進入與管理,還是能註冊能連線,就以python實作出來了)
後來想到安全性號稱很高的rust似乎也能做到,也試著弄了一個
但,先說結論
以樹莓派1來說,請考慮用python來跑就好
理由後面會說明
會選用python的理由很簡單,除了我會以外,目前樹莓派主機預設都會裝
就算沒有,要再安裝也都非常方便,沒什麼問題
只是要注意,websocket套件要另外安裝
pip3 install websockets
廢話不多說,請Gemini寫的轉接Telnet與Websocket的程式,命名為ws-proxy.py
然後將ws-proxy.py放在/home/bbs底下
詳細程式碼如下:
import asyncio
import websockets
import ssl
import os
import logging
# --- 設定區域 ---
# 本地 WebSocket 伺服器監聽設定
LOCAL_HOST = "0.0.0.0"
LOCAL_PORT = 8000
# 目標 Telnet 伺服器設定 (預設監聽自己)
TARGET_HOST = "0.0.0.0"
TARGET_PORT = 23
# SSL/TLS 憑證路徑 (若要啟用 WSS 請填寫路徑)
# 如果檔案不存在,程式會自動降級為普通 WS 模式
SSL_CERT_FILE = "cert.pem"
SSL_KEY_FILE = "key.pem"
# 設定日誌
logging.basicConfig(
format="%(asctime)s %(levelname)s: %(message)s",
level=logging.INFO
)
async def forward_ws_to_telnet(ws, writer):
"""讀取 WebSocket 訊息並寫入 Telnet (TCP)"""
try:
async for message in ws:
# WebSocket 收到通常是字串或 bytes
if isinstance(message, str):
# 如果是字串,需編碼成 bytes 發送給 Telnet
# 注意:台灣 BBS 通常使用 big5,一般 Telnet 使用 utf-8 或 ascii
# 這裡為了通用性,若傳來字串則嘗試編碼,但建議前端直接送 Blob/ArrayBuffer
data = message.encode('utf-8')
else:
data = message
writer.write(data)
await writer.drain()
except websockets.exceptions.ConnectionClosed:
logging.info("WebSocket 連線已關閉 (Client)")
except Exception as e:
logging.error(f"WS -> Telnet 錯誤: {e}")
async def forward_telnet_to_ws(reader, ws):
"""讀取 Telnet (TCP) 訊息並寫入 WebSocket"""
try:
while True:
# 讀取 Telnet 串流
data = await reader.read(4096)
if not data:
break
# 直接將原始二進位資料轉發給 WebSocket 客戶端
# 讓前端 (JavaScript) 決定如何解碼 (例如使用 TextDecoder 解碼 Big5)
await ws.send(data)
except Exception as e:
# 當連線中斷時會跳出
pass
finally:
logging.info("Telnet 連線已關閉 (Server)")
async def handler(websocket):
"""處理每一個新的 WebSocket 連線"""
client_addr = websocket.remote_address
logging.info(f"新的連線來自: {client_addr}")
reader = None
writer = None
try:
# 1. 建立與目標 Telnet Server 的 TCP 連線
logging.info(f"正在連線到 Telnet 伺服器 {TARGET_HOST}:{TARGET_PORT}...")
reader, writer = await asyncio.open_connection(TARGET_HOST, TARGET_PORT)
logging.info("Telnet 連線成功")
# 2. 建立雙向轉發任務
# task1: WS -> Telnet
# task2: Telnet -> WS
task_ws_rx = asyncio.create_task(forward_ws_to_telnet(websocket, writer))
task_telnet_rx = asyncio.create_task(forward_telnet_to_ws(reader, websocket))
# 3. 等待任一邊連線結束
done, pending = await asyncio.wait(
[task_ws_rx, task_telnet_rx],
return_when=asyncio.FIRST_COMPLETED
)
# 4. 清理未完成的任務
for task in pending:
task.cancel()
except ConnectionRefusedError:
logging.error(f"無法連線到目標 Telnet 伺服器: {TARGET_HOST}")
await websocket.close(reason="Target Telnet Server Unreachable")
except Exception as e:
logging.error(f"發生未預期錯誤: {e}")
finally:
# 確保關閉 Telnet 連線
if writer:
writer.close()
try:
await writer.wait_closed()
except:
pass
logging.info(f"連線結束: {client_addr}")
async def main():
# 檢查 SSL 設定
ssl_context = None
if os.path.exists(SSL_CERT_FILE) and os.path.exists(SSL_KEY_FILE):
logging.info(f"發現 SSL 憑證,啟用 WSS 模式 ({SSL_CERT_FILE})")
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
ssl_context.load_cert_chain(SSL_CERT_FILE, SSL_KEY_FILE)
else:
logging.warning("未發現 SSL 憑證或檔案路徑錯誤,使用普通 WS 模式")
logging.info(f"Proxy 伺服器啟動於 port {LOCAL_PORT}")
# 啟動 WebSocket 伺服器
async with websockets.serve(handler, LOCAL_HOST, LOCAL_PORT, ssl=ssl_context):
await asyncio.Future() # 讓程式持續運行
if __name__ == "__main__":
try:
asyncio.run(main())
except KeyboardInterrupt:
logging.info("伺服器手動停止")
說明一下
這程式碼還需要兩個SSL設定的檔案配合
也就是認證檔cert.pem跟鑰匙檔key.pem
或者是xxx.crt跟xxx.key這樣的檔案
才能成為WSS通訊
沒有的話就變成普通的Websocket通訊,也就是沒加密
沒加密用Websocket的功效就不大了,頂多可以突破公司防火牆
所以建議就是靠著現在免費的Let's Encrypt進行申請就好
這部分要從零開始詳細說明又是另一篇長文了,就不多說了
久遠前我有寫過申請辦法,也是跟著樹莓派做的
只是外面隔著一台RT1900AC,所以還要靠著架設的Apache處理,不一定符合所有人
想了解請參考這篇:在RT1900AC上使用Let's Encrypt的SSL憑證-半殘版
跳回到主題WSS,檔案都準備好後,執行
python3 ws-proxy.py
然後看能不能用wss連進來就知道成不成功了
測試完成後,就是背景服務化的設定了
寫一個ws-proxy專用的設定檔,命名為ws-proxy.service
放在/etc/systemd/system底下
[Unit] Description=WebSocket to Telnet Proxy Service # 確保網路啟動後才執行此服務 After=network.target [Service] # 設定執行此服務的使用者 (因為是BBS要用的所以就用BBS) User=bbs Group=bbs # 設定工作目錄 (程式所在的資料夾) WorkingDirectory=/home/bbs # 啟動指令 (請確認 python3 的絕對路徑,通常是 /usr/bin/python3) # -u 參數很重要,它代表 "Unbuffered",讓 Log 可以即時輸出 ExecStart=/usr/bin/python3 -u ws-proxy.py # 如果程式崩潰,自動重新啟動 Restart=always # 崩潰後等待 5 秒再重啟 RestartSec=5 # 設定環境變數 (可選) Environment=PYTHONUNBUFFERED=1 [Install] # 設定在多使用者模式下啟用 (標準伺服器模式) WantedBy=multi-user.target
然後呼叫
sudo systemctl enable ws-proxy
將這個服務打開
接著要看是直接reboot,還是用
sudo systemctl ws-proxy start
將服務打開都行
這樣就實作完成了,就這麼簡單
那麼使用這個的服務人數上限為幾人呢?
基本上就是視硬體規格而定了
Gemini回答以樹莓派1代來算
因為要處理SSL的加密解密會吃CPU不少效能,頂多30~50人
夠用了,自己架的小站是能有多少人啊
如果用後面3~5代硬體,應該是更沒有問題的
好,那稍微講一下rust的問題
rust其實因為它高效的特性,在樹莓派1底下跑更不是問題
而且甚至比python合適
問Gemini,它承載人數可達50~150人,明顯比用python跑的效能來得更高
但是,在樹莓派1上編譯實在是太耗時了
編譯一個4mb不到的ws-proxy程式,在樹莓1代上要3小時左右(release版)
然後也花空間,要下載快1.5GB的套件,還有在編譯時會產生240MB的中繼檔
當然如果選擇在其它機器編譯,再放過來是不用花那麼久啦
它的效能也真的比較好
不過用python直接寫個文件就可以跑來說,用python真的便捷快速多了
只是臨時架個小站玩玩,用python就好
要長期與大量使用時,才需要考慮用rust來跑
差不多都講完了
為了寫這篇能詳細一點,大概又重頭砍了BBS帳號又架了兩次
能夠在20年後又重架了一個BBS也是蠻有趣的一件事
而且再加上Websocket Secure的新(?)技術運行也算有點變化
只可惜那個2012年的MapleBBS itoc版沒辦法以全功能運行
就算註冊SYSOP有了站長權限,很奇怪的就是不能管理使用者與看板
後面重架甚至更慘,帳號登入後即刻斷線
不然以介面上來說這個東西比較舊,比較習慣說
有空再叫AI幫我找找,到底哪裡出問題吧





沒有留言:
張貼留言