温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

PHP如何使用swoole+websocket和redis实现web一对一聊天

发布时间:2021-06-04 10:55:58 来源:亿速云 阅读:152 作者:小新 栏目:开发技术

这篇文章给大家分享的是有关PHP如何使用swoole+websocket和redis实现web一对一聊天的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。

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 http-equiv="Content-Type" content="text/html; charset=utf-8" />  <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" />  <meta name="viewport" content="width=device-width, initial-scale=0.0, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0" />  <meta name="keywords" content="test" />  <meta name="description" content="test" />  <meta name="author" content="XST-APP" />  <meta content="yes" name="apple-mobile-web-app-capable" />  <meta content="black" name="apple-mobile-web-app-status-bar-style" />  <meta content="telephone=no" name="format-detection" />   <style type="text/css">  body{background:url(/static/images/yuyin_bg.jpg);background-size:100%;}  @media all and (min-width: 640px) {   body,html,.wenwen-footer,.speak_window{width:640px!important;margin:0 auto}   .speak_window,.wenwen-footer{left:50%!important;margin-left:-320px}  }  input,button{outline:none;}  .wenwen-footer{width:100%;position:fixed;bottom:-5px;left:0;background:#fff;padding:3%;border-top:solid 1px #ddd;box-sizing:border-box;}  .wenwen_btn,.wenwen_help{width:15%;text-align:center;}  .wenwen_btn img,.wenwen_help img{height:40px;}  .wenwen_text{height:40px;border-radius:5px;border:solid 1px #636162;box-sizing:border-box;width:66%;text-align:center;overflow:hidden;margin-left:2%;}  .circle-button{padding:0 5px;}  .wenwen_text .circle-button{font-size:14px;color:#666;line-height:38px;}  .write_box{background:#fff;width:100%;height:40px;line-height:40px;}  .write_box input{height:40px;padding:0 5px;line-height:40px;width:100%;box-sizing:border-box;border:0;}  .wenwen_help button{width:95%;background:#42929d;color:#fff;border-radius:5px;border:0;height:40px;}  #wenwen{height:100%;}  .speak_window{overflow-y:scroll;height:100%;width:100%;position:fixed;top:50px;left:0;}  .speak_box{margin-bottom:70px;padding:10px;}  .question,.answer{margin-bottom:1rem;}  .question{text-align:right;}  .question>div{display:inline-block;}  .left{float:left;}  .right{float:right;}  .clear{clear:both;}  .heard_img{height:60px;width:60px;border-radius:5px;overflow:hidden;background:#ddd;}  .heard_img img{width:100%;height:100%}  .question_text,.answer_text{box-sizing:border-box;position:relative;display:table-cell;min-height:60px;}  .question_text{padding-right:20px;}  .answer_text{padding-left:20px;}  .question_text p,.answer_text p{border-radius:10px;padding:.5rem;margin:0;font-size:14px;line-height:28px;box-sizing:border-box;vertical-align:middle;display:table-cell;height:30px;word-wrap:break-word;}  .answer_text p{background:#fff;}  .question_text p{background:#42929d;color:#fff;text-align:left;}  .question_text i,.answer_text i{width:0;height:0;border-top:5px solid transparent;border-bottom:5px solid transparent;position:absolute;top:25px;}  .answer_text i{border-right:10px solid #fff;left:10px;}  .question_text i{border-left:10px solid #42929d;right:10px;}  .answer_text p a{color:#42929d;display:inline-block;}  .write_list{position:absolute;left:0;width:100%;background:#fff;border-top:solid 1px #ddd;padding:5px;line-height:30px;}   </style> </head> <body> <div id="header" class="head">   <div class="wrap">     <i class="menu_back"><a href="javascript:history.go(-1);" rel="external nofollow" ></a></i>     <div class="title">       <span class="title_d"><p>与 {$tonickname} 的聊天</p></span>       <div class="clear"></div>     </div>     <!--i class="menu_share"></i-->   </div> </div> <input type="hidden" name="myemail" id="myemail" value="{$myemail}" /> <input type="hidden" name="mynickname" id="mynickname" value="{$mynickname}" /> <input type="hidden" name="myavatar" id="myavatar" value="{$myavatar}" /> <input type="hidden" name="toemail" id="toemail" value="{$toemail}" /> <input type="hidden" name="tonickname" id="tonickname" value="{$tonickname}" /> <input type="hidden" name="toavatar" id="toavatar" value="{$toavatar}" /> <!-- 对话内容 --> <div class="speak_window">  <div class="speak_box">  </div> </div> <!-- 内容输入--> <div class="wenwen-footer">  <div class="wenwen_btn left"><img src="/static/images/jp_btn.png"></div>  <div class="wenwen_text left">   <div class="write_box"><input type="text" class="left" onKeyUp="keyup()" maxlength="100" placeholder="请输入信息(100字以内)..." /></div>   </div>  <div class="wenwen_help right">    <button onClick="send()" class="right">发送</button>  </div>  <div  class="clear"></div> </div> <script type="text/javascript">  if ("WebSocket" in window){   var ws = new WebSocket("ws://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

具体效果:

PHP如何使用swoole+websocket和redis实现web一对一聊天

感谢各位的阅读!关于“PHP如何使用swoole+websocket和redis实现web一对一聊天”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,让大家可以学到更多知识,如果觉得文章不错,可以把它分享出去让更多的人看到吧!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI