基於Web實現遠端與硬體互動
專案背景:
最近在做一個類似“速遞櫃”的專案,專案需求大概如下:
1.使用者使用微信掃描二維碼
2.後臺匹配相關資料,並儲存使用者資訊,最終返回給使用者當前櫃子當前狀態
3.使用者選擇“開櫃”指令,進行存取物品
需求分析
1.實現跟硬體進行資料互動,使用TCP/IP 長連線,意味著,我們要有一個TCP伺服器,TCP伺服器一方面接受使用者發過來的相關請求指令,另一方面完成傳送指令給指定硬體這一操作。
2.除了TCP 伺服器我們還需要一個 Websocket 伺服器,也有兩個功能,一方面接受客戶端傳送的指令,另一方面處理轉發使用者指令給TCP伺服器。
功能實現
TCP 伺服器,Websocket 伺服器 使用相對成熟的 Workman 實現。
Workerman是一款純PHP開發的開源高效能的PHP socket 伺服器框架。被廣泛的用於手機app、移動通訊,微信小程式,手遊服務端、網路遊戲、PHP聊天室、硬體通訊、智慧家居、車聯網、物聯網等領域的開發。 支援TCP長連線,支援Websocket、HTTP等協議,支援自定義協議。擁有非同步Mysql、非同步Redis、非同步Http、非同步訊息佇列等眾多高效能元件。
GatewayWorker 目錄結構
├── Applications // 這裡是所有開發者應用專案
│ └── YourApp // 其中一個專案目錄,目錄名可以自定義
│ ├── Events.php // 開發者只需要關注這個檔案
│ ├── start_gateway.php // gateway程式啟動指令碼,包括埠號等設定
│ ├── start_businessworker.php // businessWorker程式啟動指令碼
│ └── start_register.php // 註冊服務啟動指令碼
│
├── start.php // 全域性啟動指令碼,此指令碼會依次載入Applications/專案/start_*.php啟動指令碼
│
└── vendor // GatewayWorker框架和Workerman框架原始碼目錄,此目錄開發者不用關心
- 首先我們
Applications
複製兩份YourApp
目錄下的檔案,此時我們的目錄結構應該是這樣的
├── Applications // 這裡是所有開發者應用專案
│ └── TcpServer // 其中一個專案目錄,目錄名可以自定義
│ ├── Events.php // 開發者只需要關注這個檔案
│ ├── start_gateway.php // gateway程式啟動指令碼,包括埠號等設定
│ ├── start_businessworker.php // businessWorker程式啟動指令碼
│ └── start_register.php // 註冊服務啟動指令碼
│ └── WSServer
│ ├── Events.php
│ ├── start_gateway.php
│ ├── start_businessworker.php
│ └── start_register.php
│
├── start.php // 全域性啟動指令碼,此指令碼會依次載入Applications/專案/start_*.php啟動指令碼
│
└── vendor // GatewayWorker框架和Workerman框架原始碼目錄,此目錄開發者不用關心
- 修改以及配置
TCPServer
,start_register.php
下我們不做任何修改,但需要注意的是register 必須是text協議
// register 必須是text協議
$register = new Register(`text://0.0.0.0:1238`);
2.修改 start_gateway.php
// gateway 程式,這裡使用 TCP 協議,可以用telnet測試
$gateway = new Gateway("tcp://0.0.0.0:8282");
// gateway名稱,status方便檢視
$gateway->name = `TcpGateway`;
// gateway程式數
$gateway->count = 4;
// 本機ip,分散式部署時使用內網ip
$gateway->lanIp = `127.0.0.1`;
// 內部通訊起始埠,假如$gateway->count=4,起始埠為4000
// 則一般會使用4000 4001 4002 4003 4個埠作為內部通訊埠
$gateway->startPort = 2900;
// 服務註冊地址
$gateway->registerAddress = `127.0.0.1:1238`;
3.修改 start_businessworker.php
// bussinessWorker 程式
$worker = new BusinessWorker();
// worker名稱
$worker->name = `TcpBusinessWorker`;
// bussinessWorker程式數量
$worker->count = 4;
// 服務註冊地址
$worker->registerAddress = `127.0.0.1:1238`;
4.根據上面的操作,我們修改WSServer
目錄下的檔案,這個修改跟上面一樣的,但是要注意的是,兩個應用應該是不用的埠,所以需要修改的地方有兩個,start_gateway
下的埠,還有start_register
埠。!!!! 兩個應用下register 和 Gateway 埠一定不一致!!!
好了,到目前為止,我們已經完成了兩個伺服器的建立,那麼我們兩個伺服器之間如何資料共享呢?答案很簡單,我們在ws服務下建立一個tcp 客戶端即可,讓tcp 客戶端與我們tcp 伺服器連線就可以,然後通過tcp 客戶端直接傳送相關資料給tcp伺服器就可以,當然並不是所有資料都是通過這樣的方式共享的,我們其他資訊還是通過共用一個資料庫來共享的。相關程式碼如下:
public static function onConnect($client_id)
{
// 向所有人傳送
$host = "127.0.0.1";
$port = 8282;
global $socket;
if (empty($socket)) {
$socket = TcpClient::getInstace()->connect($host, $port);
Gateway::sendToClient($client_id, "$client_id welcome
");
}
}
public static function onMessage($client_id, $message)
{
if ($GLOBALS[`socket`]) {
// 資料指令解包
$message_arr = str_split(str_replace(` `, ``, trim($message)), 2);
$cmd=null;
for ($j = 0; $j < count($message_arr); $j++) {
//socket_write($GLOBALS[`socket`], bin2hex($message_arr[$j]));
socket_write($GLOBALS[`socket`],chr(hexdec($message_arr[$j])));
}
}
}
好了,截止目前我們就完成了服務端工作,當然客戶端只是簡單的建立一個ws連線就好。