PHP用swoole+websocket和redis实现web一对一聊天
发布:smiling 来源: PHP粉丝网 添加日期:2022-01-21 20:36:56 浏览: 评论:0
这篇文章主要介绍了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 style="opacity:0;" 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
具体效果:
Tags: swoole websocket redis
- 上一篇:php实现JWT(json web token)鉴权实例详解
- 下一篇:最后一页
相关文章
- ·php异步多线程swoole使用实例程序(2015-04-15)
- ·PHP程序员学习使用Swoole的理由(2018-09-14)
- ·swoole_process父子进程管道通信的代码示例(2020-01-16)
- ·swoole事件处理流程(2020-04-06)
- ·Swoole协程与传统fpm同步模式比较(2020-04-09)
- ·PHP框架Swoole定时器Timer特性分析(2021-04-05)
- ·php异步多线程swoole用法实例(2021-04-26)
- ·使用php+swoole对client数据实时更新(一)(2021-07-04)
- ·PHP+swoole实现简单多人在线聊天群发(2021-07-06)
- ·基于 Swoole 的微信扫码登录功能实现代码(2021-08-30)
- ·基于swoole实现多人聊天室(2021-10-01)
- ·swoole_process实现进程池的方法示例(2021-11-01)
- ·Swoole实现异步投递task任务案例详解(2021-11-14)
- ·PHP扩展Swoole实现实时异步任务队列示例(2021-11-17)
- ·PHP+swoole+linux实现系统监控和性能优化操作示例(2021-11-17)
- ·Swoole4.4协程抢占式调度器详解(2021-11-23)
推荐文章
热门文章
最新评论文章
- 写给考虑创业的年轻程序员(10)
- PHP新手上路(一)(7)
- 惹恼程序员的十件事(5)
- PHP邮件发送例子,已测试成功(5)
- 致初学者:PHP比ASP优秀的七个理由(4)
- PHP会被淘汰吗?(4)
- PHP新手上路(四)(4)
- 如何去学习PHP?(2)
- 简单入门级php分页代码(2)
- php中邮箱email 电话等格式的验证(2)