zoukankan      html  css  js  c++  java
  • php实现socket推送技术

    在socket出现之前已经有ajax定时请求、长轮询等方案,但都不能满足需求,socket就应用而生了。

    socket基本函数socket

    总结下常用的socket函数

    服务端: socket_create 创建socket设置基本参数 

         socket_bind 绑定ip和端口号   

         socket_listen 监听

         socket_accept 客户端的连接

         socket_read 读取客户端的数据

         socket_write 给单独客户端发送数据 

         socket_close 关闭连接

    客户端:socket_create 创建socket设置基本参数 

         socket_connect 连接socket

         socket_write 给服务端发送数据

         socket_read 读取服务端数据

         socket_close 关闭连接

    H5websocket不多说了,上链接

    OK,开始贴代码~

    ----------------------------------------------------------分割线

    服务端代码:

    <?php
    class WS {
        var $master;
        var $sockets = array();
        var $debug = false;//true为调试模式,输出log日志
        var $handshake = array();
    
        function __construct($address, $port){
            $this->master=socket_create(AF_INET, SOCK_STREAM, SOL_TCP)     or die("socket_create() failed");
            socket_set_option($this->master, SOL_SOCKET, SO_REUSEADDR, 1)  or die("socket_option() failed");
            socket_bind($this->master, $address, $port)                    or die("socket_bind() failed");
            socket_listen($this->master,20)                                or die("socket_listen() failed");
            
            $this->sockets[] = $this->master;
            $this->say("Server Started : ".date('Y-m-d H:i:s'));
            $this->say("Listening on   : ".$address." port ".$port);
            $this->say("Master socket  : ".$this->master."
    ");
            
            while(true){
                $socketArr = $this->sockets;
                $write = NULL;
                $except = NULL;
                socket_select($socketArr, $write, $except, NULL);  //自动选择来消息的socket 如果是握手 自动选择主机
                foreach ($socketArr as $socket){
                    if ($socket == $this->master){  //主机
                        $client = socket_accept($this->master);
                        if ($client < 0){
                            $this->log("socket_accept() failed");
                            continue;
                        } else{
                            $this->connect($client);
                        }
                    } else {
                        $bytes = @socket_recv($socket,$buffer,2048,0);
                        if ($bytes == 0){
                            $this->disConnect($socket);
                        }
                        else{
                            $key = array_search($socket, $this->sockets);
                            if (empty($this->handshake) || !isset($this->handshake[$key]) || !$this->handshake[$key]){
                                $this->doHandShake($socket, $buffer, $key);
                            }
                            else{
                                $buffer = $this->decode($buffer);
                                echo $buffer.PHP_EOL;
                                $key = array_search($socket, $this->sockets);
                                $arr = $this->sockets;
                                array_shift($arr);
                                foreach ($arr as $s){
                                    $this->send($s, $buffer);
                                }
                            }
                        }
                    }
                }
            }
        }
        
        function send($client, $msg){
            $msg = $this->frame($msg);
            socket_write($client, $msg, strlen($msg));
        }
        function connect($socket){
            array_push($this->sockets, $socket);
            $this->say("
    " . $socket . " CONNECTED!");
            $this->say(date("Y-n-d H:i:s"));
        }
        function disConnect($socket){
            $index = array_search($socket, $this->sockets);
            socket_close($socket);
            $this->say($socket . " DISCONNECTED!");
            if ($index >= 0){
                echo 'unset index is:'.PHP_EOL;
                unset($this->sockets[$index]);
            }
        }
        function doHandShake($socket, $buffer, $handKey){
            $this->log("
    Requesting handshake...");
            $this->log($buffer);
            list($resource, $host, $origin, $key) = $this->getHeaders($buffer);
            $this->log("Handshaking...");
            $upgrade  = "HTTP/1.1 101 Switching Protocol
    " .
                        "Upgrade: websocket
    " .
                        "Connection: Upgrade
    " .
                        "Sec-WebSocket-Accept: " . $this->calcKey($key) . "
    
    ";  //必须以两个回车结尾
            $this->log($upgrade);
            $sent = socket_write($socket, $upgrade, strlen($upgrade));
            $this->handshake[$handKey]=true;
            $this->log("Done handshaking...");
            return true;
        }
    
        function getHeaders($req){
            $r = $h = $o = $key = null;
            if (preg_match("/GET (.*) HTTP/"              ,$req,$match)) { $r = $match[1]; }
            if (preg_match("/Host: (.*)
    /"             ,$req,$match)) { $h = $match[1]; }
            if (preg_match("/Origin: (.*)
    /"           ,$req,$match)) { $o = $match[1]; }
            if (preg_match("/Sec-WebSocket-Key: (.*)
    /",$req,$match)) { $key = $match[1]; }
            return array($r, $h, $o, $key);
        }
    
        function calcKey($key){
            //基于websocket version 13
            $accept = base64_encode(sha1($key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true));
            return $accept;
        }
    
        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 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;
        }
    
        
        function say($msg = ""){
            echo $msg . "
    ";
        }
        function log($msg = ""){
            if ($this->debug){
                echo $msg . "
    ";
            } 
        }
    }
        
    
    new WS('localhost', 4000);

    服务端代码2(swoole): 如果集成了swoole扩展

            //创建websocket服务器对象,监听0.0.0.0:9502端口
            $ws = new swoole_websocket_server("0.0.0.0", 4000);
    
            //监听WebSocket连接打开事件
            $ws->on('open', function ($ws, $request) {
                var_dump($request->fd, $request->get, $request->server);
                $ws->push($request->fd, "hello, welcome
    ");
            });
    
            //监听WebSocket消息事件
            $ws->on('message', function ($ws, $frame) {
                echo "Message: {$frame->data}
    ";
                $ws->push($frame->fd, "server: {$frame->data}");
            });
    
            //监听WebSocket连接关闭事件
            $ws->on('close', function ($ws, $fd) {
                echo "client-{$fd} is closed
    ";
            });
    
            $ws->start();

    客户端代码(H5):

    <html>
        <head>
            <title>demo</title>
            <script src="https://cdn.bootcss.com/jquery/1.9.1/jquery.min.js"></script>
        </head>
        <body>
        <input type="text" id="content">
        <input type="button" value="send" id="send">
            <script type="text/javascript">
                var ws = new WebSocket("ws://localhost:4000");
                ws.onopen = function(){
                    console.log("握手成功");
                }
                ws.onmessage = function(e){
                    console.log("message:" + e.data);
                }
                ws.onerror = function(){
                    console.log("error");
                }
                $("#send").click(function(){
                    content = $("#content").val();
                    console.log(content);
                    ws.send(content);
                })
            </script>
        </body>
    </html>

    然后执行php demo.php 开启socket(从运维那偷学一招,linux下执行nohup php demo.php &可以在后台执行),浏览器打开多个index.html,就能建立通讯了。

    代码解析:

    1.属性$sockets数组保存每个accept连接(不知道这么描述对不对); 

    2.属性$handshake数组保存连接是否在连接状态;

    转:https://www.cnblogs.com/kkform/p/8072836.html

    相关:https://www.cnblogs.com/loveyoume/p/6076101.html

  • 相关阅读:
    Python 学习日记 第七天
    Python 学习日记 第六天
    Python 学习日记 第五天
    Python 学习日记 第四天
    Redis 中的数据类型及基本操作
    Asp.net mvc 中View 的呈现(二)
    Asp.net mvc 中View的呈现(一)
    Asp.net mvc 中Action 方法的执行(三)
    Asp.net mvc 中Action 方法的执行(二)
    Asp.net mvc 中Action 方法的执行(一)
  • 原文地址:https://www.cnblogs.com/fps2tao/p/9273229.html
Copyright © 2011-2022 走看看