1. 客户端代码
1.1 控制器
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
// 仿百度商桥客户端
class Bchat extends Controller
{
//百度商桥首页
public function index() {
return view('bchat/index');
}
}
1.2 客户端视图
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>仿百度商桥</title>
<link rel="stylesheet" href="/static/plugins/layui/css/layui.css">
<script src="/static/plugins/layui/layui.js"></script>
</head>
<body>
<div>
这是测试内容啊老弟
</div>
</body>
</html>
<script>
layui.use(['layer'], function() {
var layer = layui.layer;
$=layui.jquery;
init_bchat();
});
// 百度商桥
function init_bchat() {
layer.open({
type: 2,
title: '百度商桥欢迎你',
// shadeClose: true,
shade: 0.2,
area: ['80%', '80%'],
content: '/bchat/index', //iframe的url
btn: ['发送'],
yes: function(index, layero) {
var body = layer.getChildFrame('body', index);
var iframeWin = window[layero.find('iframe')[0]['name']]; //得到iframe页的窗口对象,执行iframe页的方法:
iframeWin.send();
}
});
}
</script>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>客户端的聊天窗口</title>
<link rel="stylesheet" href="/static/plugins/layui/css/layui.css">
<script src="/static/plugins/layui/layui.js"></script>
<link rel="stylesheet" href="/static/css/bchat.css">
</head>
<body>
<!-- 消息内容展示 -->
<div class="msg_show">
</div>
<hr class="layui-bg-black">
<!-- 消息发送区 -->
<div class="msg_send" contenteditable="true">请输入要发送的内容</div>
</body>
</html>
<script>
layui.use(['layer'], function() {
var layer = layui.layer;
$=layui.jquery;
});
// 假设服务端ip为127.0.0.1
ws = new WebSocket("ws://127.0.0.1:2000");
ws.onopen = function() {
var data={};
data.type= 'login';
data.group = 'member';
ws.send(JSON.stringify(data));
// alert("连接成功");
// ws.send('tom');
// alert("给服务端发送一个字符串:tom");
};
ws.onmessage = function(e) {
var data=JSON.parse(e.data);
if (data.kefu_id > 0) {
var html='<div class="msg_item">\
<div class="nickname">客服'+ data.kefu_id +'说:</div>\
<div class="contents">'+ data.msg +'</div>\
</div>';
$('.msg_show').append(html);
} else {
var html='<div class="msg_item msg_customer">\
<div class="nickname">我说:</div>\
<div class="contents">'+ data.msg +'</div>\
</div>';
$('.msg_show').append(html);
}
};
// <!-- 发送消息 -->
function send() {
var data={};
data.group = 'member';
data.type='msg';//发送类型是message
// data.touid=1;//发给哪个客服
data.msg=$('.msg_send').html();
ws.send(JSON.stringify(data));
$('.msg_send').html('');
}
</script>
1.3 效果图
2. 客服端代码
2.1 客服端控制器
<?php
namespace App\Http\Controllers\admins;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
//后台客服
class Kefu extends Controller
{
//
public function index() {
return view('admins/kefu/index');
}
}
2.2 客服端视图
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>后台客服</title>
<link rel="stylesheet" href="/static/plugins/layui/css/layui.css">
<script src="/static/plugins/layui/layui.js"></script>
<link rel="stylesheet" href="/static/css/bchat.css">
</head>
<body>
<div class="kefu">
<div class="kefu_title">客户接待中心</div>
<div class="kefu_detail">
<!-- 左侧客户列表区 -->
<div class="member_list">
</div>
<!-- 右侧客户对话区 -->
<div class="message_list">
<!-- 当前的聊天内容 -->
<div class="message_content">
</div>
<!-- 消息发送区域 -->
<div class="message_send" contenteditable="true">
</div>
<div class="btn_send"><button class="layui-btn" onclick="sendmsg()">发送</button></div>
</div>
</div>
</div>
</body>
</html>
<script>
var user_list=[];//新用户列表
layui.use(['layer'], function() {
var layer = layui.layer;
$=layui.jquery;
});
// 假设服务端ip为127.0.0.1
ws = new WebSocket("ws://127.0.0.1:2000");
ws.onopen = function() {
var data={};
data.type= 'login';
data.group = 'admin';
ws.send(JSON.stringify(data));
// alert("连接成功");
// ws.send('tom');
// alert("给服务端发送一个字符串:tom");
};
ws.onmessage = function(e) {
// console.log(e.data);
var data = JSON.parse(e.data);
//新客户来了
if (data.type==='login') {
if ($.inArray(data.customer_id,user_list)===-1) {
user_list.push(data.customer_id);
}
build_kefu_user_list();
return;
}
// 客户连接断开
if (data.type==='logout') {
var index = $.inArray(data.connection_id,user_list);
if (index > -1) {
user_list.splice(index,1);
}
build_kefu_user_list();
return;
}
var msg_member_list = $('#member_'+ data.customer_id);
if (msg_member_list.length===0) {
var html_container = '<div id="member_' + data.customer_id +'"></div>';
$('.message_content').append(html_container);
}
var html='<div class="msg_item">\
<div class="nickname">'+ data.customer_id +'说:</div>\
<div class="contents">'+ data.msg +'</div>\
</div>';
$('#member_'+ data.customer_id).append(html);
$('#member_'+ data.customer_id).show().siblings('div').hide();
};
//构建客服端的客户列表
function build_kefu_user_list() {
var html='';
$.each(user_list,function(i,v){
html+=('<div class="member_item" onclick="checkme(this)" member_id="'+v+'">客户:'+ v +'</div>');
});
$('.member_list').html(html);
}
// 和某个客户聊天
function checkme(obj) {
$(obj).addClass('active').siblings('div').removeClass('active');
var member_id=$(obj).attr('member_id');
$('#member_'+ member_id).show().siblings('div').hide();
}
// <!-- 发送消息 -->
function sendmsg() {
// 获取当前聊天的客户
var touid =parseInt($('.member_list div[class*="active"]').attr('member_id'));
if (isNaN(touid)) {
return layer.alert('请先选择客户',{icon:2});
}
var data={};
data.group = 'admin';
data.type='msg';//发送类型是message
data.touid=touid;//发给哪个客户
data.msg=$('.message_send').html();
ws.send(JSON.stringify(data));
$('.message_send').html('');
var html='<div class="msg_item msg_kefu">\
<div class="nickname">我说:</div>\
<div class="contents">'+ data.msg +'</div>\
</div>';
$('.message_content').append(html);
}
</script>
2.3 效果图
3. websocket
参考WebSocket即时通讯原理实战https://www.php.cn/blog/detail/22780.html
下载PHP socket即时通讯框架。
https://www.workerman.net/download/workermanzip
<?php
use Workerman\Worker;
require_once __DIR__ . '/workerman/Autoloader.php';
// 注意:这里与上个例子不同,使用的是websocket协议
$ws_worker = new Worker("websocket://0.0.0.0:2000");
// 启动4个进程对外提供服务
$ws_worker->count = 4;
// 当收到客户端发来的数据后返回hello $data给客户端
$ws_worker->onMessage = function($connection, $data)
{
// 向客户端发送hello $data
// $connection->send($data);
// 给所有人发消息
// global $ws_worker;
// foreach($ws_worker->connections as $conn)
// {
// // $conn->send("user[{$connection->uid}] said: $data");
// $conn->send($data);
// }
// 服务器端收到客户端的信息后返回数据给客户端
var_dump($connection->id);
// return;
global $ws_worker;
$data = json_decode($data,true);
// print_r($data);
// 客户端有人登录进来了
if ($data['type']==='login'){
// 赋值
$connection->group = $data['group'];
//客户登录的时候
if ($data['group']==='member') {
//随机分配一个客服姐姐给他
$admin_list=[];
foreach($ws_worker->connections as $conn) {
if ($conn->group==='admin') {
$admin_list[]=$conn->id;
}
}
// 分配一个随机客服
$myadmin_index = array_rand($admin_list,1);//键名
$connection->touid=$admin_list[$myadmin_index];
// 通知客服有新用户进来
foreach($ws_worker->connections as $conn)
{
if ($conn->id === $connection->touid) {
$data['customer_id'] = $connection->id;
$conn->send(json_encode($data));
}
}
}
}
// 有新的消息过来了
if ($data['type']==='msg'){
// 赋值
// 客服(管理)登录进来,发消息给用户
if ($connection->group === 'admin'){
$touid = $data['touid'];
$msg = $data['msg'];
foreach($ws_worker->connections as $conn)
{
//向对应的用户发送消息
if($touid === $conn->id) {
$msgData = [];
$msgData['type'] = 'msg';
$msgData['kefu_id'] = $connection->id;
$msgData['msg'] = $msg;
$conn->send(json_encode($msgData));
// 把数据储存到数据库
$data = ['from_id'=>$connection->id,'to_uid'=>$touid,'msg'=>$msg];
$url = 'http://laravel.edu/kefumsg/save_msg';
curl_post($url,$data);
// var_dump($res);
}
// $conn->send("user[{$connection->uid}] said: $data");
}
}
// 客户端登录进来
if ($connection->group === 'member'){
// $touid=$data['touid'];
foreach($ws_worker->connections as $conn)
{
//向对应的用户发送消息
if($connection->touid === $conn->id) {
$data['customer_id'] = $connection->id;
$conn->send(json_encode($data));
// 储存数据
$data = ['from_id'=>$connection->id,'to_uid'=>$connection->touid,'msg'=>$data['msg']];
$url = 'http://laravel.edu/kefumsg/save_msg';
curl_post($url,$data);
}
// $conn->send("user[{$connection->uid}] said: $data");
}
}
}
// 用户链接断开的时候
$ws_worker->onClose = function($connection)
{
// 通知客服,有人掉线了
global $ws_worker;
foreach($ws_worker->connections as $conn) {
if ($conn->group==='admin') {
$data=[];
$data['type']='logout';
$data['connection_id']=$connection->id;
$conn->send(json_encode($data));
}
}
};
};
// 定义一个http请求函数
// $url 是请求的链接
// $postdata 是传输的数据,数组格式
function curl_post( $url, $postdata ) {
$header = array(
'Accept: application/json',
);
//初始化
$curl = curl_init();
//设置抓取的url
curl_setopt($curl, CURLOPT_URL, $url);
//设置头文件的信息作为数据流输出
curl_setopt($curl, CURLOPT_HEADER, 0);
//设置获取的信息以文件流的形式返回,而不是直接输出。
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
// 超时设置
curl_setopt($curl, CURLOPT_TIMEOUT, 10);
// 超时设置,以毫秒为单位
// curl_setopt($curl, CURLOPT_TIMEOUT_MS, 500);
// 设置请求头
curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE );
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE );
//设置post方式提交
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, $postdata);
//执行命令
$data = curl_exec($curl);
// 显示错误信息
if (curl_error($curl)) {
print "Error: " . curl_error($curl);
} else {
// 打印返回的内容
curl_close($curl);
return $data;
}
}
// 运行worker
Worker::runAll();
4. 效果演示
5. 总结
随着互联网的发展,传统的HTTP协议已经很难满足Web应用日益复杂的需求了。近年来,随着HTML5的诞生,WebSocket协议被提出,它实现了浏览器与服务器的全双工通信,扩展了浏览器与服务端的通信功能,使服务端也能主动向客户端发送数据。