websocket 封装类
后端PHP代码
<?php
class MySQLPool {
private $serv = '';
private $mysql = '';
private $teacher_openid = 'oKMfe1QTsNbzpqslEr-U1b7q8rMQ'; // 老师的微信位移标识
private $teacher_fd = ''; // 老师的文件描述符
public function __construct() {
// 创建socket套接字
$this->serv = new \swoole_websocket_server("0.0.0.0", 9502);
$this->serv->set(array(
'worker_num' => 1, //worker进程数量
'task_worker_num' => 1, //task进程数量 即为维持的MySQL连接的数量
'reactor_num'=>8,
'dispatch_mode' => 2,
'debug_mode'=> 1,
'daemonize' => 0, // 大于等于1 进程后台运行
// 'log_file' => __DIR__.'/log/webs_swoole.log',
'heartbeat_check_interval' => 60, // 表示每60秒,遍历所有连接,如果该连接在60秒内,没有向服务器发送任何数据,此连接将被强制关闭。
'heartbeat_idle_time' => 600,
));
// 绑定监听函数
$this->serv->on('WorkerStart', array($this, 'onWorkerStart'));
$this->serv->on('Open', array($this, 'onOpen'));
$this->serv->on('Connect', array($this, 'onConnect'));
$this->serv->on('Receive', array($this, 'onReceive'));
$this->serv->on('Message', array($this, 'onMessage'));
$this->serv->on('Close', array($this, 'onClose'));
// bind callback
$this->serv->on('Task', array($this, 'onTask'));
$this->serv->on('Finish', array($this, 'onFinish'));
$this->serv->start();
}
// MySQL链接数据库
public function conn_mysql(){
$mysql = new Swoole\Coroutine\MySQL();
$res = $mysql->connect([
'host' => '127.0.0.1',
'user' => 'root',
'password' => 'Bszpassword888',
'database' => 'bsz',
]);
#3
if ($res == false) {
echo("MySQL connect fail!");
return;
}
$this->mysql = $mysql;
return $mysql;
}
// 数据库插入操作
public function my_insert($tbName, $data){
$sql = "insert into ".$tbName."(".implode(',',array_keys($data)).") values(".implode(',',array_values($data)).")";
echo PHP_EOL;
echo $sql;
echo PHP_EOL;
return $this->mysql->query($sql);
}
public function onWorkerStart( $serv , $worker_id) {
echo "workerstart: ".$worker_id.PHP_EOL;
}
// 用户连接服务器响应函数
public function onOpen($serv, $data){
print_r($data->fd);
echo PHP_EOL;
// 获取老师fd
if($this->teacher_openid == $data->get['openid']){
$this->teacher_fd = $data->fd;
// echo '我是老师: ' . $this->teacher_fd;
}
$mysql = $this->conn_mysql();
$chat_date = date('Y-m-d', time());
$sql = "select * from bsz_chat_room where create_time >= $chat_date";
$messages = $mysql->query($sql);
// print_r($messages);
$mysql->close();
// 获取历史聊天消息记录
if(!empty($messages)){
$m_data = [];
$m_data['type'] = 'history_msg';
$m_data['fd'] = $data->fd;
$m_data['messages'] = $messages;
// $serv->task($m_data);
}
}
public function onConnect( $serv, $fd, $from_id ) {
echo "client:$fd Connect.".PHP_EOL;
}
public function onReceive( swoole_server $serv, $fd, $from_id, $data ) {
echo "receive#{$from_id}: receive $data ".PHP_EOL;
}
// 用户发送消息响应函数
public function onMessage($serv, $frame) {
echo "message: ".$frame->data.PHP_EOL;
$data = json_decode($frame->data, true);
if($data['type'] == 'heart'){ //用户发送心跳包
// 什么也不干,只是证明此用户还在聊天室, 保持长连接不中断
}else if($data['type'] == 'speak'){ // 用户发送消息
$create_time = date('Y-m-d H:i:s', time());
$msg_id = date('Ymdhis') . str_pad(mt_rand(1, 99999), 5, '0', STR_PAD_LEFT);
// 判断消息发送者身份
if($data['msg']['openid'] == $this->teacher_openid){
$identity = '1';
$origin_identity = '1';
}else{
$identity = '2';
$origin_identity = '2';
}
$sql = "INSERT INTO `bsz`.`bsz_chat_room` (`openid`, `nickname`, `headimgurl`, `speak`, `identity`, `origin_identity`, `create_time`, `msg_id`) VALUES ('" . $data['msg']['openid']."', '". $data['msg']['nickname'] ."', '". $data['msg']['headimgurl'] ."', '". $data['msg']['speak'] ."', '$identity', '$origin_identity', '" . $create_time . "','". $msg_id ."')";
$mysql = $this->conn_mysql();
$ret = $mysql->query($sql);
echo("swoole response is ok, result=".var_export($ret, true));
// 关闭mysql连接,释放出连接数资源
$mysql->close();
$data['fd'] = $frame->fd;
$data['msg_id'] = $msg_id;
$data['teacher_openid'] = $this->teacher_openid;
$data['teacher_fd'] = $this->teacher_fd;
$data['identity'] = $identity;
$data['origin_identity'] = $origin_identity;
$serv->task($data);
}else if($data['type'] == 'replay'){ // 老师回复用户消息
$msg_id = $data['msg']['msg_id'];
$sql = "select * from `bsz_chat_room` where msg_id='$msg_id' ";
$mysql = $this->conn_mysql();
$msg = $mysql->query($sql);
// 关闭mysql连接,释放出连接数资源
$mysql->close();
$msg[0]['type'] = 'replay';
$data['msg_id'] = $msg[0]['msg_id'];
$data['msg'] = $msg[0]['msg'];
$data['openid'] = $msg[0]['openid'];
$data['nickname'] = $msg[0]['nickname'];
$data['headimgurl'] = $msg[0]['headimgurl'];
$serv->task($data);
}
}
public function onClose( $serv, $fd, $from_id ) {
echo "Client {$fd} close connection\n";
// $mysql = new Swoole\Coroutine\MySQL();
// print_r($mysql);
}
public function onTask($serv,$task_id,$from_id, $data) {
$msgArr = [];
$msgStr = '';
switch ($data['type']){
case 'login':
$msgArr['msg'] = '我来了~';
$msgArr['nickname'] = $data['msg']['nickname'];
$msgArr['headimgurl'] = $data['msg']['headimgurl'];
$msgArr['user'] = 'login';
$msgStr = json_encode($msgArr);
// $send_msg = $msgStr;
break;
case 'history_msg':
foreach ($data['messages'] as $key => $value) {
$msgArr['msg'] = $value['speak'];
$msgArr['nickname'] = $value['nickname'];
$msgArr['headimgurl'] = $value['headimgurl'];
$msgArr['user'] = 'teacher';
$msgStr = json_encode($msgArr);
$serv->push($data['fd'], $msgStr);
}
break;
case 'speak':
$msgArr['msg_id'] = $data['msg_id'];
$msgArr['msg'] = $data['msg']['speak'];
// $msgArr['openid'] = $data['msg']['openid'];
$msgArr['nickname'] = $data['msg']['nickname'];
$msgArr['headimgurl'] = $data['msg']['headimgurl'];
if($data['origin_identity'] == '1'){ // 老师发送的消息
$msgArr['user'] = 'teacher';
}else if($data['origin_identity'] == '2'){ // 粉丝发送的消息
$msgArr['user'] = 'fans';
}
// 老师发送的消息需要发送给所有人, 自己除外
if($data['teacher_openid'] == $data['teacher_openid']){
$msgStr = json_encode($msgArr);
foreach ($serv->connections as $conn){
if($conn != $data['fd']){ // 不推送消息给自己
$serv->push($conn, $msgStr);
}
}
}else{ // 其他人发送的消息,只需要转发给老师
// 添加粉丝发送消息标志
$msgStr = json_encode($msgArr);
$serv->push($data['teacher_fd'], $msgStr);
}
break;
case 'replay':
$msgArr['user'] = 'replay';
$msgArr['msg_id'] = $data['msg_id'];
$msgArr['msg'] = $data['speak'];
$msgArr['openid'] = $data['openid'];
$msgArr['nickname'] = $data['nickname'];
$msgArr['headimgurl'] = $data['headimgurl'];
$msgStr = json_encode($msgArr);
foreach ($serv->connections as $conn){
if($conn != $this->teacher_fd){ // 回复的消息不用再次推送给老师
$serv->push($conn, $msgStr);
}
}
break;
default:
break;
}
return;
}
public function onFinish($serv,$task_id, $data) {
return true;
}
}
new MySQLPool();
前端代码
<script type="text/javascript">
// 一进来就滚动到底部
// $("#container").scrollTop($("#container")[0].scrollHeight);
// $("#btnSubmit").click(function(){
// //如果没有内容就return
// var txtVal=$("#msgInput").val();
// // alert(txtVal);
// if(txtVal==''){
// return false;
// }
// //有内容继续
// $('#msgList').append('<li>'+txtVal+'</li>');
// $("#container").scrollTop($("#container")[0].scrollHeight);
// })
// 用户基本信息(头像,昵称)
var openid = "<?php echo $userinfo['openid'] ?>";
var nickname = "<?php echo $userinfo['nickname'] ?>";
var headimgurl = "<?php echo $userinfo['headimgurl'] ?>";
// https://bszedu.com/static/public/images/qq.png
// websocket连接地址
// var wsUri ="wss://127.0.0.1:9502/";
var wsUri ="wss://bszedu.com/websocket?openid=" + openid;
// JS初始化函数
function init() {
// 三次握手连接服务器
websocket = new WebSocket(wsUri);
websocket.onopen = function(evt) {
onOpen(evt)
};
// 用户断开连接
websocket.onclose = function(evt) {
onClose(evt)
};
// 客户端接收到服务器消息
websocket.onmessage = function(evt) {
onMessage(evt)
};
// socket长连接出错
websocket.onerror = function(evt) {
onError(evt)
};
}
// 获取用户需要发送的消息
function get_speak_msg(){
// var name = $("#name").val();
// var nickname = nickname;
var speak = $("#content").val();
var arr_msg = {"nickname":nickname, "speak":speak, "headimgurl":headimgurl, 'openid':openid};
return JSON.stringify(arr_msg);
}
// 打包用户消息 JSON格式
function pack_msg(type, msg){
return '{"type":"'+type+'","msg":'+msg+'}';
// var arr_msg = {"type":type, "msg":msg};
// return JSON.stringify(arr_msg);
}
// 客户端连接服务器
function onOpen(evt) {
var msgStr = '{"msg":"欢迎来到聊天室.........", "user":"welcome", "nickname":"' + nickname +'", "headimgurl":"' + headimgurl + '"}';
// var a = JSON.stringify(arr_msg);
append_speak(msgStr);
// append_speak("已经联通服务器.........");
// speak_msg = get_speak_msg();
// send_msg = pack_msg("login", speak_msg);
// doSend(send_msg);
}
function onClose(evt) {
var msgStr = '{"msg":"连接已断开!", "who":"me", "nickname":"' + nickname +'", "headimgurl":"' + headimgurl + '"}';
append_speak(msgStr);
// append_speak("俺老孙去也!");
}
function onMessage(evt) {
append_speak(evt.data);
}
function onError(evt) {
alert(evt.data);
}
function doSend(message) {
websocket.send(message);
}
function append_speak(msgStr){
var data = JSON.parse(msgStr); //由JSON字符串转换为JSON对象
// 获取当前时间
var myDate = new Date();
var msg_hour = myDate.getHours();
var msg_minute = myDate.getMinutes();
msg_hour = msg_hour<10 ? '0'+msg_hour:msg_hour;
msg_minute = msg_minute<10 ? '0'+msg_minute:msg_minute;
var chat_time = msg_hour + ':' + msg_minute;
// 老师的消息
if(data['user'] == 'teacher'){
var msg = '<li class="live-con-lis">' +
'<img src="' + data.headimgurl + '" alt="" class="live-con-img">' +
'<div class="live-con-name">' +
'<p><span class="live-con-user">' + data.nickname + '</span><span class="live-con-time">'+ chat_time +'</span></p>' +
'<span class="live-con-text">' + data.msg + '</span>' +
// '<a href="#" class="reply">回复用户></a>' +
// '<a href="javascript:;" class="reply" onclick="replay_client(this);" data-msg-id="'+ data.msg_id +'">回复用户></a>' +
'</div>' +
'</li>';
// 添加消息到聊天窗
$('#chat-msg-ul').append(msg);
// 消息滚动到底部
$(".live-con").scrollTop($(".live-con")[0].scrollHeight);
}else if(data['user'] == 'fans'){ // 粉丝的消息
var msg = '<li class="live-reviewer">' +
'<div class="live-reviewer-name">' +
'<p style="text-align: right"><span class="live-reviewer-user">'+ data.nickname+'</span><span class="live-reviewer-time">'+ chat_time +'</span></p>' +
'<div class="live-reviewer-text">' +
'<span class="live-reviewer-ask">'+ data.msg +'</span>' +
'<a href="javascript:;" class="reply" onclick="replay_client(this);" data-msg-id="'+ data.msg_id +'">回复用户></a>' +
'</div>' +
'</div>' +
'<img src="'+ data.headimgurl +'" alt="" class="live-reviewer-img">' +
'</li>';
// 添加消息到聊天窗
$('#chat-msg-ul').append(msg);
// 消息滚动到底部
$(".live-con").scrollTop($(".live-con")[0].scrollHeight);
}else if(data['user'] == 'reply'){ // 用户成功连接服务器提示
// 判断老师 和 信息发送者的 openID
if(openid != data['openid']){ // 老师回复的消息,不用再次显示在自己屏幕
var msg = '<li class="live-reviewer">' +
'<div class="live-reviewer-name">' +
'<p style="text-align: right"><span class="live-reviewer-user">'+ data.nickname+'</span><span class="live-reviewer-time">'+ chat_time +'</span></p>' +
'<div class="live-reviewer-text">' +
'<span class="live-reviewer-ask">'+ data.msg +'</span>' +
// '<a href="javascript:;" class="reply" onclick="replay_client(this);" data-msg-id="'+ data.msg_id +'">回复用户></a>' +
'</div>' +
'</div>' +
'<img src="'+ data.headimgurl +'" alt="" class="live-reviewer-img">' +
'</li>';
// 添加消息到聊天窗
$('#chat-msg-ul').append(msg);
// 消息滚动到底部
$(".live-con").scrollTop($(".live-con")[0].scrollHeight);
}
}else if(data['user'] == 'welcome'){
var msg = '<li class="live-reviewer">' +
'<div class="live-reviewer-name">' +
'<p style="text-align: right"><span class="live-reviewer-user">'+ data.nickname+'</span><span class="live-reviewer-time">'+ chat_time +'</span></p>' +
'<div class="live-reviewer-text">' +
'<span class="live-reviewer-ask">'+ data.msg +'</span>' +
'</div>' +
'</div>' +
'<img src="'+ data.headimgurl +'" alt="" class="live-reviewer-img">' +
'</li>';
// 添加消息到聊天窗
$('#chat-msg-ul').append(msg);
// 消息滚动到底部
$(".live-con").scrollTop($(".live-con")[0].scrollHeight);
}
// document.getElementById("message").value=$("#message").val()+new_msg+"\n";
// document.getElementById('message').scrollTop = document.getElementById('message').scrollHeight;
}
// 老师回复用户消息
function replay_client(obj){
var msg_id = $(obj).attr('data-msg-id');
alert(msg_id);
var arr_msg = {"msg_id":msg_id};
arr_msg = JSON.stringify(arr_msg);
send_msg = pack_msg("replay", arr_msg);
doSend(send_msg);
// alert(msg_id);
// console.log(msg_id);
}
function speak_to_all(){
// 获取当前时间
var myDate = new Date();
var msg_hour = myDate.getHours();
var msg_minute = myDate.getMinutes();
msg_hour = msg_hour<10 ? '0'+msg_hour:msg_hour;
msg_minute = msg_minute<10 ? '0'+msg_minute:msg_minute;
var chat_time = msg_hour + ':' + msg_minute;
// 获取消息输入框内容后, 清空消息输入框
// var msg_con = $("#content").val();
//如果没有内容就return
var msg_con = $("#content").val();
// alert(txtVal);
if(msg_con==''){
return false;
}
$("#content").val() == ""
// 显示自己说的话到聊天窗
if(openid == 'oKMfe1QTsNbzpqslEr-U1b7q8rMQ'){
var msg = '<li class="live-con-lis">' +
'<img src="' + headimgurl + '" alt="" class="live-con-img">' +
'<div class="live-con-name">' +
'<p><span class="live-con-user">' + nickname + '</span><span class="live-con-time">'+ chat_time +'</span></p>' +
'<span class="live-con-text">' + msg_con + '</span>' +
// '<a href="#" class="reply">回复用户></a>' +
'</div>' +
'</li>';
}else{
var msg = '<li class="live-reviewer">' +
'<div class="live-reviewer-name">' +
'<p style="text-align: right"><span class="live-reviewer-user">'+ nickname+'</span><span class="live-reviewer-time">' + chat_time + '</span></p>' +
'<div class="live-reviewer-text">' +
'<span class="live-reviewer-ask">'+ msg_con +'</span>' +
// '<a href="#" class="reply">回复用户></a>' +
'</div>' +
'</div>' +
'<img src="'+ headimgurl +'" alt="" class="live-reviewer-img">' +
'</li>';
}
// 添加消息到聊天窗
$('#chat-msg-ul').append(msg);
// 消息滚动到底部
$("#live-con").scrollTop($("#live-con")[0].scrollHeight);
// 打包消息发送到服务器
var send_msg = pack_msg("speak", get_speak_msg());
doSend(send_msg);
}
init();
// 定时发送心跳包
//循环执行,每隔1秒钟执行一次 1000
var my_heart=window.setInterval(heartbeat, 50000);
function heartbeat() {
// console.log("ready");
// 打包消息发送到服务器
var send_msg = pack_msg("heart", get_speak_msg());
doSend(send_msg);
}
//去掉定时器的方法
// window.clearInterval(my_heart);
</script>
案例demo
<?php
// 创建socket套接字
$serv = new swoole_websocket_server("0.0.0.0", 9502);
//服务的基本设置
$serv->set(array(
'worker_num' => 5, //worker进程数量
'task_worker_num' => 5, //task进程数量 即为维持的MySQL连接的数量
'reactor_num'=>8,
'dispatch_mode' => 2,
'debug_mode'=> 1,
'daemonize' => 1, // 大于等于1 进程后台运行
'log_file' => __DIR__.'/log/'. date('Y-m-d', time()) .'.webs_swoole.log',
'heartbeat_check_interval' => 60, // 表示每60秒,遍历所有连接,如果该连接在60秒内,没有向服务器发送任何数据,此连接将被强制关闭。
'heartbeat_idle_time' => 600,
));
$serv->on('connect', function ($serv,$fd){
// echo "client:$fd Connect.".PHP_EOL;
});
//测试receive
$serv->on("receive",function(swoole_server $serv,$fd,$from_id,$data){
// echo "receive#{$from_id}: receive $data ".PHP_EOL;
});
$serv->on('open', function($server, $req) {
// echo "server#{$server->worker_pid}: handshake success with fd#{$req->fd}".PHP_EOL;;
// echo PHP_EOL;
});
$serv->on('message',function($server,$frame) {
// echo "message: ".$frame->data.PHP_EOL;
$msg=json_decode($frame->data,true);
switch ($msg['type']){
case 'login':
$server->push($frame->fd,"欢迎欢迎~");
break;
default:
break;
}
$msg['fd']=$frame->fd;
$server->task($msg);
});
$serv->on("workerstart",function($server,$workerid){
// echo "workerstart: ".$workerid.PHP_EOL;
// echo PHP_EOL;
});
$serv->on("task","on_task");
$serv->on("finish",function($serv,$task_id,$data){
return ;
});
$serv->on('close', function($server,$fd,$from_id) {
// echo "connection close: ".$fd.PHP_EOL;
// echo PHP_EOL;
});
$serv->start();
function on_task($serv,$task_id,$from_id,$data) {
switch ($data['type']){
case 'login':
$send_msg="说:我来了~";
break;
default:
$send_msg="说:{$data['msg']['speak']}";
break;
}
foreach ($serv->connections as $conn){
if ($conn!=$data['fd']){
if (strpos($data['msg']['name'],"游客")===0){
$name=$data['msg']['name']."_".$data['fd'];
}else{
$name=$data['msg']['name'];
}
}else{
$name="我";
}
$serv->push($conn,$name.$send_msg);
}
return;
}
function on_finish($serv,$task_id,$data){
return true;
}
【前端代码】
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>WebSocket测试</title>
<script language="javascript"type="text/javascript" src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js">
</script>
</head>
<body>
<h2>WebSocket Test</h2>
昵称:<input type="text" id="name" size="5" value="游客"/> <input type="text" id="content">
<button onclick="speak_to_all()">发送</button>
<br/><br/>
<textarea id="message" style="overflow-x:hidden" rows="10" cols="50"></textarea>
<div id="output"></div>
</body>
<script language="javascript"type="text/javascript">
var wsUri ="ws://188.131.152.71:9502/";
var output;
function init() {
output = document.getElementById("output");
testWebSocket();
}
function testWebSocket() {
websocket = new WebSocket(wsUri);
websocket.onopen = function(evt) {
onOpen(evt)
};
websocket.onclose = function(evt) {
onClose(evt)
};
websocket.onmessage = function(evt) {
onMessage(evt)
};
websocket.onerror = function(evt) {
onError(evt)
};
}
function get_speak_msg(){
var name=document.getElementById("name").value;
var speak=document.getElementById("content").value;
var json_msg='{"name":"'+name+'","speak":\"'+speak+'"}';
return json_msg;
}
function pack_msg(type,msg){
return '{"type":"'+type+'","msg":'+msg+'}';
}
function onOpen(evt) {
append_speak("已经联通服务器.........");
speak_msg=get_speak_msg();
send_msg=pack_msg("login",speak_msg);
doSend(send_msg);
}
function onClose(evt) {
append_speak("俺老孙去也!");
}
function onMessage(evt) {
append_speak(evt.data);
}
function onError(evt) {
alert(evt.data);
}
function doSend(message) {
websocket.send(message);
}
function append_speak(new_msg){
document.getElementById("message").value=document.getElementById("message").value+new_msg+"\n";
document.getElementById('message').scrollTop = document.getElementById('message').scrollHeight;
}
function speak_to_all(){
send_msg=pack_msg("speak",get_speak_msg());
if(document.getElementById("content").value==""){
return;
}
doSend(send_msg);
document.getElementById("content").value="";
}
init();
</script>
</html>