dever 5 years ago
commit
458a3eaed3
4 changed files with 341 additions and 0 deletions
  1. 106 0
      client.html
  2. 6 0
      server.php
  3. 117 0
      socket/Worker.php
  4. 112 0
      socket/Worker_old.php

+ 106 - 0
client.html

@@ -0,0 +1,106 @@
+
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <title>Title</title>
+</head>
+<body>
+    <div id="txtcontent" style="width: 500px;height: 250px;border: 1px solid gray"></div>
+    <div>所有用户:<select id="listuers"></select></div>
+    <div>你的昵称:<input type="text" id="username" /></div>
+    <div>
+        回复内容:
+        <textarea style="width: 500px;height: 100px" id="txtmsg"></textarea>
+    </div>
+    <div>
+        <button onclick="connectServer()">连接服务器</button>
+        <button onclick="send()">发送消息</button>
+    </div>
+ 
+    <script>
+        var socket = null;  //将socket实例保存到变量中
+        var isLogin = false;    //登录标识符
+ 
+        //1、链接服务端
+        function connectServer(){
+            var username = document.getElementById('username').value;
+            //如果用户名为空
+            if(username == ''){
+                alert('用户昵称不能为空');
+            }
+ 
+            //2、实例化
+            socket = new WebSocket('ws://192.168.33.10:2346');
+ 
+            //3、打开的时候,发送消息
+            socket.onopen = function(){
+                socket.send('login:' + username);   //谁登录了
+            };
+ 
+            //4、接收服务器返回的数据
+            socket.onmessage = function(e){
+                //console.log(e);
+                var getMsg = e.data;    //返回的数据
+ 
+                //解析getMsg。思路:通过正则匹配,显示出来
+                if(/^notice:success$/.test(getMsg)){    //服务端验证通过
+                    isLogin = true;
+                }else if(/^msg:/.test(getMsg)){         //将服务端返回的普通消息显示出来
+                    var p = document.createElement('P');
+                    //将msg:替换为空
+                    p.innerHTML = '<span>服务端返回的消息:</span>' + getMsg.replace('msg:','');
+                    document.getElementById('txtcontent').appendChild(p);   //追加节点
+                }else if(/^users:/.test(getMsg)){       //广播(将所有用户昵称显示出来)
+                    //console.log(getMsg);
+                    nicheng = getMsg.replace('users:','');  //{"61.144.116.123":"yangxi"}
+                    shownicheng = eval('('+nicheng+')');    //转json
+ 
+                    var listusers = document.getElementById('listuers');
+                    listusers.innerHTML = '';   //先清空
+                    for(var key in shownicheng){
+                        var option = document.createElement('option');
+                        option.value = key; //ip
+                        option.innerHTML = shownicheng[key]; //将昵称填充进去
+                        listusers.appendChild(option);      //追加节点
+                    }
+                }else if(/^dian:/.test(getMsg)){ //单播(点对点发消息)
+                    //console.log(getMsg);
+                    var p = document.createElement('P');    //创建节点
+                    //将msg:替换为空
+                    p.innerHTML = '<span>单播的消息:</span>' + getMsg.replace('dian:','');
+                    document.getElementById('txtcontent').appendChild(p);   //追加节点
+                }
+            };
+            //5、服务端关闭
+            socket.onclose = function(){
+                isLogin = false;
+                alert('服务器断开');
+            };
+        }
+ 
+        //发送消息按钮
+        function send(){
+            if(!isLogin){
+                alert('请先通过服务器验证');
+            }
+ 
+            //获取要发送的内容
+            var msg = document.getElementById('txtmsg').value;
+            //发送消息给服务端
+            socket.send('msg:' + msg);
+ 
+            //单聊,点对点发送消息
+            var listusers = document.getElementById('listuers');
+            var toUserIPP = listusers.options[listusers.selectedIndex].value;   //收消息的ip
+            var toUserName = listusers.options[listusers.selectedIndex].text;   //收消息的昵称
+            socket.send('dian:<' + toUserIPP + '>:' + msg);
+ 
+            //将发出的普通消息显示出来
+            var p = document.createElement('p');
+            p.innerHTML = '<span>客户端发出的消息:</span>' + msg;
+            document.getElementById('txtcontent').appendChild(p);
+        }
+    </script>
+</body>
+</html>

