php+websocket實現線上聊天室(二)
阿新 • • 發佈:2019-02-07
上一篇部落格我們完成了服務端的基本結構,這一篇部落格我們就來把它豐富起來以實現聊天室的功能。
1.整理需求
1.進行三人隨機匹配
2.取消匹配
3.傳送訊息
4.離開房間
5.獲取當前線上人數
2.規定訊息格式
我們使用json來傳遞資料
客戶端
{
'type' //0 進行匹配 1 取消匹配 2 傳送訊息 3離開房間
'data':{
'room_id'
'msg'
}
}
主要是type欄位,data欄位是可選的
服務端
{ 'type' //1 匹配成功 2 有人發訊息 3 匹配需要等待 4普通訊息推送(有人離開) 5普通訊息(取消匹配成功) 6 線上人數通知 'data':{ 'room_id' 'msg' } } 主要是type欄位,data欄位是可選的
3.程式碼實現
我們主要做的事就是解析客戶端的訊息,然後根據不同的type執行不同的函式
首先給WebsocketServer類新增兩個新的變數
private $chat_rooms;//所有聊天室
private $wait_users;//等待匹配的使用者
然後我們需要一個給同一個聊天室裡的人推送訊息的函式,和給所用客戶端推送訊息的函式用來推送線上人數
//同一個聊天室裡的使用者 廣播訊息
private function push_msg_for_room($room_id,$msg,$c_socket=null){
if($c_socket ){
// 當前使用者存在 只把訊息推送給其他兩個人
$msg=$this->msg_encode(json_encode($msg));
foreach ($this->chat_rooms[$room_id] as $user){
if($user!=$c_socket){
socket_write($user,$msg,strlen($msg));
}
}
}else {
//將訊息推送給所有人
$type=$msg['type'];
if($type==1){
//匹配成功訊息推送
//隨機三個使用者資訊
$usersinfo=$this->get_3_role();
foreach ($this->chat_rooms[$room_id] as $k=>$user){
$data=array(
'type'=>1,
'data'=>array(
'room_id'=>$room_id,
'index'=>$k,
'users'=>$usersinfo
)
);
$data=$this->msg_encode(json_encode($data));
socket_write($user,$data,strlen($data));
}
}else{
$msg=$this->msg_encode(json_encode($msg));
foreach ($this->chat_rooms[$room_id] as $user){
socket_write($user,$msg,strlen($msg));
}
}
}
}
//全域性廣播訊息 訊息為陣列格式
private function push_msg_for_all($msg,$c_socket=null){
$msg=$this->msg_encode(json_encode($msg));
//如果$c_socket不為null,除了伺服器和自己都發送
//如果$c_socket為null,除了伺服器都發送
foreach ($this->sockets as $socket){
if ($socket!=$this->server and $socket!=$c_socket){
socket_write($socket,$msg,strlen($msg));
}
}
}
然後當客戶端socket可讀時,如果已經握手了,就呼叫解析訊息函式,判斷type值再執行具體的功能函式
private function parse_msg($buffer,$socket){
$msg=json_decode($this->msg_decode($buffer),true);
switch ($msg['type']){
case 0:
//進行3人匹配
$this->match($socket);
break;
case 1:
//取消匹配
$this->dismatch($socket);
break;
case 2:
//傳送訊息
$room_id=$msg['data']['room_id'];
if(array_key_exists($room_id,$this->chat_rooms)){
$data=array(
'type'=>2,//有人發訊息
'data'=>array(
'msg'=>$msg['data']['msg'],
'user'=>$msg['data']['user']
)
);
$this->push_msg_for_room($room_id,$data,$socket);
}
break;
case 3:
//離開房間
$room_id=$msg['data']['room_id'];
$name=$msg['data']['name'];
$this->leave_room($room_id,$socket,$name);
break;
default:
break;
}
}
進行匹配時就是從$this->wait_users
陣列中隨機找兩個,不足兩個就讓使用者加入等待佇列,也就是將此使用者socekt加入$this->wait_users
陣列中
private function match($user_socket){
$user_count=count($this->wait_users);
if($user_count>=2){
//匹配成功
$rands=array_rand($this->wait_users,2);
$user_1=$this->wait_users[$rands[0]];
$user_2=$this->wait_users[$rands[1]];
$this->dismatch($user_1);
$this->dismatch($user_2);
$room_id=uniqid();//建立唯一房間號
$this->chat_rooms[$room_id]=array($user_socket,$user_1,$user_2);
//推送訊息 建立房間成功
$msg=array(
'type'=>1,//1 匹配成功
'data'=>array(
'room_id'=>$room_id,
'msg'=>'匹配成功'
)
);
$this->push_msg_for_room($room_id,$msg);
}else{
//等待匹配...
//給客戶端推送需要等待的訊息
$this->wait_users[]=$user_socket;
$msg=array(
'type'=>3,//需要等待
'data'=>array(
'msg'=>'等候匹配'
)
);
$msg=$this->msg_encode(json_encode($msg));
socket_write($user_socket,$msg,strlen($msg));
}
}
取消匹配很好處理,就是將此使用者socket從等待使用者陣列中刪除
//取消匹配
private function dismatch($socket){
foreach ($this->wait_users as $k=>$user){
if($socket==$user){
unset($this->wait_users[$k]);
break;
}
}
$msg=array(
'type'=>5,//普通訊息
'data'=>array(
'msg'=>'取消匹配成功'
)
);
$msg=$this->msg_encode(json_encode($msg));
socket_write($socket,$msg,strlen($msg));
}
離開房間也很好處理,根據客戶端傳來的room_id,找到此聊天室刪除此使用者就好了
//離開房間
private function leave_room($room_id,$user_socket,$name='有人'){
foreach ($this->chat_rooms[$room_id] as $k=>$user){
if($user==$user_socket) {
unset($this->chat_rooms[$room_id][$k]);
break;
}
}
if(count($this->chat_rooms[$room_id])==0){
//所有人都離開後 清除房間
unset($this->chat_rooms[$room_id]);
}else{
//廣播訊息有人離開
$msg=array(
'type'=>4,
'data'=>array(
'msg'=>$name."離開了房間"
)
);
$this->push_msg_for_room($room_id,$msg,$user_socket);
}
}
ok,服務端程式碼終於大功告成了!後面還用功能自行在parse_msg函式中新增就好了,前端程式碼就不講了,完整程式碼我放在了github上。