1. 程式人生 > >php+websocket實現線上聊天室(二)

php+websocket實現線上聊天室(二)

上一篇部落格我們完成了服務端的基本結構,這一篇部落格我們就來把它豐富起來以實現聊天室的功能。

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上。