Your Name před 6 měsíci
rodič
revize
389b8e31ab

+ 126
- 0
app/command/Webchat.php Zobrazit soubor

@@ -0,0 +1,126 @@
1
+<?php
2
+declare (strict_types = 1);
3
+
4
+namespace app\command;
5
+
6
+use think\console\Command;
7
+use think\console\Input;
8
+use think\console\input\Argument;
9
+use think\console\input\Option;
10
+use think\console\Output;
11
+use Workerman\worker;
12
+use Workman\Connection\TcpConnection;
13
+
14
+require __DIR__.'/../../vendor/autoload.php';
15
+
16
+class Webchat extends Command
17
+{
18
+    protected function configure()
19
+    {
20
+        // 指令配置
21
+        $this->setName('startWebchat')
22
+            ->setDescription('the startWebchat command');
23
+    }
24
+
25
+    protected function execute(Input $input, Output $output)
26
+    {
27
+        // 指令输出
28
+        $output->writeln('startWebchat');
29
+
30
+        // 开启一个websocket服务,该端口为前端请求的端口
31
+        $ws_worker = new Worker('websocket://0.0.0.0:2888');
32
+        $ws_worker->count = 4;
33
+        $ws_worker->name='test';
34
+
35
+        $ws_worker->onWorkerStart = function() {
36
+
37
+            // 开启一个内部端口,方便内部系统推送数据,Text协议格式 文本+换行符
38
+            // 开启内部端口的目的是方便服务端在某个时刻需要向前端推数据,提供了一个推数据的通道,这里的ip/端口号和前端没有关系,正常情况下是为了实现数据突变上送
39
+
40
+            $inner_text_worker = new Worker('text://0.0.0.0:2346');
41
+
42
+            // 这里是收集服务端本身向这个ws服务发数据时随时接受,然后处理完成后返回给前端
43
+            $inner_text_worker->onMessage = function($connection, $buffer) {
44
+                global $ws_worker;
45
+                // $data数组格式,里面有uid,表示向那个uid的页面推送数据
46
+                $data = json_decode($buffer, true);
47
+                if(!empty($data['uid'])) {
48
+                    $uid = $data['uid'];
49
+                    // 通过workerman,向uid的页面推送数据
50
+                    $ret = $this->sendMessageByUid($ws_worker,$uid, $buffer);
51
+                } else {
52
+                    // 广播消息
53
+                    $ret = $this->broadcast($ws_worker,$buffer);
54
+                }
55
+
56
+                // 返回推送结果给请求这个地址的服务端
57
+                $connection->send($ret ? 'ok' : 'fail');
58
+            };
59
+
60
+            // ## 执行监听 ##
61
+
62
+            $inner_text_worker->listen();
63
+
64
+        };
65
+
66
+        // 新增加一个属性,用来保存uid到connection的映射
67
+        $ws_worker->uidConnections = array();
68
+
69
+        // 当有客户端发来消息时执行的回调函数
70
+        $ws_worker->onMessage = function($connection, $data) {
71
+
72
+            global $ws_worker;
73
+
74
+            // 判断当前客户端是否已经验证,既是否设置了uid
75
+
76
+            if(!isset($connection->uid))
77
+            {
78
+                // 没验证的话把第一个包当做uid(这里为了方便演示,没做真正的验证)
79
+                $connection->uid = $data;
80
+                /* 保存uid到connection的映射,这样可以方便的通过uid查找connection,
81
+
82
+                               * 实现针对特定uid推送数据
83
+
84
+                               */
85
+                $ws_worker->uidConnections[$connection->uid] = $connection;
86
+
87
+            }
88
+            // 这句话表示客户端有请求来时,立即给客户端返回数据,正常情况下这里要根据客户端传的参数,做不同处理,返回不同数据
89
+            $connection->send('receive msg');
90
+
91
+        };
92
+
93
+        // 当有客户端连接断开时,断开删除已连接的客户端请求
94
+        $ws_worker->onClose = function($connection) {
95
+            global $ws_worker;
96
+
97
+            if(isset($connection->uid)) {
98
+                // 连接断开时删除映射
99
+                unset($ws_worker->uidConnections[$connection->uid]);
100
+            }
101
+        };
102
+
103
+        worker::runAll();
104
+    }
105
+
106
+    // 针对uid推送数据
107
+    function sendMessageByUid($worker,$uid, $message) {
108
+
109
+        if(isset($worker->uidConnections[$uid])) {
110
+            $connection = $worker->uidConnections[$uid];
111
+            $connection->send($message);
112
+            return true;
113
+        }
114
+
115
+        return false;
116
+
117
+    }
118
+
119
+    // 向所有验证的用户推送数据
120
+    function broadcast($worker,$message) {
121
+        foreach($worker->uidConnections as $connection) {
122
+            $connection->send($message);
123
+        }
124
+        return true;
125
+    }
126
+}