+ 6 - 0
server.php

@@ -0,0 +1,6 @@
+#!/usr/bin/env php
+<?php
+define('APP_PATH', __DIR__ . '/application/');
+define('BIND_MODULE','act/Worker');
+// 加载框架引导文件
+require __DIR__ . '/thinkphp/start.php';

+ 117 - 0
socket/Worker.php

@@ -0,0 +1,117 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | framework
+// +----------------------------------------------------------------------
+// | 版权所有 2014~2018 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
+// +----------------------------------------------------------------------
+// | 官方网站: http://framework.thinkadmin.top
+// +----------------------------------------------------------------------
+// | 开源协议 ( https://mit-license.org )
+// +----------------------------------------------------------------------
+// | github开源项目:https://github.com/zoujingli/framework
+// +----------------------------------------------------------------------
+
+namespace app\act\socket;
+
+use think\worker\Server;
+
+class Worker extends Server
+{
+
+    protected $clients = array();  //保存客户端信息
+    protected $socket = 'websocket://0.0.0.0:2346';
+
+    /**
+     * 收到信息 客户端必须传形如 我自己的uid加密串signature=xxx&to=xxx对方的非加密uid&msg=你好,之后再进行非对称加密
+     * @param $connection
+     * @param $data
+     */
+    public function onMessage($connection, $data)
+    {
+        # 解密data 后续开发
+
+        # 解析data
+        parse_str($data, $data);
+
+        # 验证有效性
+        if (!isset($data['signature']) && !isset($data['to']) && !isset($data['msg'])) {
+            # 通知客户端有问题
+            $connection->send('status=2&msg=error');
+        } else {
+            $data['user'] = $this->load('api/lib/user')->decode($data['signature']);
+            if (!$data['user']) {
+                $connection->send('status=2&msg=error');
+            }
+
+            if (isset($data['user']['uid']) && $data['user']['uid'] > 0 && $data['user']['uid'] != $data['to']) {
+                # 连接成功
+
+                # 检查当前客户端里有没有,没有则保存
+                if (!isset($this->clients[$data['user']['uid']])) {
+                    # 双方用户信息都由客户端获取
+                    $ip = $connection->getRemoteIp();
+                    $port = $connection->getRemotePort();
+
+                    $this->clients[$data['user']['uid']]] = array
+                    (
+                        'ip' => $ip,
+                        'port' => $port,
+                        'connection' => $connection
+                    );
+                }
+
+                # 写入数据库,状态为未读
+
+                # 检查要发送的信息,有没有在客户端列表里
+                if (isset($this->clients[$data['to']])) {
+                    # 在线,把消息发过去,客户端看过之后再设置为已读
+                    $this->clients[$data['to']]['connection']->send($data['msg']);
+                } else {
+                    # 不在线,等该用户上线再读取就行了。不用这里做什么了。
+                }
+
+            } else {
+                $connection->send('status=2&msg=error');
+            }
+        }
+    }
+
+    /**
+     * 当连接建立时触发的回调函数
+     * @param $connection
+     */
+    public function onConnect($connection)
+    {
+
+    }
+
+    /**
+     * 当连接断开时触发的回调函数
+     * @param $connection
+     */
+    public function onClose($connection)
+    {
+        unset($this->clients[$connection->getRemoteIp()]);
+    }
+
+    /**
+     * 当客户端的连接上发生错误时触发
+     * @param $connection
+     * @param $code
+     * @param $msg
+     */
+    public function onError($connection, $code, $msg)
+    {
+        echo "error $code $msg\n";
+    }
+
+    /**
+     * 每个进程启动
+     * @param $worker
+     */
+    public function onWorkerStart($worker)
+    {
+
+    }
+}

+ 112 - 0
socket/Worker_old.php

