基础介绍
(1)Swoole是一个面向生产环境的 PHP 异步网络通信引擎,使 PHP 开发人员可以编写高性能的异步并发 TCP、UDP、Unix Socket、HTTP,WebSocket 服务。Swoole 可以广泛应用于互联网、移动通信、企业软件、云计算、网络游戏、物联网(IOT)、车联网、智能家居等领域。 使用 PHP + Swoole 作为网络通信框架,可以使企业 IT 研发团队的效率大大提升。—百度百科
(2)WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket通信协议于2011年被IETF定为标准RFC 6455,并由RFC7936补充规范。WebSocket API也被W3C定为标准。
WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。 —百度百科
(3)那这篇的主题就是如何使用Swoole+WebSocket实现一个简易的聊天室。
熟悉网络通信协议的同学肯定不会陌生。
功能需求及问题处理
web端:(1)每次刷新都会生成一个唯一的ID(id值从1开始).(2)第一次进入网站时会要求用户设置昵称并会与ID进行绑定。问题点:(1)刷新页面后用户标志(ID)会重新生成,之前生成ID被弃用。(2)WebSocket生成了新的用户ID,但是跟现在的无法形成关联关系。
server端:(1)当用户进入聊天室后,发送广播给所有人并加入聊天群组(使用redis存储)。(2)当用户退出直播间后,发送广播给所有人并清除该用户的记录。(3)用户每发送一次消息都要形成新的记录广播给所有人。(4)用户生成新的昵称后把昵称推送给他。
web端问题处理方法:(1)浏览器刷新时提醒用户刷新即将重新获得新的身份。(2)用户连接成功后记录用户name,每次连接把这个name带上,清除之前该name的绑定关系,形成新的关系。
php实现代码:
<?php
include "RedisManager.php";
$server = new Swoole\WebSocket\Server("0.0.0.0",8877);
//客户端连接
$server->on('open', function (SwoolelWebSocketiServer $server, $request) {
//连接成功把当前在线的用户返回
$user_list = RedisConnectgetRedis()->hGetAll('message:user');
alone($server, $request->fd,[
'type'=>'first',
'data'=>$user_list
]);
});
//接收客户端发送的消息
$server->on('message' , function (Swoole\WebSocket\Server $server, $frame){
$message_data = json_decode($frame->data, true);
$type = $message_data['type'];
$data = $message_data['user_name'];
if (isset($message_data['send_message'])){
$user_message = $message_data['send_message'];
}
switch ($type) {
case 'save_user':
$new_name = '大夏单子'.$frame->fd.'号';
RedisConnect::getRedis()->hset('message:user',$frame->fd,$new_name);
//把生成的用户昵称返回给他
alone($server, $frame->fd,[
'type' => 'new_name',
'name' => $new_name,
'id' => $frame->fd
]);
//广播消息给其他用户
groupSending($server, [
'type' => 'open',
'name' => $new_name,
'id' => $frame->fd,
]);
break;
case 'send_message':
$msg = [
'id' => $frame-> fd,
'user_name' => $data,
'message' => $user_message,
'type' => 'message'
];
//接受用户发送的消息
RedisConnect::getRedis()->lpush('message:user:say', $msg);
groupSending($server, $msg);
break;
}
});
//退出聊天室
$server->on('close' , function ($ser, $fd){
$user = RedisConnect::getRedis()->hget('message:user',$fd);
RedisConnect::getRedis()->hdel('message:user', $fd);
$msg = [
'id' => $fd,
'user_name' => $user,
'message' => '退出聊天室',
'type' => 'close'
];
groupSending($ser, $msg, $fd);
});
//开启服务
$server->start();
//群发消息
function groupSending($server, $msg, $self = null){
foreach ($server->connections as $conn){
if ($conn == $self) break;
//不再推送给当前退出的用户
$server->push($conn, json_encode($msg));
}
}
//单独发消息
function alone($server,$fd, $msg){
$server->push($fd, json_encode($msg));
}
html代码:
<IDOCTYPE html> <html> <head> <meta charset=UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>聊天室</title> <style> *{margin:0;padding:0;).box{width:602px;height:500px;margin:20px auto}.body{width:500px;height:500px;border:1px solid #333;padding:20px;box-sizing:border-box;float:right}.title{text-align:center;margin-top:10px}.msg{margin-top:20px;width:100%;text-align:left;}.message_box{width:100%;height:90%;margin-top:10px;font-size:12px;overflow:hidden;overflow-y:auto;}.name{font-weight:bold;}.right{text-align:right;}.prompt{width:600px;margin:0 auto}.live{width:100px;height:500px;float:left;border:1px solid #333;font-size:12px;text-align:center}.Iive-box{height:100%;overflow:hidden;overflow-y:auto}.Ilive-box p{margin-top:10px;} </style> </head> <body> <div> <div> <div>+十十++十+十+++聊天室++十十+十++++++</div> <div id="test"></div> </div> <div> <div>在线人数</div> <div></div> </div> </div> <div> <input type="text" name="say"> <input type="button" value="发送" id="send"> <!--<input type="button" value="增加消息" id="add" > --> </div> </body> <script src="jquery.js"></script> <script src='//cdn.bootcss.com/socket.io/1.3.7/socket.io.js'></script> <script src='https://cdn.bootcss.com/socket.io/2.0.3/socket.io.js'></script> <script type="text/javascript"> window.onbeforeunload = function (event){ window.localStorage.clear(); }; $(function(){ window.user_name = ''; //获取在线用户 if(!("WebSocket" in window)){ alert("您的浏览器不支持WebSocketl"); return false; } //连接 let ws = new WebSocket("ws://49.235.172.110:8877");//连接成功 ws.onopen = function (event){ //修改昵称 let user_info = { 'type' : 'save_user', 'user_name': '', }; ws.send(JSON.stringify(user_info)); console.log('连接成功','修改昵称成功'); }; ws.onmessage = function (evt) { let received_msg = JSON.parse(evt.data); let type = received_msg['type']; switch (type){ case 'message': add_msg(received_msg); break; case 'close': $(".Jive-box").find('p[user='+ received_msg['id'] + ']').remove(); break; case 'connect': console.log(received _msg); break; case 'new_name': user_name = received_msg[ 'name']; break; case 'open': $('.live-box').append('<p user="' + received_msg['id']+'">'+ received_msg['name'] + '</p>'); break; case 'first': let data = received_msg['data'];let str = "; $.each(data, function (k, v){ if (isNaN(v)) { str += '<p user="' + k + '">' + v + '</p>'; } }) $('.live-box').append(str); break; } } //发送消息 $("#send").click(function () { send_msg(); }) $("input[name=say]").bind("keydown", function (e){ var theEvent = e || window.event; var code = theEvent.keyCode || theEvent.which || theEvent.charCode; if (code == 13) { send_msg(); } }); //发送消息 function send_msg() { var message = $(input[name=say]).val(); if (message !== '' ){ var arr = { 'type': 'send_message', 'user_ name': user_name, 'send_message': message } } ws.send(JSON.stringify(arr)); $('input[name=say]').val(''); var ele = document.getElementById("test"); if (ele.scrollHeight > ele.clientHeight) { ele.scrollTop = ele.scrollHeight + 100; } } //增加消息 function add_msg(data){ let str ='<div>'; str += '<span>'+ data['user_name'] + '</span>:'; str += '<span>'+ data['message'] + '</span> </div>'; $('.message_box').append(str); } }) </script> </html>
这篇文章只是简单的介绍前后端如何实现通信,很多的细节问题没有进行处理。UI比较low,可以按照自己需求进行修改…
发表评论 取消回复