1.GatewayWorker下载地址:https://www.workerman.net/download
2.将下载的文件放置vendor目录(原理上服务器任何位置都可以)
3.将GatewayWorker/Applications/YourApp/start_gateway.php第24行
$gateway = new Gateway("tcp://0.0.0.0:8282");
修改为
$gateway = new Gateway("Websocket://0.0.0.0:8282");
开启心跳检测:
将38行和40行的注释去掉,即:
// 心跳间隔
$gateway->pingInterval = 10;
// 心跳数据
$gateway->pingData = '{"type":"ping"}';
开启后,在用户与服务器连接成功后,每十秒向客户端发送“ping“以保持长时间连接,否则服务器将断开服务器与客户端的链接
此方法为服务端主动发起,其还可以从客户端主动发起(之后补充)
4.开启服务
使用服务器宝塔终端或xshell等其他命令工具,进入GatewayWorker根目录
输入:php start.php start -d
输出系统所示信息表示socket服务启动成功
注:每次修改服务端代码,soceket都需要进行重启
5.修改GatewayWorker/Applications/YourApp/Events.php
public static function onConnect($client_id)
{
Gateway::sendToClient($client_id, json_encode([
'type'=>'init',//客户连接后向用户发送绑定uid请求(1)
'client_id'=>$client_id
]));
}
/**
* 当客户端发来消息时触发
* @param int $client_id 连接id
* @param mixed $message 具体消息
*/
public static function onMessage($client_id, $message)
{
$message_data = json_decode($message,true);
if(!$message_data){
return;
}
switch ($message_data['type']) {
case "say":
$content = $message_data['data'];
$fromid = $message_data['fromid'];
$toid = $message_data['toid'];
$infotype = $message_data['infotype'];
$data = [
'type' => 'answer',
'id' => $client_id,
'fromid' =>$fromid,
'toid' => $toid,
'time' => time(),
'data' => $content,
'infotype' => $infotype
];
if(Gateway::isUidOnline($toid)){//判断用户是否在线
$data['isread'] = 1;
Gateway::sendToUid($toid, json_encode($data));//向指定用户发送信息
}else{
$data['isread'] = 0;
$data['type'] = 'save';
Gateway::sendToUid($fromid,json_encode($data));//向指定用户发送信息
}
break;
case "bind":
/*接收到客户端发来的uid后进行绑定(3)*/
$uid = $message_data['fromid'];
Gateway::bindUid($client_id,$uid);//将client_id与uid进行绑定
break;
case "pong":
return ;//心跳检测
break;
}
}
6.在前端代码中添加以下js代码
<script type="text/javascript">
var fromid = {$from_id};
var toid = {$uid};
var ws = new WebSocket("ws://0.0.0.0");
ws.onmessage = function(e){
console.log(e);
var message = eval("("+e.data+")");
switch(message.type){
case "answer" :
if(toid==message.fromid){
var html = '';
if(message.infotype=='1'){
html += '<li class=\"right\">'+'<p>'+message.data+'</p>'+'</li>';
}else{
html += '<li class=\"right\"><img src=\"'+message.data+'\" style="width:100px;height:100px;"></li>';
}
$("#talk").append(html);
message.type = '1';
send(message);
}
break;
case "init" :
var bind = '{"type":"bind","fromid":"'+fromid+'"}';
ws.send(bind);/*接收到服务端发来的绑定uid请求,将uid发送至服务端进行绑定(2)*/
break;
case "ping" :
var pong = '{"type":"pong","fromid":"'+fromid+'"}'
break;
case "save" :
if(message.fromid==fromid){
message.fromid = toid;
message.type='2';
send(message);
}
break;
}
};
$(".send-btn").click(function() {
var message = $("#comment").val();
var html = '';
html += '<li class=\"left\">'+'<p>'+message+'</p>'+'</li>';
$("#talk").append(html);
$("#comment").val("");
var msg = '{"type":"say","infotype":"1","data":"'+message+'","fromid":"'+fromid+'","toid":"'+toid+'"}';
// send();
ws.send(msg);
});
function send(message){
var uid = message.fromid;
var content = message.data;
var time = message.time;
var isread = message.isread;
$.ajax({
url: "{:url('vilage/message/talk')}",
type: 'POST',
dataType: 'json',
data: {
'uid' : uid,
'content' : content,
'time' : time,
'isread' : isread,
'type':message.type,
'info_type':message.infotype
}
})
}
$(document).ready(function(){
$('#talk').scrollTop( $('#talk')[0].scrollHeight );
});
$("#pic").click(function() {
$("#file").click();
});
$("#file").change(function() {
fromdata = new FormData();
fromdata.append('file',$("#file")[0].files[0]);
$.ajax({
url: "{:url('vilage/message/upload')}",
type: 'post',
dataType: 'json',
data: fromdata,
processData:false,
contentType:false,
success:function(res){
console.log(res);
res = JSON.parse(res);
console.log(res);
if(res.code==1)
{
var domain = window.location.host;
var imgurl = 'http://' + domain + res.url;
var msg = '{"type":"say","infotype":"2","infotype":"2","data":"'+imgurl+'","fromid":"'+fromid+'","toid":"'+toid+'"}';
var html = '';
html += '<li class=\"left\"><img src=\"'+imgurl+'\" style="width:100px;height:100px;"></li>';
$("#talk").append(html);
$(".chat-content").append(html);
ws.send(msg);
}else{
alert(res.msg);
}
}
})
});
</script>
7.对应的html模板为:
<div class="panel panel-default panel-intro">
<input type="hidden" id="uid" value="{$uid}">
<div class="main wrap">
<ul id="talk" class="wrap">
{foreach name="list" item="vo"}
{if condition="$vo.message_type!='2'"}
{if condition="$vo.info_type=='1'"}
<li class="right"><p>{$vo.message_info}</p></li>
{else}
<li class="right"><img src="{$vo.message_info}" style="width:100px;height:100px;"></li>
{/if}
{else}
{if condition="$vo.info_type=='1'"}
<li class="left"><p>{$vo.message_info}</p></li>
{else}
<li class="left"><img src="{$vo.message_info}" style="width:100px;height:100px;"></li>
{/if}
{/if}
{/foreach}
</ul>
</div>
<div class="comment">
<textarea id="comment" class="form-control editor" rows="1" name="comment" cols="50"></textarea>
<input type="file" name="pic" id="file" style="display:none">
<span id="pic">图片</span>
<button type="button" class="butt send-btn">发送</button>
</div>
</div>
以上就实现了消息和图片的实时通信,及消息持久化
8.思路:
a.用户打开链接后服务器发送绑定uid请求
b.客户端接收到服务器的绑定uid请求后,将用户id发送给服务端
c.服务端接收到uid后进行绑定
d.用户发送消息后将消息追加至消息列表,并发送给服务端
e.服务端判断to_user是否在线和消息的类型(文字或图片)
f.如果to_user在线即服务端发送给to_user,否则服务端发送给from_user让其进行存储
g.如果to_user在线,则其接受到消息后进行存储,并追加至消息列表
原文链接:https://blog.csdn.net/Shuainan_0619/article/details/110392545