+ 178
- 1
app/work/controller/Push.php Zobrazit soubor

@@ -3,6 +3,7 @@ namespace app\work\controller;
3 3
 use think\facade\Db;
4 4
 use think\worker\Server;
5 5
 use Workerman\Lib\Timer;
6
+use Workerman\worker;
6 7
 use think\facade\View;
7 8
 
8 9
 /*
@@ -14,9 +15,39 @@ class Push extends Server
14 15
     protected $socket = 'http://0.0.0.0:2346';   //端口自行修改
15 16
 
16 17
     protected static $heartbeat_time = 55;
18
+    public $connectionArr = array();
17 19
 
18 20
     public function onWorkerStart($worker)
19 21
     {
22
+
23
+        // 开启一个内部端口,方便内部系统推送数据,Text协议格式 文本+换行符
24
+        // 开启内部端口的目的是方便服务端在某个时刻需要向前端推数据,提供了一个推数据的通道,这里的ip/端口号和前端没有关系,正常情况下是为了实现数据突变上送
25
+
26
+        $inner_text_worker = new Worker('text://0.0.0.0:5678');
27
+
28
+        // 这里是收集服务端本身向这个ws服务发数据时随时接受,然后处理完成后返回给前端
29
+        $inner_text_worker->onMessage = function($connection, $buffer) {
30
+            //global $worker;
31
+            // $data数组格式,里面有uid,表示向那个uid的页面推送数据
32
+            $data = json_decode($buffer, true);
33
+            if(!empty($data['uid'])) {
34
+                $uid = $data['uid'];
35
+                // 通过workerman,向uid的页面推送数据
36
+                $ret = $this->sendMessageByUid($uid, $buffer);
37
+            } else {
38
+                // 广播消息
39
+                $ret = $this->broadcast($buffer);
40
+            }
41
+
42
+            // 返回推送结果给请求这个地址的服务端
43
+            $connection->send($ret ? 'ok888' : 'fail666');
44
+        };
45
+
46
+        // ## 执行监听 ##
47
+        $inner_text_worker->listen();
48
+
49
+
50
+        // 进程启动后设置一个每秒运行一次的定时器
20 51
         //查看是否有新的充值或提现订单,有就推送给所有用户
21 52
         Timer::add(3, function () use ($worker) {
22 53
 
@@ -31,10 +62,27 @@ class Push extends Server
31 62
 
32 63
 
33 64
                 foreach ($worker->connections as $connection) {
65
+
66
+                    // 有可能该connection还没收到过消息,则lastMessageTime设置为当前时间
34 67
                     if (empty($connection->lastMessageTime)) {
35 68
                         $connection->lastMessageTime = $time_now;
69
+                        continue;
70
+                    }
71
+
72
+                    if((int)$connection->uid !== 10000){
73
+                        $connection->send("当前用户id非10000");
74
+                    }else{
75
+                        $data =  [
76
+                            'toUser' => 10000,
77
+                            'msgContent' => 'success6666',
78
+                            'connection' => json($connection)
79
+                        ];
80
+                        //$this->sendMsg($data);
36 81
                     }
37 82
 
83
+
84
+                    //$connection->lastMessageTime 是指上次通讯时间
85
+                    //// 上次通讯时间间隔大于心跳间隔,则认为客户端已经下线,关闭连接
38 86
                     if ($time_now - $connection->lastMessageTime > self::$heartbeat_time) {
39 87
                         //后台控制连接
40 88
                         //前端断了 可以点击重新连接 类似游戏重新登录
@@ -43,7 +91,7 @@ class Push extends Server
43 91
 
44 92
                     $data = ['time'=> $time_now,'lastMessageTime' => $connection->lastMessageTime];
45 93
 
46
-                    $connection->send(json_encode($data));
94
+                    //$connection->send(json_encode($data));
47 95
                 }
48 96
                 //处理发送状态 ['is_push' => 1]
49 97
             } else {
@@ -61,7 +109,113 @@ class Push extends Server
61 109
         });
62 110
     }
63 111
 
112
+    // 针对uid推送数据
113
+    function sendMessageByUid($uid, $message)
114
+    {
115
+        if (isset($this->connectionArr[$uid])) {
116
+            $connection = $this->connectionArr[$uid];
117
+            $connection->send($message);
118
+            return true;
119
+        }
120
+    }
121
+
122
+    // 向所有验证的用户推送数据
123
+    function broadcast($message) {
124
+        foreach($this->connectionArr as $connection) {
125
+            $connection->send($message);
126
+        }
127
+        return true;
128
+    }
129
+
130
+    /**
131
+     * 收到信息
132
+     * @param $connection
133
+     * @param $data
134
+     */
135
+    public function onMessage($connection, $data)
136
+    {
137
+        //收到消息刷新最后时间 前端发送心跳
138
+        $connection->lastMessageTime = time();
139
+        $wsData = json_decode($data,true);
140
+
141
+        if(!isset($wsData['option'])){
142
+            $connection->send('接收数据:'.$data);
143
+            return;
144
+        }
145
+        switch($wsData['option']){
146
+            case "login":
147
+                $connection->uid=$wsData['uid'];
148
+                //echo $wsData['uid']."上线了";
149
+                $connection->send($wsData['uid']."上线了");
150
+                $this->setOnline($connection);
151
+                break;
152
+            default:
153
+                $connection->send('其他数据');
154
+                break;
155
+        }
156
+    }
157
+
158
+    //如果是发送消息
159
+    public function sendMsg($data){
160
+        /*$data =  [
161
+            'toUser' => 10000,
162
+            'msgContent' => '主动发送',
163
+        ];*/
164
+        $msgStr = json_encode(array("option"=>"sendMsg","msg"=>"发给".$data['toUser']."的消息:".$data['msgContent']));
165
+        $this->connectionArr[$data['toUser']]->send($msgStr);
166
+    }
167
+
168
+    //用户上线
169
+    public function setOnline($connection){
170
+        $this->connectionArr[$connection->uid] = $connection;
171
+    }
172
+
173
+    /**
174
+     * 当连接建立时触发的回调函数
175
+     * @param $connection
176
+     */
177
+    public function onConnect($connection)
178
+    {
179
+    }
180
+
181
+    /**
182
+     * 当连接断开销毁连接对象节约内存空间
183
+     * @param $connection
184
+     */
185
+    public function onClose($connection)
186
+    {
187
+        // 连接断开时删除映射
188
+        unset($this->connectionArr[$connection->uid]);
189
+    }
190
+
191
+    /**
192
+     * 当客户端的连接上发生错误时触发
193
+     * @param $connection
194
+     * @param $code
195
+     * @param $msg
196
+     */
197
+    public function onerror($connection, $code, $msg)
198
+    {
199
+        echo "\r\n error $code $msg\n";
200
+    }
201
+
64 202
     /*
203
+     * 阿里云
204
+     * https://ecs.console.aliyun.com/
205
+     * kod桌面
206
+     * http://rong.zx2049.com/
207
+     * 禅道
208
+     * http://zentao.dev.zx2049.com/
209
+     * 宝塔
210
+     * https://47.120.56.77:25705/
211
+     * layui手册
212
+     * http://layui.apixx.net/demo/panel.html
213
+     * tp5.1手册
214
+     * https://www.kancloud.cn/manual/thinkphp5_1/354084
215
+     * tp8.0手册
216
+     * https://doc.thinkphp.cn/v8_0/shitubianliang.html
217
+     * 后端
218
+     * http://console.zx2049.com/admin/index/index.html#
65 219
      * 客户端
66 220
      * http://workerman.dev.zx2049.com/work/push/index
67 221
      */
