thinkphp5+GatewayWorker+Workerman聊天室,可以多人聊天,指定某个人进行聊天,还可以切换聊天房间
thinkphp5+GatewayWorker+Workerman聊天室,可以多人聊天,指定某个人进行聊天,还可以切换聊天房间Windows版安装a) 安装thinkphp5;composercreate-projecttopthink/thinktp5--prefer-dist复制代码b) 进入tp5的目录,安装Windows版本的workerman;compos...
thinkphp5+GatewayWorker+Workerman聊天室,可以多人聊天,指定某个人进行聊天,还可以切换聊天房间
Windows版安装
a) 安装thinkphp5;
composer create-project topthink/think tp5 --prefer-dist
复制代码
b) 进入tp5的目录,安装Windows版本的workerman;
composer require workerman/workerman-for-win
复制代码
c} 安装Windows版本的gateway;
composer require workerman/gateway-worker-for-win
复制代码
开始关键部分,服务端实现
控制器 控制器:app\index\controller\Sregister
<?phpnamespace app\index\controller;use Workerman\Worker;use GatewayWorker\Register;class Sregister{public function __construct(){// register 服务必须是text协议$register = new Register('text://0.0.0.0:1236');// 如果不是在根目录启动,则运行runAll方法if(!defined('GLOBAL_START')){Worker::runAll();}}}
复制代码
控制器:app\index\controller\Sgateway
<?phpnamespace app\index\controller;use Workerman\Worker;use GatewayWorker\Gateway;use Workerman\Autoloader;class Sgateway{public function __construct(){// gateway 进程$gateway = new Gateway("Websocket://0.0.0.0:7272");// 设置名称,方便status时查看$gateway->name = 'ChatGateway';// 设置进程数,gateway进程数建议与cpu核数相同$gateway->count = 4;// 分布式部署时请设置成内网ip(非127.0.0.1)$gateway->lanIp = '127.0.0.1';// 内部通讯起始端口,假如$gateway->count=4,起始端口为4000// 则一般会使用4000 4001 4002 4003 4个端口作为内部通讯端口$gateway->startPort = 2300;// 心跳间隔$gateway->pingInterval = 10;// 心跳数据$gateway->pingData = '{"type":"ping"}';// 服务注册地址$gateway->registerAddress = '127.0.0.1:1236';/*// 当客户端连接上来时,设置连接的onWebSocketConnect,即在websocket握手时的回调$gateway->onConnect = function($connection){$connection->onWebSocketConnect = function($connection , $http_header){// 可以在这里判断连接来源是否合法,不合法就关掉连接// $_SERVER['HTTP_ORIGIN']标识来自哪个站点的页面发起的websocket链接if($_SERVER['HTTP_ORIGIN'] != 'http://chat.workerman.net'){$connection->close();}// onWebSocketConnect 里面$_GET $_SERVER是可用的// var_dump($_GET, $_SERVER);};};*/// 如果不是在根目录启动,则运行runAll方法if(!defined('GLOBAL_START')){Worker::runAll();}}}
复制代码
控制器:app\index\controller\Sbusinessworker
<?phpnamespace app\index\controller;use Workerman\Worker;use GatewayWorker\BusinessWorker;use Workerman\Autoloader;class Sbusinessworker{public function __construct(){// bussinessWorker 进程$worker = new BusinessWorker();// worker名称$worker->name = 'ChatBusinessWorker';// bussinessWorker进程数量$worker->count = 4;// 服务注册地址$worker->registerAddress = '127.0.0.1:1236';//设置处理业务的类,此处制定Events的命名空间$worker->eventHandler = 'app\index\controller\Events';// 如果不是在根目录启动,则运行runAll方法if(!defined('GLOBAL_START')){Worker::runAll();}}}
复制代码
控制器:app\index\controller\Events
<?phpnamespace app\index\controller;/*** 用于检测业务代码死循环或者长时间阻塞等问题* 如果发现业务卡死,可以将下面declare打开(去掉//注释),并执行php start.php reload* 然后观察一段时间workerman.log看是否有process_timeout异常*///declare(ticks=1);/*** 聊天主逻辑* 主要是处理 onMessage onClose*/use \GatewayWorker\Lib\Gateway;class Events{/*** 有消息时* @param int $client_id* @param mixed $message*/public static function onMessage($client_id, $message){// debugecho "client:{$_SERVER['REMOTE_ADDR']}:{$_SERVER['REMOTE_PORT']} gateway:{$_SERVER['GATEWAY_ADDR']}:{$_SERVER['GATEWAY_PORT']} client_id:$client_id session:".json_encode($_SESSION)." onMessage:".$message."\n";// 客户端传递的是json数据$message_data = json_decode($message, true);if(!$message_data){return ;}// 根据类型执行不同的业务switch($message_data['type']){// 客户端回应服务端的心跳case 'pong':return;// 客户端登录 message格式: {type:login, name:xx, room_id:1} ,添加到客户端,广播给所有客户端xx进入聊天室case 'login':// 判断是否有房间号if(!isset($message_data['room_id'])){throw new \Exception("\$message_data['room_id'] not set. client_ip:{$_SERVER['REMOTE_ADDR']} \$message:$message");}// 把房间号昵称放到session中$room_id = $message_data['room_id'];$client_name = htmlspecialchars($message_data['client_name']);$_SESSION['room_id'] = $room_id;$_SESSION['client_name'] = $client_name;// 获取房间内所有用户列表$clients_list = Gateway::getClientSessionsByGroup($room_id);foreach($clients_list as $tmp_client_id=>$item){$clients_list[$tmp_client_id] = $item['client_name'];}$clients_list[$client_id] = $client_name;// 转播给当前房间的所有客户端,xx进入聊天室 message {type:login, client_id:xx, name:xx}$new_message = array('type'=>$message_data['type'], 'client_id'=>$client_id, 'client_name'=>htmlspecialchars($client_name), 'time'=>date('Y-m-d H:i:s'));Gateway::sendToGroup($room_id, json_encode($new_message));Gateway::joinGroup($client_id, $room_id);// 给当前用户发送用户列表$new_message['client_list'] = $clients_list;Gateway::sendToCurrentClient(json_encode($new_message));return;// 客户端发言 message: {type:say, to_client_id:xx, content:xx}case 'say':// 非法请求if(!isset($_SESSION['room_id'])){throw new \Exception("\$_SESSION['room_id'] not set. client_ip:{$_SERVER['REMOTE_ADDR']}");}$room_id = $_SESSION['room_id'];$client_name = $_SESSION['client_name'];// 私聊if($message_data['to_client_id'] != 'all'){$new_message = array('type'=>'say','from_client_id'=>$client_id,'from_client_name' =>$client_name,'to_client_id'=>$message_data['to_client_id'],'content'=>"<b>对你说: </b>".nl2br(htmlspecialchars($message_data['content'])),'time'=>date('Y-m-d H:i:s'),);Gateway::sendToClient($message_data['to_client_id'], json_encode($new_message));$new_message['content'] = "<b>你对".htmlspecialchars($message_data['to_client_name'])."说: </b>".nl2br(htmlspecialchars($message_data['content']));return Gateway::sendToCurrentClient(json_encode($new_message));}$new_message = array('type'=>'say','from_client_id'=>$client_id,'from_client_name' =>$client_name,'to_client_id'=>'all','content'=>nl2br(htmlspecialchars($message_data['content'])),'time'=>date('Y-m-d H:i:s'),);return Gateway::sendToGroup($room_id ,json_encode($new_message));}}/*** 当客户端断开连接时* @param integer $client_id 客户端id*/public static function onClose($client_id){// debugecho "client:{$_SERVER['REMOTE_ADDR']}:{$_SERVER['REMOTE_PORT']} gateway:{$_SERVER['GATEWAY_ADDR']}:{$_SERVER['GATEWAY_PORT']} client_id:$client_id onClose:''\n";// 从房间的客户端列表中删除if(isset($_SESSION['room_id'])){$room_id = $_SESSION['room_id'];$new_message = array('type'=>'logout', 'from_client_id'=>$client_id, 'from_client_name'=>$_SESSION['client_name'], 'time'=>date('Y-m-d H:i:s'));Gateway::sendToGroup($room_id, json_encode($new_message));}}}
复制代码
代码目录截图
然后在项目根目录 新增入口文件 start_register.php 、start_gateway.php 、start_businessworker.php三个入口文件
文件:start_register.php
<?phpdefine('APP_PATH', __DIR__ . '/application/');define('BIND_MODULE','index/Sregister');// 加载框架引导文件require __DIR__ . '/thinkphp/start.php';
复制代码
文件:start_gateway.php
<?phpdefine('APP_PATH', __DIR__ . '/application/');define('BIND_MODULE','index/Sgateway');// 加载框架引导文件require __DIR__ . '/thinkphp/start.php';
复制代码
文件:start_businessworker.php
<?phpdefine('APP_PATH', __DIR__ . '/application/');define('BIND_MODULE','index/Sbusinessworker');// 加载框架引导文件require __DIR__ . '/thinkphp/start.php';
复制代码
[b]由于PHP-CLI在windows系统无法实现多进程以及守护进程,所以只能把三个文件放到bat文件,然后双击启动
bat文件:start_for_win.bat
php start_register.php start_gateway.php start_businessworker.phppause
复制代码
代码目录截图
启动程序
在项目的根目录下双击启动 start_for_win.bat
服务端实现
view : tp5\application\index\view\index.html
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"><title>workerman-chat PHP聊天室 Websocket(HTLM5/Flash)+PHP多进程socket实时推送技术</title><script type="text/javascript">//WebSocket = null;</script><link href="__PUBLIC__/chat/css/bootstrap.min.css" rel="stylesheet"><link href="__PUBLIC__/chat/css/style.css" rel="stylesheet"><!-- Include these three JS files: --><script type="text/javascript" src="__PUBLIC__/chat/js/swfobject.js"></script><script type="text/javascript" src="__PUBLIC__/chat/js/web_socket.js"></script><script type="text/javascript" src="__PUBLIC__/chat/js/jquery.min.js"></script><script type="text/javascript">if (typeof console == "undefined") { this.console = { log: function (msg) { } };}// 如果浏览器不支持websocket,会使用这个flash自动模拟websocket协议,此过程对开发者透明WEB_SOCKET_SWF_LOCATION = "__PUBLIC__/chat/swf/WebSocketMain.swf";// 开启flash的websocket debugWEB_SOCKET_DEBUG = true;var ws, name, client_list={};// 连接服务端function connect() {// 创建websocketws = new WebSocket("ws://"+document.domain+":7272");// 当socket连接打开时,输入用户名ws.onopen = onopen;// 当有消息时根据消息类型显示不同信息ws.onmessage = onmessage;ws.onclose = function() {console.log("连接关闭,定时重连");connect();};ws.onerror = function() {console.log("出现错误");};}// 连接建立时发送登录信息function onopen(){if(!name){show_prompt();}// 登录var login_data = '{"type":"login","client_name":"'+name.replace(/"/g, '\\"')+'","room_id":"<?php echo isset($_GET['room_id']) ? $_GET['room_id'] : 1?>"}';console.log("websocket握手成功,发送登录数据:"+login_data);ws.send(login_data);}// 服务端发来消息时function onmessage(e){console.log(e.data);var data = eval("("+e.data+")");switch(data['type']){// 服务端ping客户端case 'ping':ws.send('{"type":"pong"}');break;;// 登录 更新用户列表case 'login'://{"type":"login","client_id":xxx,"client_name":"xxx","client_list":"[...]","time":"xxx"}say(data['client_id'], data['client_name'], data['client_name']+' 加入了聊天室', data['time']);if(data['client_list']){client_list = data['client_list'];}else{client_list[data['client_id']] = data['client_name'];}flush_client_list();console.log(data['client_name']+"登录成功");break;// 发言case 'say'://{"type":"say","from_client_id":xxx,"to_client_id":"all/client_id","content":"xxx","time":"xxx"}say(data['from_client_id'], data['from_client_name'], data['content'], data['time']);break;// 用户退出 更新用户列表case 'logout'://{"type":"logout","client_id":xxx,"time":"xxx"}say(data['from_client_id'], data['from_client_name'], data['from_client_name']+' 退出了', data['time']);delete client_list[data['from_client_id']];flush_client_list();}}// 输入姓名function show_prompt(){name = prompt('输入你的名字:', '');if(!name || name=='null'){name = '游客';}}// 提交对话function onSubmit() {var input = document.getElementById("textarea");var to_client_id = $("#client_list option:selected").attr("value");var to_client_name = $("#client_list option:selected").text();ws.send('{"type":"say","to_client_id":"'+to_client_id+'","to_client_name":"'+to_client_name+'","content":"'+input.value.replace(/"/g, '\\"').replace(/\n/g,'\\n').replace(/\r/g, '\\r')+'"}');input.value = "";input.focus();}// 刷新用户列表框function flush_client_list(){var userlist_window = $("#userlist");var client_list_slelect = $("#client_list");userlist_window.empty();client_list_slelect.empty();userlist_window.append('<h4>在线用户</h4><ul>');client_list_slelect.append('<option value="all" id="cli_all">所有人</option>');for(var p in client_list){userlist_window.append('<li id="'+p+'">'+client_list[p]+'</li>');client_list_slelect.append('<option value="'+p+'">'+client_list[p]+'</option>');}$("#client_list").val(select_client_id);userlist_window.append('</ul>');}// 发言function say(from_client_id, from_client_name, content, time){$("#dialog").append('<div class="speech_item"><img src="http://lorempixel.com/38/38/?'+from_client_id+'" class="user_icon" /> '+from_client_name+' <br> '+time+'<div style="clear:both;"></div><p class="triangle-isosceles top">'+content+'</p> </div>');}$(function(){select_client_id = 'all';$("#client_list").change(function(){select_client_id = $("#client_list option:selected").attr("value");});});</script></head><body onload="connect();"><div class="container"><div class="row clearfix"><div class="col-md-1 column"></div><div class="col-md-6 column"><div class="thumbnail"><div class="caption" id="dialog"></div></div><form onsubmit="onSubmit(); return false;"><select style="margin-bottom:8px" id="client_list"><option value="all">所有人</option></select><textarea class="textarea thumbnail" id="textarea"></textarea><div class="say-btn"><input type="submit" class="btn btn-default" value="发表" /></div></form><div><b>房间列表:</b>(当前在 房间<?php echo isset($_GET['room_id'])&&intval($_GET['room_id'])>0 ? intval($_GET['room_id']):1; ?>)<br><a href="/?room_id=1">房间1</a> <a href="/?room_id=2">房间2</a> <a href="/?room_id=3">房间3</a> <a href="/?room_id=4">房间4</a><br><br></div><p class="cp">PHP多进程+Websocket(HTML5/Flash)+PHP Socket实时推送技术 Powered by <a href="http://www.workerman.net/workerman-chat" target="_blank">workerman-chat</a></p></div><div class="col-md-3 column"><div class="thumbnail"><div class="caption" id="userlist"></div></div></div></div></div><script type="text/javascript">var _bdhmProtocol = (("https:" == document.location.protocol) ? " https://" : " http://");document.write(unescape("%3Cscript src='" + _bdhmProtocol + "hm.baidu.com/h.js%3F7b1919221e89d2aa5711e4deb935debd' type='text/javascript'%3E%3C/script%3E"));</script></body></html>
复制代码
运行结果截图
linux版安装
a) 安装thinkphp5;
composer create-project topthink/think tp5 --prefer-dist
复制代码
b) 进入tp5的目录,安装linux版本的workerman;
composer require topthink/think-worker
复制代码
c} 安装linux版本的gateway;
composer require workerman/gateway-worker-for-win
复制代码
关键部分,服务端实现
复制代码
控制器 app\index\controller\Gate
<?php/*** linux workerman例子测试* 需要在Linux系统控制台进行启动,启动文件位于根目录的start.php文件中* Windows无法进行同时启动多个协议* 由于PHP-CLI在windows系统无法实现多进程以及守护进程,所以windows版本Workerman建议仅作开发调试使用。*/namespace app\index\controller;use Workerman\Worker;use GatewayWorker\Gateway;use GatewayWorker\Register;use GatewayWorker\BusinessWorker;class Gate{/*** 构造函数* @access public*/public function __construct(){//初始化各个GatewayWorker//初始化register register 服务必须是text协议$register = new Register('text://0.0.0.0:1236');//初始化 bussinessWorker 进程$worker = new BusinessWorker();// worker名称$worker->name = 'ChatBusinessWorker';// bussinessWorker进程数量$worker->count = 4;// 服务注册地址$worker->registerAddress = '127.0.0.1:1236';//设置处理业务的类,此处制定Events的命名空间$worker->eventHandler = 'app\index\controller\Events';// 初始化 gateway 进程$gateway = new Gateway("websocket://0.0.0.0:7272");// 设置名称,方便status时查看$gateway->name = 'ChatGateway';$gateway->count = 4;// 分布式部署时请设置成内网ip(非127.0.0.1)$gateway->lanIp = '127.0.0.1';// 内部通讯起始端口,假如$gateway->count=4,起始端口为4000// 则一般会使用4000 4001 4002 4003 4个端口作为内部通讯端口$gateway->startPort = 2300;// 心跳间隔$gateway->pingInterval = 10;// 心跳数据$gateway->pingData = '{"type":"ping"}';// 服务注册地址$gateway->registerAddress = '127.0.0.1:1236';//运行所有Worker;Worker::runAll();}}
复制代码
入口文件
文件: start.php
<?php/*** workerman + GatewayWorker* 此文件只能在Linux运行* run with command* php start.php start*/ini_set('display_errors', 'on');if(strpos(strtolower(PHP_OS), 'win') === 0){exit("start.php not support windows.\n");}//检查扩展if(!extension_loaded('pcntl')){exit("Please install pcntl extension. See http://doc3.workerman.net/appendices/install-extension.html\n");}if(!extension_loaded('posix')){exit("Please install posix extension. See http://doc3.workerman.net/appendices/install-extension.html\n");}define('APP_PATH', __DIR__ . '/application/');define('BIND_MODULE','chat/Gate');// 加载框架引导文件require __DIR__ . '/thinkphp/start.php';
复制代码
启动程序
php start.php start
复制代码
客户端跟Windows一样就可以了
workerman官网:http://www.workerman.net/
workerman文档:http://doc3.workerman.net/
GatewayWorker文档:http://doc3.workerman.net/
tp5.zip ( 837.52 KB 下载:905 次 )
更多推荐




所有评论(0)