效果
命令如下:
php index.php 可以查看到上图
php index.php start 开启服务(Debug模式)
php index.php start -d 开启服务(Daemon模式)
php index.php status 查看服务状态
php index.php reload 服务热加载
php index.php stop 关闭服务
index.php 这是文件名称,大家叫什么都可以。
目录结构如下:
├─ controller
│ ├── ...
├─ client
│ ├─ websocket
│ ├── ...
│ ├─ tcp
│ ├── ...
├─ server
│ ├─ config
│ ├── config.php
│ ├─ core
│ ├── Common.php
│ ├── Core.php
│ ├── HandlerException.php
│ ├─ log -- 需要 读/写 权限
│ ├── ...
├─ index.php
目前就这几个文件,后期研究新的知识点会直接集成到这里面。
说说实现了什么:
1、启动了 WebSocket、HTTP、TCP、UDP 等服务。
2、WebSocket 例子,在 client/websocket 文件夹,实现了视频弹幕。
3、HTTP 例子,在浏览器直接访问:http://ip:port,逻辑代码在 controller 文件夹。
4、TCP 例子,在 client/tcp 文件夹。
5、UDP 例子,直接运行 netcat-u ip port 即可。
6、相关配置,在 server/config 文件夹。
代码
这里我还准备了一分学习图和资料,如下:
链接:https://pan.baidu.com/s/1v5gm7n0L7TGyejCmQrMh2g 提取码:x2p5
免费分享,但是X度限制严重,如若链接失效点击链接或搜索加群 群号518475424。
放不全,就放一个主要的文件吧(Core.php)。
<?php if (!defined('SERVER_PATH')) exit("No Access"); class Core { private static $serv; public function __construct() { set_error_handler(['HandlerException', 'appError']); register_shutdown_function(['HandlerException', 'fatalError']); } public static function run() { static::checkCli(); static::checkExtension(); static::showUsageUI(); static::parseCommand(); } protected static function checkCli() { if (php_sapi_name() !== 'cli') { exit(output('服务只能运行在 cli sapi 模式下')); } } protected static function checkExtension() { if (!extension_loaded('swoole')) { exit(output('请安装 swoole 扩展')); } } protected static function showUsageUI() { global $argc; if ($argc <= 1 || $argc >3) { echo PHP_EOL; echo "----------------------------------------".PHP_EOL; echo "| Swoole |".PHP_EOL; echo "|--------------------------------------|".PHP_EOL; echo '| USAGE: php index.php commond |'.PHP_EOL; echo '|--------------------------------------|'.PHP_EOL; echo '| 1. start 以debug模式开启服务 |'.PHP_EOL; echo '| 2. start -d 以daemon模式开启服务 |'.PHP_EOL; echo '| 3. status 查看服务状态 |'.PHP_EOL; echo '| 4. reload 热加载 |'.PHP_EOL; echo '| 5. stop 关闭服务 |'.PHP_EOL; echo "----------------------------------------".PHP_EOL; echo PHP_EOL; exit; } } protected static function parseCommand() { global $argv; $command = $argv[1]; $option = isset( $argv[2] ) ? $argv[2] : '' ; switch ($command) { case 'start': if ($option === '-d') { //以daemon形式启动 get_config(['set@daemonize' => true]); } self::workerStart(); break; case 'status': self::workerStatus(); break; case 'reload': self::workerReload(); break; case 'stop': self::workerStop(); break; default: echo "Bad Command.".PHP_EOL; } } protected static function workerStart() { $config = get_config(); self::$serv = new swoole_websocket_server($config['ip'], $config['websocket_port']); self::$serv->set($config['set']); self::$serv->on('Start', function ($serv) use ($config) { $start = new OnStart(); $start::run($serv, $config); }); self::$serv->on('ManagerStart', function ($serv) use ($config) { $manager_start = new OnManagerStart(); $manager_start::run($serv, $config); }); self::$serv->on('WorkerStart', function ($serv, $worker_id) use ($config) { $worker_start = new OnWorkerStart(); $worker_start::run($serv, $worker_id, $config); }); //TCP $tcp = self::$serv->listen($config['ip'], $config['tcp_port'], SWOOLE_SOCK_TCP); $tcp->set($config['tcp_set']); $tcp->on('Receive', function ($serv, $fd, $reactor_id, $data) { $receive = new OnReceive(); $receive::run($serv, $fd, $reactor_id, $data); }); //UDP $udp = self::$serv->listen($config['ip'], $config['udp_port'], SWOOLE_SOCK_UDP); $udp->set($config['udp_set']); $udp->on('Packet', function ($serv, $data, $client_info) { $packet = new OnPacket(); $packet::run($serv, $data, $client_info); }); self::$serv->on('Task', function ($serv, $task_id, $src_worker_id, $data) use ($config) { $task = new OnTask(); $dataArr = json_decode($data, true); switch ($dataArr['server']) { case "tcp": $task::tcp_task_run($serv, $task_id, $src_worker_id, $data); break; case "ws": $task::ws_task_run($serv, $task_id, $src_worker_id, $data); break; } }); self::$serv->on('Open', function ($serv, $request) { echo output("onOpen: handshake success with fd={$request->fd}"); }); self::$serv->on('Message', function ($serv, $frame) { $message = new OnMessage(); $message::run($serv, $frame); }); self::$serv->on('Request', function ($request, $response) { $req = new OnRequest(); $req::run($request, $response); }); self::$serv->on('Finish', function ($serv, $task_id, $data) { $finish = new OnFinish(); $finish::run($serv, $task_id, $data); }); self::$serv->on('Close', function ($serv, $fd, $reactor_id){ try { echo output('客户端关闭'); } catch(Exception $e) { } }); self::$serv->on('Shutdown', function ($serv) { echo output("服务关闭"); }); self::showProcessUI(); self::$serv->start(); } protected static function workerStatus() { $config = get_config(); if (!file_exists($config['master_pid_file']) || !file_exists($config['manager_pid_file']) || !file_exists($config['worker_pid_file']) ) { echo output("暂无启动的服务"); return false; } self::showProcessUI($config); $masterPidString = trim(@file_get_contents($config['master_pid_file'])); $masterPidArr = explode( '-', $masterPidString); echo str_pad("Master", 18, ' ', STR_PAD_BOTH ). str_pad($config['master_process_name'], 26, ' ', STR_PAD_BOTH ). str_pad($masterPidArr[0], 16, ' ', STR_PAD_BOTH ). str_pad($masterPidArr[1], 16, ' ', STR_PAD_BOTH ). str_pad($masterPidArr[2], 16, ' ', STR_PAD_BOTH ).PHP_EOL; $managerPidString = trim(@file_get_contents($config['manager_pid_file'])); $managerPidArr = explode( '-', $managerPidString); echo str_pad("Manager", 20, ' ', STR_PAD_BOTH ). str_pad($config['manager_process_name'], 24, ' ', STR_PAD_BOTH ). str_pad($managerPidArr[0], 16, ' ', STR_PAD_BOTH ). str_pad($managerPidArr[1], 16, ' ', STR_PAD_BOTH ). str_pad($managerPidArr[2], 16, ' ', STR_PAD_BOTH ).PHP_EOL; $workerPidString = rtrim(@file_get_contents($config['worker_pid_file']), '|' ); $workerPidArr = explode( '|', $workerPidString ); if (isset($workerPidArr) && !empty($workerPidArr)) { foreach ($workerPidArr as $key => $val) { $v = explode( '-', $val); echo str_pad("Worker", 18, ' ', STR_PAD_BOTH ). str_pad($config['worker_process_name'], 26, ' ', STR_PAD_BOTH ). str_pad($v[0], 16, ' ', STR_PAD_BOTH ). str_pad($v[1], 16, ' ', STR_PAD_BOTH ). str_pad($v[2], 16, ' ', STR_PAD_BOTH ).PHP_EOL; } } $taskPidString = rtrim(@file_get_contents($config['task_pid_file']), '|' ); $taskPidArr = explode( '|', $taskPidString ); if (isset($taskPidArr) && !empty($taskPidArr)) { foreach ($taskPidArr as $key => $val) { $v = explode( '-', $val); echo str_pad("Task", 18, ' ', STR_PAD_BOTH ). str_pad($config['task_process_name'], 24, ' ', STR_PAD_BOTH ). str_pad($v[0], 20, ' ', STR_PAD_BOTH ). str_pad($v[1], 12, ' ', STR_PAD_BOTH ). str_pad($v[2], 20, ' ', STR_PAD_BOTH ).PHP_EOL; } } } protected static function workerReload() { $config = get_config(); if (!file_exists($config['master_pid_file'])) { echo output("暂无启动的服务"); return false; } $masterPidString = trim(file_get_contents($config['master_pid_file'])); $masterPidArr = explode( '-', $masterPidString); if (!swoole_process::kill($masterPidArr[0], 0)) { echo output("PID:{$masterPidArr[0]} 不存在"); return false; } swoole_process::kill($masterPidArr[0], SIGUSR1); @unlink($config['worker_pid_file']); @unlink($config['task_pid_file']); echo output("热加载成功"); return true; } protected static function workerStop() { $config = get_config(); if (!file_exists($config['master_pid_file'])) { echo output("暂无启动的服务"); return false; } $masterPidString = trim(file_get_contents($config['master_pid_file'])); $masterPidArr = explode( '-', $masterPidString); if (!swoole_process::kill($masterPidArr[0], 0)) { echo output("PID:{$masterPidArr[0]} 不存在"); return false; } swoole_process::kill($masterPidArr[0]); $time = time(); while (true) { usleep(2000); if (!swoole_process::kill($masterPidArr[0], 0)) { unlink($config['master_pid_file']); unlink($config['manager_pid_file']); unlink($config['worker_pid_file']); unlink($config['task_pid_file']); echo output("服务关闭成功"); break; } else { if (time() - $time > 5) { echo output("服务关闭失败,请重试"); break; } } } return true; } protected static function showProcessUI() { $config = get_config(); if ($config['set']['daemonize'] == true) { return false; } echo str_pad("-", 90, '-', STR_PAD_BOTH) . PHP_EOL; echo "|" . str_pad("启动/关闭", 92, ' ', STR_PAD_BOTH) . "|" . PHP_EOL; echo str_pad("-", 90, '-', STR_PAD_BOTH) . PHP_EOL; echo str_pad("Start success.", 50, ' ', STR_PAD_BOTH) . str_pad("php index.php stop", 50, ' ', STR_PAD_BOTH) . PHP_EOL; echo str_pad("-", 90, '-', STR_PAD_BOTH) . PHP_EOL; echo "|" . str_pad("版本信息", 92, ' ', STR_PAD_BOTH) . "|" . PHP_EOL; echo str_pad("-", 90, '-', STR_PAD_BOTH) . PHP_EOL; echo str_pad("Swoole Version:" . SWOOLE_VERSION, 50, ' ', STR_PAD_BOTH) . str_pad("PHP Version:" . PHP_VERSION, 50, ' ', STR_PAD_BOTH) . PHP_EOL; echo str_pad("-", 90, '-', STR_PAD_BOTH) . PHP_EOL; echo "|" . str_pad("IP 信息", 90, ' ', STR_PAD_BOTH) . "|" . PHP_EOL; echo str_pad("-", 90, '-', STR_PAD_BOTH) . PHP_EOL; echo str_pad("IP:" . $config['ip'], 50, ' ', STR_PAD_BOTH) . str_pad("PORT:" . $config['websocket_port'], 50, ' ', STR_PAD_BOTH) . PHP_EOL; echo str_pad("-", 90, '-', STR_PAD_BOTH) . PHP_EOL; echo "|" . str_pad("进程信息", 92, ' ', STR_PAD_BOTH) . "|" . PHP_EOL; echo str_pad("-", 90, '-', STR_PAD_BOTH) . PHP_EOL; echo str_pad("Swoole进程", 20, ' ', STR_PAD_BOTH) . str_pad('进程别名', 30, ' ', STR_PAD_BOTH) . str_pad('进程ID', 18, ' ', STR_PAD_BOTH) . str_pad('父进程ID', 18, ' ', STR_PAD_BOTH) . str_pad('用户', 18, ' ', STR_PAD_BOTH) . PHP_EOL; } protected static function signalHandler() { //TODO 未完成 //swoole_process::signal(SIGINT, function ($signal) { // echo $signal; // return; //}); } }