@@ -76,4 +230,27 @@ class Push extends Server
76 230
         return View::fetch();
77 231
     }
78 232
 
233
+    /*
234
+     * 测试
235
+     * http://workerman.dev.zx2049.com/work/push/test
236
+     */
237
+    public function test(){
238
+
239
+        $post = request()->post();
240
+        if(empty($post['info'])){
241
+            return false;
242
+        }
243
+        // 建立socket连接到内部推送端口
244
+        $client = stream_socket_client('tcp://127.0.0.1:5678', $errno, $errmsg, 1);
245
+        //dump($client);
246
+        // 推送的数据,包含uid字段,表示是给这个uid推送
247
+        $data = array('uid'=>'10000', 'percent'=>'88%','msg'=>$post['info']);
248
+
249
+        // 发送数据,注意5678端口是Text协议的端口,Text协议需要在数据末尾加上换行符
250
+        fwrite($client, json_encode($data,JSON_UNESCAPED_UNICODE)."\n");
251
+
252
+        // 读取推送结果
253
+        echo fread($client, 8192);
254
+    }
255
+
79 256
 }

+ 14
- 0
app/work/controller/Worker.php Zobrazit soubor

@@ -0,0 +1,14 @@
1
+<?php
2
+namespace app\work\controller;
3
+
4
+use think\worker\Server;
5
+
6
+class Worker extends Server
7
+{
8
+    protected $socket = 'http://0.0.0.0:2346';
9
+
10
+    public function onMessage($connection,$data)
11
+    {
12
+        $connection->send(json_encode($data));
13
+    }
14
+}

