WorkMan学习篇:三

来源:互联网 发布:2017优化最好的游戏 编辑:程序博客网 时间:2024/06/16 07:47

WorkerMan学习篇:websocket+workerman聊天功能(三):点对点发送消息模拟

原创 2017年02月14日 16:25:37

1.WorkerMan学习篇:准备和热身 
2.WorkerMan学习篇:连接mysql时到底发生了什么鬼 
3. WorkerMan学习篇:websocket+workerman聊天功能设计(一):简单认证 
4.WorkerMan学习篇:websocket+workerman聊天功能(二):同步在线用户列表

上节课我们已经完成了同步在线用户列表,因为我们是使用IP作为一个唯一判断标志,所以我们的一台电脑只能登录一个用户:

    if(preg_match('/^login:(\w{3,20})/i',$data,$result)){ //代表是客户端认证        $ip = $connection->getRemoteIp();        if(!array_key_exists($ip,$clients)){ //必须是之前没有注册过//....//....
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

实际上,可以在一台电脑上,同时几个客户端来登录,只要保证这个key不唯一就行了,比如说可以拼接上一个端口:

 //存储新登录用户的数据$clients[$ip.':'.$port] = ['ipp'=>$ip.':'.$port,'name'=>$result[1],'conn'=>$connection];
  • 1
  • 2

这里写图片描述 
server端全部代码:

<?php//本机IP是10.211.55.13//需要监听的端口是 9090use Workerman\Connection\AsyncTcpConnection;use Workerman\Worker;require 'workerman/Autoloader.php';$clients = []; //保存客户端信息// 创建一个Worker监听9090端口,使用websocket协议通讯$ws_worker = new Worker("websocket://10.211.55.13:9090");// 启动4个进程对外提供服务$ws_worker->count = 4;/** * 同步登录用户列表 */function syncUsers(){    global $clients;    $users = 'users:'.json_encode(array_column($clients,'name','ipp')); //准备要广播的数据    foreach($clients as $ip=>$client){        $client['conn']->send($users);    }}// 当收到客户端发来的数据后$ws_worker->onMessage = function($connection, $data){    //这里用global的原因是:php是有作用域的,我们是在onMessage这个回调还是里操作外面的数组    //想要改变作用域外面的数组,就global一下    global $clients;    //验证客户端用户名在3-20个字符    if(preg_match('/^login:(\w{3,20})/i',$data,$result)){ //代表是客户端认证        $ip = $connection->getRemoteIp();        $port = $connection->getRemotePort();        if(!array_key_exists($ip.':'.$port, $clients)){ //必须是之前没有注册过            //存储新登录用户的数据            $clients[$ip.':'.$port] = ['ipp'=>$ip.':'.$port,'name'=>$result[1],'conn'=>$connection];            // 向客户端发送数据            $connection->send('notice:success'); //验证成功消息            $connection->send('msg:welcome '.$result[1]); //普通消息            echo $ip .':'.$port.'==>'.$result[1] .'==>login' . PHP_EOL; //这是为了演示,控制台打印信息            //有新用户登录            //需要同步登录用户数据            syncUsers();        }    }elseif(preg_match('/^msg:(.*?)/isU',$data,$msgset)){ //代表是客户端发送的普通消息        if(array_key_exists($connection->getRemoteIp(),$clients)){ //必须是之前验证通过的客户端            echo 'get msg:' . $msgset[1] .PHP_EOL; //这是为了演示,控制台打印信息            if($msgset[1] == 'nihao'){                //如果收到'nihao',就给客户端发送'nihao 用户名'                //给客户端发送普通消息                $connection->send('msg:nihao '.$clients[$connection->getRemoteIp()]);            }        }    }    // 设置连接的onClose回调    $connection->onClose = function($connection) //客户端主动关闭    {        global $clients;        unset($clients[$connection->getRemoteIp().':'.$connection->getRemotePort()]);        //客户端关闭        //即退出登录,也需要更新用户列表数据        syncUsers();        echo "connection closed\n";    };};// 运行workerWorker::runAll();
  • 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

点对点聊天简单设计

听起来好像是:利用websocket客户端直接向其他宇宙中的websocket发送消息。

实际上是: 
客户端html——>workerman监听9090端口(自己有个协议,服务端来中转)——–>客户端html

1.客户端构建发送消息,遵守一定的协议

var listusers = document.getElementById('listusers');var toUserIPP = listusers.options[listusers.selectedIndex].value; //发给用户的ip和端口var toUserName = listusers.options[listusers.selectedIndex].text; //发给用户的昵称socket.send('chat:<'+toUserIPP+'>:'+msg);
  • 1
  • 2
  • 3
  • 4

客户端最终发送一条chat:<10.211.55.2:50543>:message 这样的消息给服务端。 
服务端判断并根据ip+端口发送给对应的用户:

if (preg_match('/^chat:\<(.*?)\>:(.*?)/isU',$data,$msgset)){    $ipp = $msgset[1];    $msg = $msgset[2];    if (array_key_exists($ipp,$clients)){ //如果有这个用户        //就发送普通消息        $clients[$ipp]['conn']->send('msg:'.$msg);        echo $ipp.'==>'.$msg.PHP_EOL;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

这里写图片描述

服务端全部代码:

<?php//本机IP是10.211.55.13//需要监听的端口是 9090use Workerman\Connection\AsyncTcpConnection;use Workerman\Worker;require 'workerman/Autoloader.php';$clients = []; //保存客户端信息// 创建一个Worker监听9090端口,使用websocket协议通讯$ws_worker = new Worker("websocket://10.211.55.13:9090");// 启动4个进程对外提供服务$ws_worker->count = 4;/** * 同步登录用户列表 */function syncUsers(){    global $clients;    $users = 'users:'.json_encode(array_column($clients,'name','ipp')); //准备要广播的数据    foreach($clients as $ip=>$client){        $client['conn']->send($users);    }}// 当收到客户端发来的数据后$ws_worker->onMessage = function($connection, $data){    //这里用global的原因是:php是有作用域的,我们是在onMessage这个回调还是里操作外面的数组    //想要改变作用域外面的数组,就global一下    global $clients;    //验证客户端用户名在3-20个字符    if(preg_match('/^login:(\w{3,20})/i',$data,$result)){ //代表是客户端认证        $ip = $connection->getRemoteIp();        $port = $connection->getRemotePort();        if(!array_key_exists($ip.':'.$port, $clients)){ //必须是之前没有注册过            //存储新登录用户的数据            $clients[$ip.':'.$port] = ['ipp'=>$ip.':'.$port,'name'=>$result[1],'conn'=>$connection];            // 向客户端发送数据            $connection->send('notice:success'); //验证成功消息            $connection->send('msg:welcome '.$result[1]); //普通消息            echo $ip .':'.$port.'==>'.$result[1] .'==>login' . PHP_EOL; //这是为了演示,控制台打印信息            //有新用户登录            //需要同步登录用户数据            syncUsers();        }    }elseif(preg_match('/^msg:(.*?)/isU',$data,$msgset)){ //代表是客户端发送的普通消息        if(array_key_exists($connection->getRemoteIp(),$clients)){ //必须是之前验证通过的客户端            echo 'get msg:' . $msgset[1] .PHP_EOL; //这是为了演示,控制台打印信息            if($msgset[1] == 'nihao'){                //如果收到'nihao',就给客户端发送'nihao 用户名'                //给客户端发送普通消息                $connection->send('msg:nihao '.$clients[$connection->getRemoteIp()]);            }        }    }elseif (preg_match('/^chat:\<(.*?)\>:(.*?)/isU',$data,$msgset)){        $ipp = $msgset[1];        $msg = $msgset[2];        if (array_key_exists($ipp,$clients)){ //如果有这个用户            //就发送普通消息            $clients[$ipp]['conn']->send('msg:'.$msg);            echo $ipp.'==>'.$msg.PHP_EOL;        }    }    // 设置连接的onClose回调    $connection->onClose = function($connection) //客户端主动关闭    {        global $clients;        unset($clients[$connection->getRemoteIp().':'.$connection->getRemotePort()]);        //客户端关闭        //即退出登录,也需要更新用户列表数据        syncUsers();        echo "connection closed\n";    };};// 运行workerWorker::runAll();
  • 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

客户端全部代码:

<!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8">    <title>WebSocket_client</title>    <script>        //创建一个socket实例        var socket = null; //初始为null        var isLogin = false; //是否登录到服务器上        //定义一个连服务的函数        function connectServer(){            var username = document.getElementById('username').value;            if (username == ''){                alert('用户昵称必填');            }            socket = new WebSocket("ws://10.211.55.13:9090");            socket.onopen = function() {                socket.send('login:' + username);            };            socket.onmessage = function(e) {                var getMsg = e.data;                if(/^notice:success$/.test(getMsg)){ //服务器验证通过                    isLogin = true;                }else if(/^msg:/.test(getMsg)){ //代表是普通消息                    console.log(getMsg);                    var p = document.createElement('p');                    p.innerHTML = '<span>收到消息:</span>' + getMsg.replace('msg:','');                    document.getElementById('txtcontent').appendChild(p);                }else if(/^users:/.test(getMsg)){ //显示当前已登录用户                    console.log(getMsg);                    getMsg = getMsg.replace('users:','');                    getMsg= eval('('+getMsg+')'); //转json                    var listusers = document.getElementById('listusers');                    listusers.innerHTML = '';//清空                    for(var key in getMsg){                        var option = document.createElement('option');                        option.value = key; //ip                        option.innerHTML = getMsg[key]; //昵称                        listusers.appendChild(option); //添加元素进去                    }                }            };            socket.onclose = function(){                isLogin = false;            }        }        //发送消息        function send(){            if (!isLogin){                alert('请先通过服务器验证');            }            var msg = document.getElementById('txtmsg').value;            //console.log(msg);            socket.send('msg:' + msg); //发送消息到服务端            var listusers = document.getElementById('listusers');            var toUserIPP = listusers.options[listusers.selectedIndex].value; //发给用户的ip和端口            var toUserName = listusers.options[listusers.selectedIndex].text; //发给用户的昵称            socket.send('chat:<'+toUserIPP+'>:'+msg);            //chat:<10.211.55.2:50543>:message            //显示我们的消息到div中            var p = document.createElement('p');            p.innerHTML = '<span>发送消息给:['+toUserName+']:</span>' + msg;            document.getElementById('txtcontent').appendChild(p);        }    </script></head><body>    <div id="txtcontent" style="width: 500px;height: 250px;border: 1px solid gray"></div>    <div>所有用户:<select id="listusers"></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></body></html>
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 嘴唇上长透明泡怎么办 嘴唇上长很多泡怎么办 嘴巴里长白色泡怎么办 做了漂唇起泡了怎么办 漂唇之后起泡了怎么办 漂唇后起了水泡怎么办 嘴唇起泡,弄破了怎么办 九个月的宝宝上火了怎么办 8岁儿童嘴唇起泡怎么办 宝宝嘴皮上火起泡了怎么办 上嘴唇起泡肿了怎么办 上嘴唇突然肿了怎么办? 醒来上嘴唇肿了怎么办 嘴巴突然肿了怎么办呢 下嘴唇肿起来了怎么办 上嘴唇肿了起泡怎么办 上火下嘴唇肿了怎么办 上火嘴唇都肿了怎么办 嘴唇起泡后肿了怎么办 嘴唇上有白点颗粒状怎么办 嘴唇缺了一块红怎么办 人得钩端螺旋体怎么办 脖子上有鸡皮肤怎么办 不结婚老了以后怎么办 丁克族老了怎么办知乎 2个月宝宝咳嗽怎么办 干活累的手疼怎么办 脸上长白色的癣怎么办 全身起红斑很痒怎么办 宝宝脖子红烂了怎么办 背上长红斑很痒怎么办 身上起风疙瘩很痒怎么办 身上起小包很痒怎么办 浑身起红包很痒怎么办 手太粗糙怎么办小窍门 小腿长疙瘩很痒怎么办 腿过敏起红疙瘩怎么办 肚子上起红疙瘩很痒怎么办 小蚂蚁咬了肿了怎么办 锦鲤鱼尾巴烂了怎么办 泰迪身上长白毛怎么办