php基于socket开发简单页面聊天

<?php
/**
 * Created by PhpStorm.
 * User: suzim
 * Date: 2019/2/4
 * Time: 6:49
 */
namespace socket;

$ip = '0.0.0.0';
$port = 8888;

class Chat{

    protected $master = null; //服务端socket
    protected $connectPool = []; //连接池
    protected $handPool = []; //http升级webSocket池

    public function __construct($ip,$port)
    {
        $this->startSocket($ip,$port);
    }

    public function startSocket($ip,$port)
    {
        //创建服务端socket
        //参数:domain:可用的地址/协议系列 ,IPV4
        //参数type:使用的通信类型 ,字节流
        //参数protocol:参数设置domain在返回的套接字上进行通信时要使用的指定内的特定协议,TCP
        $this->connectPool[] = $this->master =  \socket_create(AF_INET,SOCK_STREAM,SOL_TCP);
        //设置属性,绑定端口,监听端口
        \socket_bind($this->master,$ip,$port);
        \socket_listen($this->master100);
        //开启阻塞模式
        while (true){
            $sockets = $this->connectPool;
            $write = $except = null;
            \socket_select($sockets,$write,$except,60);
            //
            foreach($sockets as $socket)
            {
                if($socket == $this->master)
                {
                    $this->connectPool[] = $client = \socket_accept($this->master);
                    $keyArr = \array_keys($this->connectPool,$client);
                    $key = end($keyArr);
                    $this->handPool[$key] = false;
                }else{
                    //接受客户端消息
                    $length = \socket_recv($socket,$buffer,1024,0);
                    //小于7为断开连接,在服务器端断开连接。
                    if($length < 7)
                    {
                        $this->close($socket);
                    }else{
                        //将连接的http升级为webSocket
                        $key = array_search($socket,$this->connectPool);
                        if($this->handPool[$key] == false)
                        {
                            //升级协议
                            $this->handShake($socket,$buffer,$key);
                        }else{
                            //解帧
                            $this->deFrame($buffer);
                            //封帧
                            $this->enFrame($buffer);
                            //发送群聊
                        }
                    }
                }
            }
        }
    }
    //关闭连接
    public function close($socket)
    {
        $key = array_search($socket,$this->connectPool);
        unset($this->connectPool[$key]);
        unset($this->handPool[$key]);
        \socket_close($socket);
    }
    //http升级webSocket
    public function handShake($socket,$buff,$key)
    {
        //提取websocket传的key并进行加密 (这是固定的握手机制获取Sec-WebSocket-Key:里面的key)
        $buf = substr($buff,strpos($buff,'Sec-WebSocket-Key:')+18);
        //去除换行空格字符
        $key = trim(substr($buf,0,strpos($buf,"\r\n")));
        //固定的加密算法
        $new_key = base64_encode(sha1($key."258EAFA5-E914-47DA-95CA-C5AB0DC85B11",true));
        $requestHeader = "HTTP/1.1 101 Switching Protocols\r\n";
        $requestHeader .= "Upgrade: websocket\r\n";
        $requestHeader .= "Sec-WebSocket-Version: 13\r\n";
        $requestHeader .= "Connection: Upgrade\r\n";
        $requestHeader .= "Sec-WebSocket-Accept: " . $new_key . "\r\n\r\n";
        //将套接字写入缓冲区,升级协议成功
        socket_write($socket,$requestHeader,strlen($requestHeader));
        //标记此套接字握手成功
        $this->handPool[$key] = true;
    }

    //解帧
    public function deFrame($buff)
    {
        //$buff 解析数据帧
        $mask = array();
        $data = '';
        $msg = unpack('H*', $buff); //用unpack函数从二进制将数据解码
        $head = substr($msg[1], 0, 2);
        if (hexdec($head{1}) === 8) {
            $data = false;
        } else if (hexdec($head{1}) === 1) {
            $mask[] = hexdec(substr($msg[1], 4, 2));
            $mask[] = hexdec(substr($msg[1], 6, 2));
            $mask[] = hexdec(substr($msg[1], 8, 2));
            $mask[] = hexdec(substr($msg[1], 10, 2));
            //遇到的问题 刚连接的时候就发送数据 显示 state connecting
            $s = 12;
            $e = strlen($msg[1]) - 2;
            $n = 0;
            for ($i = $s; $i <= $e; $i += 2) {
                $data .= chr($mask[$n % 4] ^ hexdec(substr($msg[1], $i, 2)));
                $n++;
            }
            //发送数据到客户端
            //如果长度大于125 将数据分块
            $block = str_split($data, 125);
            $mess = array(
                'mess' => $block[0],
            );
            return $mess;

        }
    }

    public function enFrame($buff)
    {
        $len = strlen($buff);
        if($len <= 125)
        {
            return "\81".chr($len).$buff;
        }elseif($len <= 65535)
        {
            return "\81".chr(126).pack("n",$len).$buff;
        }else{
            return "\81".chr(127).pack("xxxxN",$len).$buff;
        }
    }

    //发送群聊
    public function send($message)
    {
        foreach($this->connectPool as $socket)
        {
            if($socket != $this->master)
            {
                \socket_write($socket,$message,strlen($message));
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值