+ 1
- 1
config/worker_server.php Zobrazit soubor

@@ -19,7 +19,7 @@ return [
19 19
     'port'           => 2346, // 监听端口
20 20
     'socket'         => '', // 完整监听地址
21 21
     'context'        => [], // socket 上下文选项
22
-    'worker_class'   => 'app\work\controller\Push', // 自定义Workerman服务类名 支持数组定义多个服务
22
+    'worker_class'   => ['app\work\controller\Push'], // 自定义Workerman服务类名 支持数组定义多个服务
23 23
 
24 24
     // 支持workerman的所有配置参数
25 25
     'name'           => 'thinkphp',

+ 89
- 37
view/work/push/index.html Zobrazit soubor

@@ -14,6 +14,11 @@
14 14
     <script src="//unpkg.com/layui@2.9.8/dist/layui.js"></script>
15 15
 
16 16
 
17
+    <style>
18
+        #info_li li{
19
+            width: 100%;float: left;margin-bottom: 5px;
20
+        }
21
+    </style>
17 22
 </head>
18 23
 
19 24
 <body>
@@ -123,23 +128,22 @@
123 128
         </div>-->
124 129
         <div class="layui-form-item">
125 130
             <div class="layui-input-block">
126
-                <button class="layui-btn" lay-submit lay-filter="formDemo">立即提交</button>
131
+                <button class="layui-btn" lay-submit lay-filter="formDemo" id="myButton">立即提交</button>
127 132
                 <button type="reset" class="layui-btn layui-btn-primary">重置</button>
128 133
             </div>
129 134
         </div>
130 135
     </form>
131 136
 </div>
132 137
 
133
-<div class="layui-bg-gray" style="padding: 30px;">
138
+<div class="layui-bg-gray" style="padding: 30px;width:800px;margin: 0 auto;">
134 139
     <div class="layui-row layui-col-space15">
135
-        <div class="layui-col-md6">
136
-            <div class="layui-panel">
137
-                <div style="padding: 50px 30px;">一个面板</div>
138
-            </div>
139
-        </div>
140
-        <div class="layui-col-md6">
141
-            <div class="layui-panel">
142
-                <div style="padding: 50px 30px;">一个面板</div>
140
+        <div class="layui-col-md12">
141
+            <blockquote class="layui-elem-quote">执行过程:</blockquote>
142
+            <div class="layui-panel" style="height:300px;overflow-y:scroll;">
143
+                <div style="padding: 50px 30px;" id="info_li">
144
+                    <li id="start_li"></li>
145
+
146
+                </div>
143 147
             </div>
144 148
         </div>
145 149
     </div>
@@ -249,12 +253,17 @@
249 253
         //监听提交
