php如何实现websocket

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了php如何实现websocket相关的知识,希望对你有一定的参考价值。

php有可用的websocket库,不需要php-fpm。

目前比较成熟的有swoole(swoole.com),和workman(workman.net)

swoole是c写的php扩展, 效率比nodejs还要高,workman是纯php实现,两者都号称可以实现并发百万TCP连接。

给你个例子:

这个要通过cmd运行的 具体带的参数有点忘记了
<?php
    error_reporting(E_ALL);
    set_time_limit(0);
    ob_implicit_flush();

    //创建一个socket连接 设置参数 绑定 监听 并且返回
    $master  = WebSocket("localhost",12345);

    //标示是否已经进行过握手了
    $is_shaked = false;

    //是否已经关闭
    $is_closed = true;

    //将socket变为一个可用的socket

    while(true)
        //如果是关闭状态并且是没有握手的话 则创建一个可用的socket(貌似第二个条件可以去除)
        if($is_closed && !$is_shaked)
            if(($sock = socket_accept($master)) < 0)
                echo "socket_accept() failed: reason: " . socket_strerror($sock) . "\\n";
            

            //将关闭状态修改为false
            $is_closed = false;
        

        //开始进行数据处理
        process($sock);
    

    //处理请求的函数
    function process($socket)
        //先从获取到全局变量
        global $is_closed, $is_shaked;

        //从socket中获取数据
        $buffer = socket_read($socket,2048);

        //如果buffer返回值为false并且已经握手的话 则断开连接
        if(!$buffer && $is_shaked)
            disconnect($socket);
        else
            //如果没有握手的话则握手 并且修改握手状态
            if($is_shaked == false)
                $return_str = dohandshake($buffer);
                $is_shaked = true;
            else
                //如果已经握手的话则送入deal函数中进行相应处理
                $data_str = decode($buffer);    //解析出来的从前端送来的内容
                console($data_str);
                $return_str = encode(deal($socket, $data_str));
                //$return_str = encode($data_str);
            

            //将应该返回的字符串写入socket返回
            socket_write($socket,$return_str,strlen($return_str));
        
    

    function deal($socket, $msgObj)
        $obj = json_decode($msgObj);
        foreach($obj as $key=>$value)
            if($key == 'close')
                disconnect($socket);
                console('close success');
                return 'close success';
            else if($key == 'msg')
                console($value."\\n");
                return $value;
            
        
    

    //获取头部信息 
    function getheaders($req)
        $r=$h=$o=null;
        if(preg_match("/GET (.*) HTTP/"   ,$req,$match)) $r=$match[1]; 
        if(preg_match("/Host: (.*)\\r\\n/"  ,$req,$match)) $h=$match[1]; 
        if(preg_match("/Origin: (.*)\\r\\n/",$req,$match)) $o=$match[1]; 
        if(preg_match("/Sec-WebSocket-Key: (.*)\\r\\n/",$req,$match)) $key=$match[1]; 
        if(preg_match("/\\r\\n(.*?)\\$/",$req,$match)) $data=$match[1]; 
        return array($r,$h,$o,$key,$data);
    

    function WebSocket($address,$port)
        $master=socket_create(AF_INET, SOCK_STREAM, SOL_TCP)     or die("socket_create() failed");
        socket_set_option($master, SOL_SOCKET, SO_REUSEADDR, 1)  or die("socket_option() failed");
        socket_bind($master, $address, $port)                    or die("socket_bind() failed");
        socket_listen($master,20)                                or die("socket_listen() failed");
        echo "Server Started : ".date('Y-m-d H:i:s')."\\n";
        echo "Master socket  : ".$master."\\n";
        echo "Listening on   : ".$address." port ".$port."\\n\\n";
        return $master;
    

    function dohandshake($buffer)
        list($resource,$host,$origin,$key,$data) = getheaders($buffer);
        echo "resource is $resource\\n";
        echo "origin is $origin\\n";
        echo "host is $host\\n";
        echo "key is $key\\n\\n";

        $response_key = base64_encode(sha1($key.'258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true));

        $return_str = "HTTP/1.1 101 Switching Protocols\\r\\n".
                    "Upgrade: websocket\\r\\n".
                    "Connection: Upgrade\\r\\n".
                    "Sec-WebSocket-Accept: $response_key\\r\\n\\r\\n";
        return $return_str;
    

    function console($msg)
        $msg = transToGBK($msg);
        echo "$msg\\n";
        return $msg;
    

    function decode($msg="") 
        $mask = array();
        $data = "";
        $msg = unpack("H*",$msg);

        $head = substr($msg[1],0,2);

        if (hexdec($head1) === 8)
            $data = false;
         else if (hexdec($head1) === 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));

            $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++;
              
          

        return $data;
    

    function encode($msg="")
        $frame = array();
        $frame[0] = "81";
        $msg .= ' is ok';
        $len = strlen($msg);
        $frame[1] = $len<16?"0".dechex($len):dechex($len);
        $frame[2] = ord_hex($msg);
        $data = implode("",$frame);
        return pack("H*", $data);
    


    function transToGBK($s)//UTF8->GBK
        //echo $s;
        return iconv("UTF-8", "GBK", $s);
        return $s;
    

    function ord_hex($data)
        $msg = "";
        $l = strlen($data);

        for ($i=0; $i<$l; $i++)
            //ord是返回字符串第一个字符的ascii值
            //dechex把十进制转换为十六进制
            $msg .= dechex(ord($data$i));
        

        return $msg;
    

    function disconnect($socket)
        global $is_shaked, $is_closed;
        $is_shaked = false;
        $is_closed = true;
        socket_close($socket);
    
?>

参考技术A 轮训加长连接,php的sleep函数 参考技术B 查看swoole 参考技术C 你问错了吧,websocket是javascript的吧?

用PHP 和Websocket实现实时通讯---GoEasy

说到websocket大家一定不会陌生,WebSocket是HTML5一种新的协议。它实现了浏览器与服务器全双工通信(full-duplex)。一开始的握手需要借助HTTP请求完成,当浏览器和服务器握手成功后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。有了websocket, 大家就可以摒弃以往用轮询来实现实时通讯的方式了。
有了websocket后,应运而生的相关产品也不在少数,选择也成了最大的问题,在这里你可能会说“干嘛用别人的,我可以自己用原始的开发一个啊”对此我只想说你真NB,我也很想知道你是如何解决以下问题的:
1.        你是打算花1个月,2个月还是1年还实现一个websocket通信的?如果你说你打算花一个月,那说明两点,你的技术要上天了(这么刁的东西你可以那么快弄出来还不存在性能问题,除了我现有的膝盖,请把我下辈子的膝盖也拿走),贵公司土豪(贵公司对技术的炙热追求已经达到了行业的巅峰了,看来你们的项目不是很紧嘛,预算挺多嘛,告诉我贵公司的名字我也来钻研技术,哈哈哈)
2.        Websocket只支持ie10+, chrome, firefox, safari, opera. 不说多了,当前IE9及低版本的用户群体还有很多,你是怎么觉得这些低版本浏览器的实时通讯的?
上面两个问题无非就是涉及到两方面问题:开发成本和浏览器兼容性问题。站在管理者角度来看,都希望尽可能多的压缩开发成本,所有选择第三方推送服务是众多企业的一个理性选择。
选择产品时需要注意以下几点:
1.        代码结构是否清晰易懂
2.        消息到达率怎么样,数据到达情况是否可视
3.        浏览器兼容性如何
4.        产品的稳定性和安全性
好,重点来了,我不去评判其他产品的优缺点,只给出我对我最终选择的产品-GoEasy推送作一个单方面的评价。GoEasy推送满足我上面列的所有指标。
1.        代码结构是否清晰易懂
GoEasy的代码分为订阅和推送两部分:
订阅时,只需要三句代码:
a.        引入goeasy.js (文件非常小)
<script type="text/javascript" src="http://cdn.goeasy.io/goeasy.js"></script>
b.        创建goeasy实例

var goEasy = new GoEasy({appkey: ‘appkey‘});

c.        订阅channel.

                     goEasy.subscribe({

                 channel: ‘channel1‘,

                 onMessage: function(message){

                              alert(‘Meessagereceived:‘+message.content);//接收到推送的消息

                           }

                    });

推送时,用goeasy提供的restful api来实现,api只需要三个参数即可:

URL: http://goeasy.io/goeasy/publish

Method: Post

Parameters: appkey, channel, content

2.        消息到达率怎么样,数据到达情况是否可视
我们项目的用户并发量目前最高在300人的样子,每天会推送30条消息的样子,每条消息的到达情况都可以在goeasy后台页面进行查看。至于到达率,我们项目的对到达率的要求是98%,就目前来看GoEasy应该是100%的到达率。
3.        浏览器兼容性如何
除了常用的浏览器chrome, firefox, safari, opera外还支持IE 6到IE11的版本,低版本IE浏览器GoEasy采用的是polling的方式。GoEasy在兼容性这方面做的很不错。
4.        产品的稳定性和安全性
稳定性的判定:项目已经持续运行了4个月了,没有出现过消息推不出或接收不到的情况。
安全性的判定:GoEasy在安全控制方面主要是通过appkey来控制,创建好app后系统会生成两个key,一个既可以用来接收又可以用来推送,另一个只可以用来接收。所以用户可以选择性的暴露你的key.
这样轻松用PHP实现客户端与服务器端的实时通信了。
 PHP websocket实时消息推送


以上是关于php如何实现websocket的主要内容,如果未能解决你的问题,请参考以下文章

LAMP 专家如何轻松实现 WebSockets?

PHP Websocket实现扫码二维码登录---GoEasy

如何实现从 Redis 中订阅消息转发到 WebSocket 客户端

Websockets是如何实现的?

PHP 实现 WebSocket 协议

如何使用 websocket 实现自动完成?