如何用Swoole WebSocket和Redis实现长尾词长问句的实时聊天室?
- 内容介绍
- 文章标签
- 相关推荐
本文共计1143个文字,预计阅读时间需要5分钟。
Redis实现每个连接WebSocket的服务器都唯一绑定一个用户。通过用户账号=WebSocket连接fd 存储到Redis中。MySQL实现离线消息池。若用户不在线,其他用户发送的消息暂时存储在MySQL中。
Redis 实现每个连接websocket的服务都唯一绑定一个用户。通过 用户账号 = websocket fd 存到redis中。
Mysql 实现离线消息池。如果一个用户不在线,则其他用户发送给他的消息暂时存储在mysql。待该用户上线时,再从离线消息池取出发送。
具体参考代码和相应注释:
<?php $server = new swoole_websocket_server("0.0.0.0", 9052); $redis = new Redis(); $redis->connect('127.0.0.1', 6379); $db = new mysqli('127.0.0.1', 'test', 'test', 'thinkphp5'); $server->on('open', function (swoole_websocket_server $server, $request) { echo "server: handshake success with fd{$request->fd}\n";//$request->fd 是客户端id }); $server->on('message', function (swoole_websocket_server $server, $frame) { $data = json_decode($frame->data,true); if($data['flag'] == 'init'){ //用户刚连接的时候初始化,每个用户登录时记录该用户对应的fd $GLOBALS['redis']->set($data['from'], $frame->fd); //处理发给该用户的离线消息 $sql = "SELECT `from`,content FROM thinkphp5.app_offline WHERE `to`='{$data['from']}' AND `from`='{$data['to']}' AND `status`='0' ORDER BY addtime ASC;"; if ($result = $GLOBALS['db']->query($sql)) { $re = array(); while ($row = $result->fetch_assoc()) { array_push($re, $row); } $result->free(); foreach($re as $content){ $content = json_encode($content); $server->push($frame->fd , $content); } //设置消息池中的消息为已发送 $sql = "UPDATE thinkphp5.app_offline SET `status`=1 WHERE `to`='{$data['from']}' AND `from`='{$data['to']}';"; $GLOBALS['db']->query($sql); } }else if($data['flag'] == 'msg'){ //非初始化的信息发送,一对一聊天,根据每个用户对应的fd发给特定用户 $tofd = $GLOBALS['redis']->get($data['to']); //消息要发给谁 $fds = []; //所有在线的用户(打开聊天窗口的用户) foreach($server->connections as $fd){ array_push($fds, $fd); } if(in_array($tofd,$fds)){ $tmp['from'] = $data['from']; //消息来自于谁 $tmp['content'] = $data['content']; //消息内容 $re = json_encode($tmp); $server->push($tofd , $re); }else{ //该玩家不在线(不在聊天室内),将信息发送到离线消息池 $time = time(); $sql = "INSERT INTO thinkphp5.app_offline (`to`,`from`,`content`,`status`,`addtime`) VALUES ('{$data['to']}','{$data['from']}','{$data['content']}','0','{$time}');"; $GLOBALS['db']->query($sql); } }else if($data['flag'] == 'group'){ //todo 群聊 }else if($data['flag'] == 'all'){ //全站广播 foreach($server->connections as $fd){ $server->push($fd , $data); } } }); $server->on('close', function ($ser, $fd) { echo "client {$fd} closed\n"; }); $server->start();
客户端代码:
<!DOCTYPE html> <html> <head> <title>XST-app</title> <meta 192.168.0.1:9052"); ws.onopen = function(){ console.log("握手成功"); var myemail = $("#myemail").val(); var toemail = $("#toemail").val(); var arr = {"flag":"init","from":myemail,"to":toemail}; var str = JSON.stringify(arr); ws.send(str); }; ws.onmessage = function(e){ var toemail = $("#toemail").val(); var toavatar = $("#toavatar").val(); var obj = JSON.parse(e.data); console.log(e.data); //但同时与两个人聊天时,可能两个人的消息都会出现在当前窗口,所以此处加个判断,此窗口只接收当前聊天对象的消息,其他则忽略 if(obj.from === toemail){ var ans = '<div class="answer"><div class="heard_img left"><img src="'+toavatar+'"></div>'; ans += '<div class="answer_text"><p>'+obj.content+'</p><i></i>'; ans += '</div></div>'; $('.speak_box').append(ans); for_bottom(); } }; ws.onerror = function(){ console.log("error"); var str = '<div class="question">'; str += '<div class="heard_img right"><img src="/static/images/xitong.jpg"></div>'; str += '<div class="question_text clear"><p>聊天服务器出现异常,暂时无法提供服务。</p><i></i>'; str += '</div></div>'; $('.speak_box').append(str); $('.write_box input').val(''); $('.write_box input').focus(); autoWidth(); for_bottom(); }; function send() { var content = $('.write_box input').val(); if(content === ''){ alert('请输入消息!'); $('.write_box input').focus(); }else{ var toemail = $("#toemail").val(); var myemail = $("#myemail").val(); var myavatar = $("#myavatar").val(); var arr = {"flag":"msg","to":toemail,"from":myemail,"content":content}; var msg = JSON.stringify(arr); console.log(msg); ws.send(msg); var str = '<div class="question">'; str += '<div class="heard_img right"><img src="'+myavatar+'"></div>'; str += '<div class="question_text clear"><p>'+content+'</p><i></i>'; str += '</div></div>'; $('.speak_box').append(str); $('.write_box input').val(''); $('.write_box input').focus(); autoWidth(); for_bottom(); } } }else{ alert("您的浏览器不支持 WebSocket!"); } function for_bottom(){ var speak_height = $('.speak_box').height(); $('.speak_box,.speak_window').animate({scrollTop:speak_height},500); } function autoWidth(){ $('.question_text').css('max-width',$('.question').width()-60); } autoWidth(); </script> </body> </html>
数据表结构:
CREATE TABLE `app_offline` ( `id` int(11) NOT NULL AUTO_INCREMENT, `from` varchar(50) DEFAULT NULL COMMENT '离线发送方', `to` varchar(50) DEFAULT NULL COMMENT '离线接收方', `content` varchar(1000) DEFAULT NULL COMMENT '发送的离线内容', `status` tinyint(4) DEFAULT '0' COMMENT '发送状态:0-未发送,1-已发送', `addtime` int(11) DEFAULT NULL COMMENT '发送方发送时间', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8
具体效果:
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持易盾网络。
本文共计1143个文字,预计阅读时间需要5分钟。
Redis实现每个连接WebSocket的服务器都唯一绑定一个用户。通过用户账号=WebSocket连接fd 存储到Redis中。MySQL实现离线消息池。若用户不在线,其他用户发送的消息暂时存储在MySQL中。
Redis 实现每个连接websocket的服务都唯一绑定一个用户。通过 用户账号 = websocket fd 存到redis中。
Mysql 实现离线消息池。如果一个用户不在线,则其他用户发送给他的消息暂时存储在mysql。待该用户上线时,再从离线消息池取出发送。
具体参考代码和相应注释:
<?php $server = new swoole_websocket_server("0.0.0.0", 9052); $redis = new Redis(); $redis->connect('127.0.0.1', 6379); $db = new mysqli('127.0.0.1', 'test', 'test', 'thinkphp5'); $server->on('open', function (swoole_websocket_server $server, $request) { echo "server: handshake success with fd{$request->fd}\n";//$request->fd 是客户端id }); $server->on('message', function (swoole_websocket_server $server, $frame) { $data = json_decode($frame->data,true); if($data['flag'] == 'init'){ //用户刚连接的时候初始化,每个用户登录时记录该用户对应的fd $GLOBALS['redis']->set($data['from'], $frame->fd); //处理发给该用户的离线消息 $sql = "SELECT `from`,content FROM thinkphp5.app_offline WHERE `to`='{$data['from']}' AND `from`='{$data['to']}' AND `status`='0' ORDER BY addtime ASC;"; if ($result = $GLOBALS['db']->query($sql)) { $re = array(); while ($row = $result->fetch_assoc()) { array_push($re, $row); } $result->free(); foreach($re as $content){ $content = json_encode($content); $server->push($frame->fd , $content); } //设置消息池中的消息为已发送 $sql = "UPDATE thinkphp5.app_offline SET `status`=1 WHERE `to`='{$data['from']}' AND `from`='{$data['to']}';"; $GLOBALS['db']->query($sql); } }else if($data['flag'] == 'msg'){ //非初始化的信息发送,一对一聊天,根据每个用户对应的fd发给特定用户 $tofd = $GLOBALS['redis']->get($data['to']); //消息要发给谁 $fds = []; //所有在线的用户(打开聊天窗口的用户) foreach($server->connections as $fd){ array_push($fds, $fd); } if(in_array($tofd,$fds)){ $tmp['from'] = $data['from']; //消息来自于谁 $tmp['content'] = $data['content']; //消息内容 $re = json_encode($tmp); $server->push($tofd , $re); }else{ //该玩家不在线(不在聊天室内),将信息发送到离线消息池 $time = time(); $sql = "INSERT INTO thinkphp5.app_offline (`to`,`from`,`content`,`status`,`addtime`) VALUES ('{$data['to']}','{$data['from']}','{$data['content']}','0','{$time}');"; $GLOBALS['db']->query($sql); } }else if($data['flag'] == 'group'){ //todo 群聊 }else if($data['flag'] == 'all'){ //全站广播 foreach($server->connections as $fd){ $server->push($fd , $data); } } }); $server->on('close', function ($ser, $fd) { echo "client {$fd} closed\n"; }); $server->start();
客户端代码:
<!DOCTYPE html> <html> <head> <title>XST-app</title> <meta 192.168.0.1:9052"); ws.onopen = function(){ console.log("握手成功"); var myemail = $("#myemail").val(); var toemail = $("#toemail").val(); var arr = {"flag":"init","from":myemail,"to":toemail}; var str = JSON.stringify(arr); ws.send(str); }; ws.onmessage = function(e){ var toemail = $("#toemail").val(); var toavatar = $("#toavatar").val(); var obj = JSON.parse(e.data); console.log(e.data); //但同时与两个人聊天时,可能两个人的消息都会出现在当前窗口,所以此处加个判断,此窗口只接收当前聊天对象的消息,其他则忽略 if(obj.from === toemail){ var ans = '<div class="answer"><div class="heard_img left"><img src="'+toavatar+'"></div>'; ans += '<div class="answer_text"><p>'+obj.content+'</p><i></i>'; ans += '</div></div>'; $('.speak_box').append(ans); for_bottom(); } }; ws.onerror = function(){ console.log("error"); var str = '<div class="question">'; str += '<div class="heard_img right"><img src="/static/images/xitong.jpg"></div>'; str += '<div class="question_text clear"><p>聊天服务器出现异常,暂时无法提供服务。</p><i></i>'; str += '</div></div>'; $('.speak_box').append(str); $('.write_box input').val(''); $('.write_box input').focus(); autoWidth(); for_bottom(); }; function send() { var content = $('.write_box input').val(); if(content === ''){ alert('请输入消息!'); $('.write_box input').focus(); }else{ var toemail = $("#toemail").val(); var myemail = $("#myemail").val(); var myavatar = $("#myavatar").val(); var arr = {"flag":"msg","to":toemail,"from":myemail,"content":content}; var msg = JSON.stringify(arr); console.log(msg); ws.send(msg); var str = '<div class="question">'; str += '<div class="heard_img right"><img src="'+myavatar+'"></div>'; str += '<div class="question_text clear"><p>'+content+'</p><i></i>'; str += '</div></div>'; $('.speak_box').append(str); $('.write_box input').val(''); $('.write_box input').focus(); autoWidth(); for_bottom(); } } }else{ alert("您的浏览器不支持 WebSocket!"); } function for_bottom(){ var speak_height = $('.speak_box').height(); $('.speak_box,.speak_window').animate({scrollTop:speak_height},500); } function autoWidth(){ $('.question_text').css('max-width',$('.question').width()-60); } autoWidth(); </script> </body> </html>
数据表结构:
CREATE TABLE `app_offline` ( `id` int(11) NOT NULL AUTO_INCREMENT, `from` varchar(50) DEFAULT NULL COMMENT '离线发送方', `to` varchar(50) DEFAULT NULL COMMENT '离线接收方', `content` varchar(1000) DEFAULT NULL COMMENT '发送的离线内容', `status` tinyint(4) DEFAULT '0' COMMENT '发送状态:0-未发送,1-已发送', `addtime` int(11) DEFAULT NULL COMMENT '发送方发送时间', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8
具体效果:
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持易盾网络。