250 254
         form.on('submit(formDemo)', function(data){
251 255
             //layer.msg(JSON.stringify(data.field));
256
+            var index = layer.load();
257
+            $("#myButton").attr("disabled", true);
258
+
252 259
             $.ajax({  // 定义ajax发送请求
253 260
                 url:'http://console.zx2049.com/api/ai/uploadCsvFrom',  // 请求发送的地址  有三种填写方式,与form标签的action一致
254 261
                 method:'post',  // 请求发送的方式
255 262
                 data: data.field,  // 请求携带的数据
256 263
                 success:function(res){  // 异步等待,当后端响应成功会回调执行匿名函数,并将数据传递给data参数
257 264
                     console.log(res);
265
+                    layer.close(index);
266
+                    $("#myButton").attr("disabled", false);
258 267
 
259 268
                     if(res.code == 0){
260 269
                         layer.msg('提交成功');
@@ -277,40 +286,83 @@
277 286
 
278 287
 
279 288
 <script>
280
-    let ws = new WebSocket("ws://47.120.56.77:2346")
281
-
282
-    ws.onopen = function() {
283
-        //绑定连接事件
284
-        console.log("连接成功");
285
-
286
-        //每30秒发送一次心跳
287
-        setInterval(function(){
289
+    layui.use(['element', 'layer'], function(){
290
+        var $ = layui.jquery
291
+            ,element = layui.element
292
+            ,layer = layui.layer;
288 293
 
289
-            // 假设ws是一个WebSocket实例
290
-            if (ws.readyState !== WebSocket.OPEN) {
291
-                // 连接不是打开状态,可能是关闭中或已关闭,不应该发送数据
292
-                console.log('WebSocket is not open.');
294
+        function isJSON(str) {
295
+            try {
296
+                const obj = eval('(' + str + ')');
297
+                return typeof obj === "object" && obj !== null;
298
+            } catch (e) {
293 299
                 return false;
294 300
             }
301
+        }
302
+
303
+        /*
304
+        var str = '{"uid":"10000","percent":"88%","msg":"相同规则与相同主词下,[建筑职称中级需要什么条件]已存在-不可重复导入!"}';
305
+        console.log(isJSON((str)));
306
+        var data = JSON.parse(str);
307
+        if('msg' in data){
308
+            var html = '<li>'+data.msg+'</li>';
309
+            $('#start_li').after(html);
310
+        }*/
311
+
312
+
313
+        //内部会存在此次连接
314
+        let ws = new WebSocket("ws://47.120.56.77:2346")
295 315
 
296
-            ws.send(JSON.stringify({'type':"peng"}));
297
-            console.log('发送心跳...');
316
+        ws.onopen = function() {
317
+            //绑定连接事件
318
+            console.log("连接成功");
298 319
 
299
-        },30000)
300 320
 
301
-    };
321
+            ws.send(JSON.stringify({'uid': '10000','option':'login'}));
322
+            console.log('发送登录...');
302 323
 
303
-    ws.onmessage = function(evt) {
304
-        //绑定收到消息事件
305
-        data = JSON.parse(evt.data)
306
-        console.log(data);
307
-        //这里处理收到的消息, type类型有两种: connectin、deposit如果有deposit要提示有新的订单
324
+            //每30秒发送一次心跳
325
+            setInterval(function(){
308 326
 
309
-    };
327
+                // 假设ws是一个WebSocket实例
328
+                if (ws.readyState !== WebSocket.OPEN) {
329
+                    // 连接不是打开状态,可能是关闭中或已关闭,不应该发送数据
330
+                    console.log('WebSocket is not open.');
331
+                    return false;
332
+                }
310 333
 
334
+                ws.send(JSON.stringify({'data': '10000'}));
335
+                console.log('发送心跳...');
336
+
337
+            },30000)
338
+
339
+        };
340
+
341
+
342
+
343
+        ws.onmessage = function(evt) {
344
+            //绑定收到消息事件
345
+            if(isJSON(evt.data) === true){
346
+                var data = JSON.parse(evt.data);
347
+                if('msg' in data){
348
+                    var html = '<li>'+data.msg+'</li>';
349
+                    console.log(html);
350
+                    $('#start_li').after(html);
351
+                }
352
+            }else{
353
+
354
+            }
355
+            console.log(evt.data);
356
+            //这里处理收到的消息, type类型有两种: connectin、deposit如果有deposit要提示有新的订单
357
+        };
358
+
359
+
360
+        ws.onclose = function(evt) {
361
+            //绑定关闭或断开连接事件
362
+            console.log("连接已关闭");
363
+        };
364
+
365
+
366
+    });
367
+</script>
311 368
 
312
-    ws.onclose = function(evt) {
313
-        //绑定关闭或断开连接事件
314
-        console.log("连接已关闭");
315
-    };
316
-</script>

Loading…
Zrušit
Uložit