PHP WebSocket

PHP WebSocket_第1张图片

PHP WebSocket_第2张图片

PHP WebSocket_第3张图片

PHP WebSocket_第4张图片

强调一下:websocket 是一个应用层协议,协议标识符为ws 即websocket的缩写,wss 为 websocket security的缩写

PHP WebSocket_第5张图片

websocket步骤

PHP WebSocket_第6张图片

PHP WebSocket_第7张图片

PHP WebSocket_第8张图片

PHP WebSocket_第9张图片

来自客户端的握手,在服务端看来,就是客户端发过来一段HTTP报头

GET / HTTP/1.1
Host: 101.200.142.148:8880
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:80.0) Gecko/20100101 Firefox/80.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: keep-alive
Upgrade-Insecure-Requests: 1

这时,服务端需要响应如下信息给客户端,这样握手就成功了。

PHP WebSocket_第10张图片

#一个demo

客户端:





    
    
    
    Document


    

这是一个websocket的demo

服务端:

#Web群聊聊天室DEMO

大体思路:

使用IO多路复用 socket_select

如果当前活跃的是 套接字 就建立连接 ,并初始化用户列表

如果当前活跃的是 连接 ,就ws握手,如果已经握手,就解码客户端

传来的数据帧,然后调用send方法,将数据帧推送给在线的每个用户。

Ws.php

socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
        socket_set_option($this->socket, SOL_SOCKET, SO_REUSEADDR, true);
        socket_bind($this->socket, $allow_ip, $port);
        socket_listen($this->socket);
        $this->sockets[] = $this->socket;

        while (true) {
            $tmp_sockets = $this->sockets;
            socket_select($tmp_sockets, $this->write, $this->except, null);
            foreach ($tmp_sockets as $sock) {
                if ($sock == $this->socket) {
                    $connSock = socket_accept($this->socket);
                    $this->sockets[] = $connSock;
                    $this->users[] = [
                        'socket' => $connSock,
                        'hand_shake' => false,
                    ];
                } else {
                    $index = $this->getUserIndex($sock);
                    $request = socket_read($sock, 1024);
                    if(strlen($request) == 8){
                        $this->logout($index);
                    }else{
                        if ($this->users[$index]['hand_shake'] == false) {
                            $response = $this->handShake($request);
                            //响应客户端的握手请求
                            socket_write($sock, $response, strlen($response));
                            $this->users[$index]['hand_shake'] = true;
                        } else {
                            //解码数据帧
                            $msg = $this->decode($request);
                            $this->send($msg, $index);
                        }
                    }
                }
            }
        }
    }
    private function logout($user_index){
        socket_close($this->users[$user_index]['socket']);
        unset($this->users[$user_index]);
        $this->sockets = null;
        $this->sockets[] = $this->socket;
        foreach($this->users as $user){
            $this->sockets[] = $user['socket'];
        }
    }
    public function send($msg, $index)
    {
        $arr = explode('===', $msg);
        switch ($arr[0]) {
            case 'login':
                $this->users[$index]['name'] = $arr[1];
                $res['msg'] = $arr[1] . ':登录成功';
                $res['type'] = 'login';
                //获取所有用户名
                $names['userlist'] = $this->getUserNames();
                $names['type'] = 'user';
                $names = $this->frame(json_encode($names));
                //向每个用户推送
                foreach ($this->users as $user) {
                    socket_write($user['socket'], $names, strlen($names));
                }
                break;
            case  'content':
                $res['content'] = $arr[1];
                $res['name'] = $this->users[$index]['name'];
                $res['time'] = date('Y-m-d H:i:s',time());
                $res['type'] = 'content';
                break;
        }
        $res = $this->frame(json_encode($res));
        //向每个用户推送
        foreach ($this->users as $user) {
            socket_write($user['socket'], $res, strlen($res));
        }
    }
    private function getUserNames()
    {
        foreach ($this->users as $user) {
            $names[] =  $user['name'];
        }
        return $names;
    }

    public function getUserIndex($sock)
    {
        foreach ($this->users as $key => $user) {
            if ($user['socket'] == $sock) {
                return $key;
            }
        }
    }
    public function handShake($request)
    {
        preg_match("/Sec-WebSocket-Key:(.*)\r\n/", $request, $match);
        $key = trim($match[1]);
        var_dump($match);
        $new_key = base64_encode(sha1($key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true));
        $response = "HTTP/1.1 101 Switching Protocols\r\n";
        $response .= "Upgrade: websocket\r\n";
        $response .= "Connection: Upgrade\r\n";
        $response .= "Sec-WebSocket-Accept: $new_key\r\n";
        $response .= "Sec-WebSocket-Protocol: chat\r\n\r\n";
        return $response;
    }

    //解析客户端发来的数据帧
    public function decode($buffer)
    {
        $len = $masks = $data = $decoded = null;
        $len = ord($buffer[1]) & 127;
        if ($len === 126) {
            $masks = substr($buffer, 4, 4);
            $data = substr($buffer, 8);
        } else if ($len === 127) {
            $masks = substr($buffer, 10, 4);
            $data = substr($buffer, 14);
        } else {
            $masks = substr($buffer, 2, 4);
            $data = substr($buffer, 6);
        }
        for ($index = 0; $index < strlen($data); $index++) {
            $decoded .= $data[$index] ^ $masks[$index % 4];
        }
        return $decoded;
    }
    //编码数据帧
    public function frame($s)
    {
        $a = str_split($s, 125);
        if (count($a) == 1) {
            return "\x81" . chr(strlen($a[0])) . $a[0];
        }
        $ns = "";
        foreach ($a as $o) {
            $ns .= "\x81" . chr(strlen($o)) . $o;
        }
        return $ns;
    }
}

server.php

index.html






    
    
    
    西电221聊骚室
    
    



    
西电221聊骚室
消息框
用户列表

 

你可能感兴趣的:(PHP,计算机网络)