thinkcmf5(tp5.0) + workman实现简单客服聊天功能

配置过程中遇到的问题及说明:

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-&gt;is_json($data)){&lt;br ?--> $connection-&gt;send('非法请求');
return;
}

$data = json_decode($data,true);

if (!isset($data['type'])){
$connection-&gt;send('非法请求');
return;
}

switch ($data['type']){
case 'private_message':

// 假设消息格式为 uid:message 时是对 uid 发送 message
$p_msg = explode(':',$data['data']['message']);

$_msg = ['name'=&gt;$this-&gt;getName($connection-&gt;id),'message'=&gt;$p_msg[1]];

$this-&gt;sendMessageByUid($p_msg[0], $this-&gt;createData('private_message',$_msg));

break;
case 'message':
foreach ( $this-&gt;worker-&gt;connections as $_connection ){
$_connection-&gt;send($this-&gt;createData('message',['name'=&gt;$this-&gt;getName($connection-&gt;id),'message'=&gt;$data['data']['message']]));
}

break;
case 'setName':
$this-&gt;setName($connection-&gt;id, $data['data']['name']);

//测试 设置id
$this-&gt;_uidConnections[$data['data']['name']] = $connection;

$connection-&gt;send($this-&gt;createData('msg',['msg'=&gt;'设置昵称为:'.$data['data']['name']]));
break;
case 'push':
if ($data['id']){
if (isset($this-&gt;worker-&gt;connections[$data['id']])){
$this-&gt;worker-&gt;connections[$data['id']]-&gt;send($this-&gt;createData('message',['name'=&gt;'后台','message'=&gt;$data['data']]));
}else{
$connection-&gt;send($this-&gt;createData('result',['msg'=&gt;'连接不存在'],1));
}

}else{
foreach ( $this-&gt;worker-&gt;connections as $_connection ){
if ($connection-&gt;id != $_connection-&gt;id)
$_connection-&gt;send($this-&gt;createData('message',['name'=&gt;'后台','message'=&gt;$data['data']]));
}
}

$connection-&gt;send($this-&gt;createData('result',['msg'=&gt;'推送成功']));
break;
}

}

// 针对uid推送数据
function sendMessageByUid($uid, $message)
{
if(isset($this-&gt;_uidConnections[$uid]))
{
$connection = $this-&gt;_uidConnections[$uid];
$connection-&gt;send($message);
}
}

/**
* 当连接建立时触发的回调函数
* @param $connection
*/

public function onConnect($connection)
{
//设置默认昵称
$this-&gt;setName($connection-&gt;id,'匿名'.rand(1000,9999));
}

/**
* 当连接断开时触发的回调函数
* @param $connection
*/

public function onClose($connection)
{
unset($this-&gt;_name[$connection-&gt;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' =&gt; $code,
'type' =&gt; $type,
'data' =&gt; $data
];
return json_encode($_data);
}

private function setName($id,$name){
$this-&gt;_name[$id] = $name;
}

private function getName($id){
return $this-&gt;_name[$id];
}

private function is_json($str)
{
json_decode($str);
return(json_last_error() == JSON_ERROR_NONE) &amp;&amp; !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>

&nbsp;
<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