PHP用swoole+websocket和redis实现web一对一聊天
Redis实现每个连接websocket的服务都唯一绑定一个用户。通过用户账号=websocketfd存到redis中。
Mysql实现离线消息池。如果一个用户不在线,则其他用户发送给他的消息暂时存储在mysql。待该用户上线时,再从离线消息池取出发送。
具体参考代码和相应注释:
connect('127.0.0.1',6379); $db=newmysqli('127.0.0.1','test','test','thinkphp5'); $server->on('open',function(swoole_websocket_server$server,$request){ echo"server:handshakesuccesswithfd{$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`,contentFROMthinkphp5.app_offlineWHERE`to`='{$data['from']}'AND`from`='{$data['to']}'AND`status`='0'ORDERBYaddtimeASC;"; if($result=$GLOBALS['db']->query($sql)){ $re=array(); while($row=$result->fetch_assoc()){ array_push($re,$row); } $result->free(); foreach($reas$content){ $content=json_encode($content); $server->push($frame->fd,$content); } //设置消息池中的消息为已发送 $sql="UPDATEthinkphp5.app_offlineSET`status`=1WHERE`to`='{$data['from']}'AND`from`='{$data['to']}';"; $GLOBALS['db']->query($sql); } }elseif($data['flag']=='msg'){ //非初始化的信息发送,一对一聊天,根据每个用户对应的fd发给特定用户 $tofd=$GLOBALS['redis']->get($data['to']);//消息要发给谁 $fds=[];//所有在线的用户(打开聊天窗口的用户) foreach($server->connectionsas$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="INSERTINTOthinkphp5.app_offline(`to`,`from`,`content`,`status`,`addtime`)VALUES('{$data['to']}','{$data['from']}','{$data['content']}','0','{$time}');"; $GLOBALS['db']->query($sql); } }elseif($data['flag']=='group'){ //todo群聊 }elseif($data['flag']=='all'){ //全站广播 foreach($server->connectionsas$fd){ $server->push($fd,$data); } } }); $server->on('close',function($ser,$fd){ echo"client{$fd}closed\n"; }); $server->start();
客户端代码:
XST-app body{background:url(/static/images/yuyin_bg.jpg);background-size:100%;} @mediaalland(min-width:640px){ body,html,.wenwen-footer,.speak_window{width:640px!important;margin:0auto} .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:solid1px#ddd;box-sizing:border-box;} .wenwen_btn,.wenwen_help{width:15%;text-align:center;} .wenwen_btnimg,.wenwen_helpimg{height:40px;} .wenwen_text{height:40px;border-radius:5px;border:solid1px#636162;box-sizing:border-box;width:66%;text-align:center;overflow:hidden;margin-left:2%;} .circle-button{padding:05px;} .wenwen_text.circle-button{font-size:14px;color:#666;line-height:38px;} .write_box{background:#fff;width:100%;height:40px;line-height:40px;} .write_boxinput{height:40px;padding:05px;line-height:40px;width:100%;box-sizing:border-box;border:0;} .wenwen_helpbutton{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_imgimg{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_textp,.answer_textp{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_textp{background:#fff;} .question_textp{background:#42929d;color:#fff;text-align:left;} .question_texti,.answer_texti{width:0;height:0;border-top:5pxsolidtransparent;border-bottom:5pxsolidtransparent;position:absolute;top:25px;} .answer_texti{border-right:10pxsolid#fff;left:10px;} .question_texti{border-left:10pxsolid#42929d;right:10px;} .answer_textpa{color:#42929d;display:inline-block;} .write_list{position:absolute;left:0;width:100%;background:#fff;border-top:solid1px#ddd;padding:5px;line-height:30px;} 与{$tonickname}的聊天