@@ -0,0 +1,112 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | framework
+// +----------------------------------------------------------------------
+// | 版权所有 2014~2018 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
+// +----------------------------------------------------------------------
+// | 官方网站: http://framework.thinkadmin.top
+// +----------------------------------------------------------------------
+// | 开源协议 ( https://mit-license.org )
+// +----------------------------------------------------------------------
+// | github开源项目:https://github.com/zoujingli/framework
+// +----------------------------------------------------------------------
+
+namespace app\act\socket;
+
+use think\worker\Server;
+
+class Worker extends Server
+{
+
+    protected $clients = [];  //保存客户端信息
+    protected $socket = 'websocket://0.0.0.0:2346';
+
+    /**
+     * 收到信息
+     * @param $connection
+     * @param $data
+     */
+    public function onMessage($connection, $data)
+    {
+        if (preg_match('/^login:(\w{3,20})/i', $data, $result)) {
+            $ip = $connection->getRemoteIp();       //获取当前客户端IP
+            $port = $connection->getRemotePort();   //获取当前客户端端口
+
+            if (!array_key_exists($ip, $this->clients)) {
+                $this->clients[$ip.':'.$port] = ['ipp'=>$ip.':'.$port,'name'=>$result[1],'conn'=>$connection]; //广播的话,不止保存昵称,还要保存ip和当前和服务器连接的那个客户端
+                //将处理完的信息返回给客户端(给客户端发送任意消息)
+                $connection->send('notice:success');
+                $connection->send('msg:你好'.$result[1]);
+                echo $ip . ':'.$port . '--------' .$result[1] . 'login' . PHP_EOL; //打印看结果
+     
+                //一旦有用户登录,就把保存的客户端信息发过去(显示出所有用户)
+                //$connection->send('users:'.json_encode($clients));
+                //广播(群聊)
+                $users = 'users:'.json_encode(array_column($this->clients,'name','ipp'));   //返回数组中指定的列
+                foreach($this->clients as $ip=>$client){
+                    //拿当前和服务器连接的那个客户端,发送消息
+                    $client['conn']->send($users);
+                }
+            }
+        } elseif(preg_match('/^msg:(.*?)/isU',$data,$megset)) {
+            //2.2、处理发来的普通消息
+            if(array_key_exists($connection->getRemoteIp(),$this->clients)) {  //判断该ip是否存在,存在就是已经登录的
+                echo '用户:' . $connection->getRemoteIp() . '发的消息是' . $megset[1] . PHP_EOL;
+                if($megset[1] == 'nihao'){
+                    $connection->send('msg:nihao'.$this->clients[$connection->getRemoteIp()]);
+                }
+                //我认为广播应该在这些,将用户A说的话,显示到页面上,让所有用户都能看见
+            }
+        } elseif(preg_match('/^dian:\<(.*?)\>:(.*?)/isU',$data,$meg)) {
+            //单播,点对点发消息
+            $ipp = $meg[1]; //接收消息用户的ip
+            $msg = $meg[2]; //发送的数据
+            $name = $this->clients[$ipp]['name'];    
+            echo "<pre>";
+            var_dump($name);
+            if(array_key_exists($ipp,$this->clients)){    //接收的ip也登录了,也就是有这个用户
+                $this->clients[$ipp]['conn']->send('dian:'.$msg);
+                echo $ipp.'==>'.$msg.PHP_EOL;
+            }
+        }
+    }
+
+    /**
+     * 当连接建立时触发的回调函数
+     * @param $connection
+     */
+    public function onConnect($connection)
+    {
+
+    }
+
+    /**
+     * 当连接断开时触发的回调函数
+     * @param $connection
+     */
+    public function onClose($connection)
+    {
+        unset($this->clients[$connection->getRemoteIp()]);
+    }
+
+    /**
+     * 当客户端的连接上发生错误时触发
+     * @param $connection
+     * @param $code
+     * @param $msg
+     */
+    public function onError($connection, $code, $msg)
+    {
+        echo "error $code $msg\n";
+    }
+
+    /**
+     * 每个进程启动
+     * @param $worker
+     */
+    public function onWorkerStart($worker)
+    {
+
+    }
+}