php webSocket

<?php
class SocketChat
{
    private $timeout = 60;  //超时时间
    private $handShake = False; //默认未牵手
    private $master = 1;  //主进程
    private $port = 9505;  //监听端口
    private $address = '127.0.0.1';//'127.0.0.1';  //监听端口
    private static $connectPool = [];  //连接池
    private static $maxConnectNum = 1024; //最大连接数

    public function __construct( $address, $port = 0 )
    {
        !empty( $port ) && $this->port = $port;
        $this->address = $address;
        $this->startServer();
    }

    //开始服务器
    public function startServer()
    {
        $this->master = socket_create( AF_INET, SOCK_STREAM, SOL_TCP );
        socket_set_option( $this->master, SOL_SOCKET, SO_REUSEADDR, 1);
        socket_bind( $this->master, $this->address, $this->port );
        socket_listen( $this->master);
        if( !$this->master ) throw new \ErrorException("listen {$this->port} fail !");

        self::$connectPool[] = $this->master;

        while( true ){
            $readFds = self::$connectPool;
            //阻塞接收客户端链接
            @socket_select( $readFds, $writeFds, $e = null, $this->timeout );

            foreach( $readFds as $socket ){
                //当前链接 是主进程
                if( $this->master == $socket ){

                    $client = socket_accept( $this->master );  //接收新的链接
                    $this->handShake = False;

                    if ($client < 0){
                        $this->log('clinet connect false!');
                        continue;
                    } else{
                        //超过最大连接数
                        if( count( self::$connectPool ) > self::$maxConnectNum )
                            continue;

                        //加入连接池
                        $this->connect( $client );
                    }

                }else{
                    //不是主进程,开始接收数据
                    $bytes = @socket_recv($socket, $buffer, 2048, 0);
                    //未读取到数据
                    if( $bytes == 0 ){
                        $this->disConnect( $socket );

                    }else{
                        //未握手 先握手
                        if( !$this->handShake ){

                            $this->doHandShake( $socket, $buffer );
                        }else{

                            //如果是已经握完手的数据,广播其发送的消息
                            $buffer = $this->decode( $buffer );
                            $this->parseMessage($buffer,$socket);
                        }
                    }
                }
            }
        }
    }

    //解析发送的数据
    public function parseMessage( $message, $socket )
    {
        //msg type  1 初始化  2 通知  3 一般聊天  4 断开链接  5 获取在线用户 6 通知下线
        $message = json_decode( $message, true );
        $this->sendToAll( $socket, $message );
    }

    //广播所有的客户端(排除自己和master)
    public function sendToAll( $client, $mess )
    {
        foreach( self::$connectPool as $socket ){
            if( $socket != $this->master && $socket != $client  ){
                $this->send( $socket, $mess );
            }
        }
    }

    //处理发送信息
   public function send( $client, $msg )
    {
        $msg = $this->frame( json_encode( $msg ) );
        socket_write( $client, $msg, strlen($msg) );
    }

    //握手协议
    function doHandShake($socket, $buffer)
    {
        list($resource, $host, $origin, $key) = $this->getHeaders($buffer);
        $upgrade  = "HTTP/1.1 101 Switching Protocol\r\n" .
            "Upgrade: websocket\r\n" .
            "Connection: Upgrade\r\n" .
            "Sec-WebSocket-Accept: " . $this->calcKey($key) . "\r\n\r\n";  //必须以两个回车结尾

        socket_write($socket, $upgrade, strlen($upgrade));
        $this->handShake = true;
        return true;
    }

    //获取请求头
    function getHeaders( $req )
    {
        $r = $h = $o = $key = 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]; }
        return [$r, $h, $o, $key];
    }

    //验证socket
    function calcKey( $key )
    {
        //基于websocket version 13
        $accept = base64_encode(sha1($key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true));
        return $accept;
    }


    //打包函数 返回帧处理
    public function frame( $buffer )
    {
        $len = strlen($buffer);
        if ($len <= 125) {

            return "\x81" . chr($len) . $buffer;
        } else if ($len <= 65535) {

            return "\x81" . chr(126) . pack("n", $len) . $buffer;
        } else {

            return "\x81" . char(127) . pack("xxxxN", $len) . $buffer;
        }
    }

    //解码 解析数据帧
    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;
    }

    //客户端链接处理函数
    function connect( $socket )
    {
        array_push( self::$connectPool, $socket );
        $this->log("\n" . $socket . " CONNECTED!");
        $this->log(date("Y-n-d H:i:s"));
    }

    //客户端断开链接函数
    function disConnect( $socket )
    {
        $index = array_search( $socket, self::$connectPool );
        socket_close( $socket );

        $this->log( $socket . " DISCONNECTED!" );
        if ($index >= 0){
            array_splice( self::$connectPool, $index, 1 );
        }
    }

    //系统日志
    public function log( $mess = '' )
    {
        @file_put_contents( './log/' . date("Y-m-d") . ".log", date('Y-m-d H:i:s') . "  " . $mess . PHP_EOL, FILE_APPEND );
    }
}
set_time_limit(0);
new SocketChat('0.0.0.0',9502);

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现基于Websocket 协议的 PHP类库 和 javascript类库 , 实现事件 回调函数的架构开发,以一个简单聊天室实例介绍其使用方法: 不同浏览器,不同电脑,不同地域, 实时通讯。实现的详细 请研究源码。 /***服务端****/ /**  * 实例化  */ $io = new SocketIO('127.0.0.1',8000); /**  * 监听连接  */ $io->on('connect',function($ws,$uid){ $msg = "任意数据类型,结构需要和前端协议,便于通信";     $ws->broadcast(evet, $msg);     //$ws->emit(evet, $uid, $msg); }); /**  * 任意事件,与前端协议好,// evet 由开发者定义  */ $io->on('event',function($ws,$uid,$msg){     $msg = "任意数据类型,结构需要和前端协议,便于通信";     $ws->broadcast(evet, $msg); }); /* * 关闭 */ $io->on('close',function($ws,$uid,$err){     // evet 由开发者定义     $msg = "任意数据类型,结构需要和前端协议,便于通信";     $ws->broadcast('close', $msg); }); /**  * 启动  */ $io->run();   /*** 客户端 ***/ var io = new SocketIO('127.0.0.1',8000); io.on('connect',function(){      console.log('open');      // 发出请求, event 由开发者定义      io.emit(event, user, function(ok){           if(ok){                        }else{                           }      });      // 收到消息, event 由开发者定义      io.on(event, function(msg){                        console.log(msg);      });      //关闭事件  io.on('close', function(){ }); }); 标签:phpws

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值