1. 安裝GatewayWorker類庫
composer require workerman/workerman-for-win
composer require workerman/GatewayWorker-for-win
安裝完成後,目錄結構如下:
2. 編寫代碼
在application目錄下,創建一個應用模塊,模塊名稱爲gatewayapp,其目錄結構如下:
controller/GwEvents.php文件,該文件是業務邏輯處理類,實際開發中,只需要關注這個文件即可。
namespace app\gatewayapp\controller;
use GatewayWorker\Lib\Gateway;
/**
* 主邏輯
* 主要是處理 onConnect onMessage onClose 三個方法
* onConnect 和 onClose 如果不需要可以不用實現並刪除
*/
class GwEvents {
/**
* 當客戶端連接時觸發
* 如果業務不需此回調可以刪除onConnect *
* @param int $client_id 連接id
*/
public static function onConnect($client_id) {
// 向當前client_id發送數據
Gateway::sendToClient($client_id, sprintf('Hello %s',$client_id));
// 向所有人發送
Gateway::sendToAll(sprintf('用戶 %s 已登錄!',$client_id));
}
/**
* 當客戶端發來消息時觸發
* @param int $client_id 連接id
* @param mixed $message 具體消息
*/
public static function onMessage($client_id, $message) {
// 向所有人發送
Gateway::sendToAll(sprintf('用戶 %s 說:%s',$client_id,$message));
}
/**
* 當用戶斷開連接時觸發
* @param int $client_id 連接id
*/
public static function onClose($client_id) {
// 向所有人發送
GateWay::sendToAll(sprintf('用戶 %s 已退出!',$client_id));
}
}
controller/Index.php文件
namespace app\gatewayapp\controller;
use think\Controller;
class Index extends Controller {
public function index() {
return $this->fetch();
}
}
view/index/index.html視圖文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>WebSocket示例</title>
</head>
<body>
<script type="text/javascript">
var webSocket = null;
initSocket();
function initSocket() {
if (!"WebSocket" in window) {
console.log("您的瀏覽器不支持 WebSocket!");
return;
}
webSocket = new WebSocket("ws://"+document.domain+":8283");
webSocket.onopen = handleSend;
webSocket.onmessage = handleMessage;
webSocket.onclose = handleClose;
webSocket.onerror = handleError;
}
// 向服務器端發送數據
function handleSend() {
// Web Socket 已連接上,使用 send() 方法發送數據
testing();
}
// 處理服務器端發送過來的數據
function handleMessage(evt) {
var received_msg = evt.data;
console.log(received_msg);
}
// 處理連接關閉事件
function handleClose() {
console.log("連接已關閉...");
}
// 處理WebSocket錯誤
function handleError() {
console.log("WebSocketError!");
}
function testing() {
//每隔3秒鐘向服務器發送數據,此處僅用於測試
var items = ['張三','李四','小二黑','阿杜','單工','大理寺','花榮','劉備','諸葛亮'];
setInterval(function () {
var item = items[Math.floor(Math.random()*items.length)];
webSocket.send(item);
},3000);
}
</script>
</body>
</html>
run/start_register.php文件
use GatewayWorker\Register;
use Workerman\Worker;
$registerInstance = new GwRegister();
call_user_func_array(array($registerInstance,'index'),array());
class GwRegister {
public function __construct() {
require_once dirname(__FILE__) . '/../../../vendor/autoload.php';
include_once dirname(__FILE__).'/../const.php';
}
public function index() {
// register 必須是text協議
$registerAddress = sprintf('text://%s',GW_REGISTER_PROTOCOL);
$register = new Register($registerAddress);
// 如果不是在根目錄啓動,則運行runAll方法
if(!defined('GLOBAL_START')) {
Worker::runAll();
}
}
}
run/start_gateway.php文件
use Workerman\Worker;
use GatewayWorker\Gateway;
use Workerman\Autoloader;
$gatewayInstance = new GwGateway();
call_user_func_array(array($gatewayInstance,'index'),array());
class GwGateway {
private $gatewayAddress;
public function __construct() {
require_once dirname(__FILE__) . '/../../../vendor/autoload.php';
include_once dirname(__FILE__).'/../const.php';
// gateway 進程,這裏使用websocket協議
$this->gatewayAddress = sprintf('websocket://%s',GW_GATEWAY_ADDRESS);
}
public function index() {
$gateway = new Gateway($this->gatewayAddress);
// 設置名稱,方便status時查看
$gateway->name = GW_GATEWAY_NAME;
// 設置進程數,gateway進程數建議與cpu核數相同
$gateway->count = GW_GATEWAY_COUNT;
// 本機ip,分佈式部署時請設置成內網ip(非127.0.0.1)
$gateway->lanIp = GW_LOCAL_HOST_IP;
// 內部通訊起始端口,假如$gateway->count=4,起始端口爲4000
// 則一般會使用4000 4001 4002 4003 4個端口作爲內部通訊端口
$gateway->startPort = GW_GATEWAY_START_PORT;
// 服務註冊地址
$gateway->registerAddress = GW_REGISTER_ADDRESS;
// 心跳間隔,單位:秒,0 表示不發送心跳檢測
$gateway->pingInterval = GW_GATEWAY_PING_INTERVAL;
// 心跳數據
$gateway->pingData = '{"type":"ping"}';
/*
// 當客戶端連接上來時,設置連接的onWebSocketConnect,即在websocket握手時的回調
$gateway->onConnect = function($connection) {
$connection->onWebSocketConnect = function($connection , $http_header) {
// 可以在這裏判斷連接來源是否合法,不合法就關掉連接
// $_SERVER['HTTP_ORIGIN']標識來自哪個站點的頁面發起的websocket鏈接
if($_SERVER['HTTP_ORIGIN'] != 'http://kedou.workerman.net') {
$connection->close();
}
// onWebSocketConnect 裏面$_GET $_SERVER是可用的
// var_dump($_GET, $_SERVER);
};
};
*/
// 如果不是在根目錄啓動,則運行runAll方法
if(!defined('GLOBAL_START')) {
Worker::runAll();
}
}
}
run/start_businessworker.php文件
use Workerman\Worker;
use GatewayWorker\BusinessWorker;
use Workerman\Autoloader;
$businessInstance = new GwBusinessWorker();
call_user_func_array(array($businessInstance,'index'),array());
class GwBusinessWorker {
public function __construct() {
require_once dirname(__FILE__) . '/../../../vendor/autoload.php';
include_once dirname(__FILE__).'/../const.php';
}
public function index() {
// bussinessWorker 進程
$worker = new BusinessWorker();
// worker名稱
$worker->name = GW_WORKER_NAME;
// bussinessWorker進程數量
$worker->count = GW_BUSINESS_WORKER_COUNT;
// 服務註冊地址
$worker->registerAddress = GW_REGISTER_ADDRESS;
//設置處理業務的類,此處制定Events的命名空間
$worker->eventHandler = GW_BUSINESS_EVENT_HANDLER;
// 如果不是在根目錄啓動,則運行runAll方法
if(!defined('GLOBAL_START')) {
Worker::runAll();
}
}
}
const.php常量定義文件,根據需要,修改相關常量對應的值。
// 註冊協議
define('GW_REGISTER_PROTOCOL','0.0.0.0:1236');
// 註冊地址
define('GW_REGISTER_ADDRESS','127.0.0.1:1236');
// 網關地址
define('GW_GATEWAY_ADDRESS','0.0.0.0:8283');
// 網關起始端口
define('GW_GATEWAY_START_PORT','2900');
// 心跳檢測間隔,單位:秒,0 表示不發送心跳檢測
define('GW_GATEWAY_PING_INTERVAL',10);
// 本機ip,分佈式部署時請設置成內網ip(非127.0.0.1)
define('GW_LOCAL_HOST_IP','127.0.0.1');
// 網關名稱
define('GW_GATEWAY_NAME','Gateway001');
// worker進程名稱
define('GW_WORKER_NAME','BusinessWorker001');
// Gateway進程數量,建議與CPU核數相同
define('GW_GATEWAY_COUNT',2);
// BusinessWorker進程數量,建議設置爲CPU核數的1倍-3倍
define('GW_BUSINESS_WORKER_COUNT',6);
// Business業務處理類,可以帶命名空間
define('GW_BUSINESS_EVENT_HANDLER','app\gatewayapp\controller\GwEvents');
start_for_win.bat啓動文件
php run\start_register.php run\start_gateway.php run\start_businessworker.php
pause
3. 運行
雙擊start_for_win.bat文件,啓動websocket服務,啓動成功後的界面如下:
打開瀏覽器,定位到視圖url,可以多開幾個瀏覽器,然後切換到任何一個瀏覽器的控制檯,效果如下:
GatewayWorker與ThinkPHP3的整合也已實現,有需要的請在留言中註明。