成果如下
{
parent::init(); // TODO: Change the autogenerated stub
$this->m=new User();
$this->message=new Message();
}
Server类中的代码对 Socket 的各个事件都有其对应的回调方法
['onWorkerStart', 'onConnect', 'onMessage', 'onClose', 'onError', 'onBufferFull', 'onBufferDrain', 'onWorkerStop', 'onWorkerReload']
我仅使用了
- onMessage 即收到信息
- onClose 断开连接的回调
说说思路:
因为服务端都是通过onMessage来服务客户端,因此可以约定一个type参数用来表示当前操作类型,例如,type=”join” 意为首次建立连接并加入会话。需要记录当前的用户信息和连接对象( c o n n e c t i o n ) , 即 上 述 的 【 connection),即上述的【 connection),即上述的【ciients】建议用数组存储,键可以使用当前客户端的ip【此种方式会使得当前ip只能有一个用户】或者 ip+用户id以及其他唯一标识。 因此$clients看起来可能是如下形式
$clients=[
"127.0.0.1&12"=>{userInfo=>"当前链接的用户信息",key=>"127.0.0.1&12",...clients对象的其他属性}
]
/**
* 收到信息
* @param $connection
* @param $data Object 带过来的参数
*/
public function onMessage($connection, $data)
{
$data=json_decode($data);
if(empty($data->uid)) {
$connection->send("未携带uid");
}else{
//拼接请求的key
$data->key=$connection->getRemoteIp()."&".$data->uid;
//如果不存在于记录的连接数组 就将 添加进去
if(!array_key_exists($data->key,$this->clients)){
//查询出当前用户的昵称和头像
$temp=$this->m->field(["nickname","avatar","id"])->find($data->uid);
$temp["joinTime"]=date("Y-m-d H:i:s",time());
//将用户信息和key存在链接对象里
$connection->userInfo=$temp;
$connection->key=$data->key;
//保存当前用户链接的key
$this->clients[$data->key]=$connection;
//广播所有链接用户
$this->broadCast(['type'=>"join",'data'=>$temp,"total"=>sizeof($this->clients)]);
}else{
// 如果当前消息意为发送信息
if(!empty($data->type) && $data->type=="send"){
//将消息存储至数据库
$this->message->insert([
"createtime"=>time(),
"text"=>$data->data->text,
"images"=>$data->data->images,
"uid"=>$connection->userInfo->id
]);
//将消息广播至所有用户
$this->broadCast([
'type'=>"onMessage",
'data'=>$data->data,
"publisher"=>$connection->userInfo,
'publishTime'=>datetime(time())
]);
}
}
}
}
消息广播即遍历clients数组分别对其发送
/**
* 给当前所有的链接用户发送消息
* @param $data
* @param $exceptKey array 期望忽略的用户的key列表
*/
public function broadCast($data, array $exceptKey=[])
{
//遍历客户端数组
foreach ($this->clients as $k=>$v){
// 如果不是被忽略的对象 就给该用户发送数据
if(!in_array($k,$exceptKey)){
$v->send(json_encode($data));
}
}
}
当客户端断开连接,需要关闭当前链接,并告知所有用户
/**
* 当连接断开时触发的回调函数
* @param $connection
*/
public function onClose($connection)
{
$connection->close();
if(!empty($connection->key)){
unset($this->clients[$connection->key]);
$this->broadCast(['type'=>"leave",'data'=>$connection->userInfo,"total"=>sizeof($this->clients)]);
}
}
前端部分,将websocket对象放至pinia中了。
就建立连接发送消息,接收消息并渲染
function send(p = {}) {
//发送信息时携带uid
p["uid"] = store.uinfo.id
// p["uid"]=10;
store.websocket.send(JSON.stringify(p));
}
function connection() {
//如果当前链接对象是空或者还没链接
if (store.websocket == null || store.websocket.readyState != 1) {
store.websocket = new WebSocket("ws://localhost:2345");
console.log("进行重连");
store.websocket.onopen = function (evt) {
//立即将用户uid发过去 用来告知其他用户 有人加入
send();
online.value = true;
};
store.websocket.onclose = function (evt) {
console.log("关闭链接", evt);
online.value = false;
};
store.websocket.onmessage = function (evt) {
let data = JSON.parse(evt.data);
messageList.value.push(data);
};
store.websocket.onerror = function (evt) {
console.log("错误", evt);
};
} else {
online.value = true;
}
}
至此,基本功能就实现了。