配置过程中遇到的问题及说明:
1、首先需要对长连接相关概念有一定了解。
2、通过composer默认安装的workman版本不对,报错,使用命令composer require topthink/think-worker=1.0.1
3、通过1、发现composer包列表网址,https://packagist.org,可以通过该网址查找composer包,包括说明。
4、报错:stream_socket_server() has been disabled for security reasons … ,修改php.ini,disable_functions项,去掉stream_socket_server。
安装步骤说明:
1、composer安装命令,composer包安装倒了vendor/workerman/目录中。
1 | composer require topthink/think-worker=1.0.1 |
2、在项目根目录中新建server文件,内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | #!/usr/bin/env php <?php // +---------------------------------------------------------------------- // | Date:2019.1.15 // +---------------------------------------------------------------------- // | Author: asher.w // +---------------------------------------------------------------------- // 调试模式开关 define("APP_DEBUG", true); // 定义CMF根目录,可更改此目录 define('CMF_ROOT', __DIR__ . '/'); // 定义插件目录 define('PLUGINS_PATH', __DIR__ . '/public/plugins/'); // 定义应用目录 define('APP_PATH', __DIR__ . '/app/'); // 定义CMF核心包目录 define('CMF_PATH', CMF_ROOT . 'simplewind/cmf/'); // 定义扩展目录 define('EXTEND_PATH', CMF_ROOT . 'simplewind/extend/'); define('VENDOR_PATH', CMF_ROOT . 'simplewind/vendor/'); // 定义应用的运行时目录 define('RUNTIME_PATH', CMF_ROOT . 'data/runtime/'); define('BIND_MODULE','push/Chat'); // 加载框架引导文件 require CMF_ROOT.'simplewind/thinkphp/start.php'; |
3、服务器端控制器代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | <!--?php /** * Created by PhpStorm. * User: asher * Date: 2019/1/13 * Time: 15:19 */ namespace app\push\controller; use think\worker\Server; class ChatController extends Server { protected $socket = 'websocket://0.0.0.0:2346'; private $_name = []; private $_uidConnections = []; /** * 收到信息 * @param $connection * @param $data */ public function onMessage($connection, $data) { if (!$this->is_json($data)){<br ?--> $connection->send('非法请求'); return; } $data = json_decode($data,true); if (!isset($data['type'])){ $connection->send('非法请求'); return; } switch ($data['type']){ case 'private_message': // 假设消息格式为 uid:message 时是对 uid 发送 message $p_msg = explode(':',$data['data']['message']); $_msg = ['name'=>$this->getName($connection->id),'message'=>$p_msg[1]]; $this->sendMessageByUid($p_msg[0], $this->createData('private_message',$_msg)); break; case 'message': foreach ( $this->worker->connections as $_connection ){ $_connection->send($this->createData('message',['name'=>$this->getName($connection->id),'message'=>$data['data']['message']])); } break; case 'setName': $this->setName($connection->id, $data['data']['name']); //测试 设置id $this->_uidConnections[$data['data']['name']] = $connection; $connection->send($this->createData('msg',['msg'=>'设置昵称为:'.$data['data']['name']])); break; case 'push': if ($data['id']){ if (isset($this->worker->connections[$data['id']])){ $this->worker->connections[$data['id']]->send($this->createData('message',['name'=>'后台','message'=>$data['data']])); }else{ $connection->send($this->createData('result',['msg'=>'连接不存在'],1)); } }else{ foreach ( $this->worker->connections as $_connection ){ if ($connection->id != $_connection->id) $_connection->send($this->createData('message',['name'=>'后台','message'=>$data['data']])); } } $connection->send($this->createData('result',['msg'=>'推送成功'])); break; } } // 针对uid推送数据 function sendMessageByUid($uid, $message) { if(isset($this->_uidConnections[$uid])) { $connection = $this->_uidConnections[$uid]; $connection->send($message); } } /** * 当连接建立时触发的回调函数 * @param $connection */ public function onConnect($connection) { //设置默认昵称 $this->setName($connection->id,'匿名'.rand(1000,9999)); } /** * 当连接断开时触发的回调函数 * @param $connection */ public function onClose($connection) { unset($this->_name[$connection->id]); } /** * 当客户端的连接上发生错误时触发 * @param $connection * @param $code * @param $msg */ public function onError($connection, $code, $msg) { echo "error $code $msg\n"; } /** * 每个进程启动 * @param $worker */ public function onWorkerStart($worker) { } private function createData($type,$data,$code=0){ $_data = [ 'code' => $code, 'type' => $type, 'data' => $data ]; return json_encode($_data); } private function setName($id,$name){ $this->_name[$id] = $name; } private function getName($id){ return $this->_name[$id]; } private function is_json($str) { json_decode($str); return(json_last_error() == JSON_ERROR_NONE) && !is_numeric($str) ; } } |
4、linux进入项目根目录,启用服务监听端口。
1 | php server start |
1 2 3 4 5 6 7 8 | Workerman[server] start in DEBUG mode ------------------------------------------- WORKERMAN -------------------------------------------- Workerman version:3.5.17 PHP version:5.6.36 -------------------------------------------- WORKERS --------------------------------------------- proto user worker listen processes status tcp root none websocket://0.0.0.0:2346 1 [OK] -------------------------------------------------------------------------------------------------- Press Ctrl+C to stop. Start success. |
5、前端页面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | <style> .content{<br /> width: 700px;<br /> margin: auto;<br /> }<br /> .box{<br /> padding-bottom: 3px;<br /> padding-top: 3px;<br /> }<br /> .box input{<br /> width: 70%;<br /> }<br /> .box button{<br /> width: 25%;<br /> }<br /> </style> <h3>CHAT TEST</h3> <hr /> <div class="content"> <div class="box"><input name="name" type="text" /> <button id="setName">设置昵称</button></div> <div class="box"><input name="message" type="text" /> <button id="send">发送</button></div> <div id="content" class="box"></div> </div> <script> var ws; var connected = false; $(function () { ws = new WebSocket("ws://local.hillmatrix.com:2346"); ws.onopen = function() { console.log('WebSocket 连接成功'); connected = true; }; ws.onmessage = function(e) { // e.data console.log(e.data); try { var data = JSON.parse(e.data); //非法数据,不处理 if (!data.hasOwnProperty("code")) return; if (data.code != 0){ if (data.hasOwnProperty("msg")){ alert(data.msg); } return } if (!data.hasOwnProperty("data") || !data.hasOwnProperty("type") || !data.hasOwnProperty("data")) return; switch (data.type){ case 'private_message': addChatItem(data.data.name + '(悄悄的...)',data.data.message) break; case 'message': addChatItem(data.data.name,data.data.message) break; case 'msg': alert(data.data.msg) break; } }catch (ex) { } }; }); $("#setName").click(function () { var name = $('input[name=name]').val().trim(); if (name==''){ alert('昵称不能为空'); return; } sendData('setName',{name:name}); }); $("#send").click(function () { var message = $('input[name=message]').val().trim(); if (message==''){ alert('内容不能为空'); return; } if(message.indexOf(":") >= 0){ sendData('private_message',{message:message}); }else{ sendData('message',{message:message}); } }) function addChatItem( name,message ) { var temp = '</p> <p> </p> <div class="box"><p> </p> <p>__NAME__ 说:</p> <p> </p> <p>__CONTENT__</p> <p> </div> <p> </p> <p>'; temp = temp.replace( "__NAME__",name ); temp = temp.replace( "__CONTENT__",message ); $("#content").append(temp); } function sendData(type,data) { if(ws.readyState != 1){ alert(connected ? "连接已中断" : "连接不成功"); return; } ws.send(JSON.stringify({type:type,data:data})); } </script> |
6、测试效果
参考代码:https://gitee.com/goto8848/build_a_simple_chat_room_with_workerman
官方文档:http://doc.workerman.net/faq/send-data-to-client.html
thinkphp5.1 + workerman 留着以后参考:https://blog.csdn.net/qq_27238185/article/details/81477303