PHP 用websocket实现客户端和服务器消息双向推送

栏目: PHP · 发布时间: 8年前

内容简介:PHP 用websocket实现客户端和服务器消息双向推送

PHP 实现websocket

html代码

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>发送弹幕</title>
  <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
  <script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script>
  <script src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>

<body>
  <div class="container">
    <div class="row">
      <div class="col-xs-1 col-sm-1 col-md-1 col-lg-1">
      </div>
      <div class="col-xs-10 col-sm-10 col-md-10 col-lg-10">
        <div class="form-group">
          <p style="height:30px"></p>
          <div class="col-sm-2">
          </div>
          <div class="col-sm-4">
            <input type="text" class="form-control" id="barrage" name="barrage" placeholder="弹幕" value="">
          </div>
          <div class="col-sm-4">
            <button type="button" class="btn btn-primary" id="send">发送弹幕</button>
          </div>
        </div>

        <!-- 弹幕内容 -->
        <div class="form-group">
          <p style="height:30px"></p>
          <textarea class="form-control" rows="20" id="content"></textarea>
        </div>
      </div>
      <div class="col-xs-1 col-sm-1 col-md-1 col-lg-1">
      </div>
    </div>
  </div>
</body>
<script>
  $(document).ready(function () {
    var ws = new WebSocket("ws://127.0.0.1:9777");
    ws.onopen = function () {
      console.log("握手成功");
    }
    ws.onmessage = function (e) {
      var content = e.data;
      $('#content').append(content + "\n");
      console.log(content);
    }
    ws.onerror = function () {
      console.log("error");
    }
    $('#send').click(function (e) {
      e.preventDefault();
      var barrage = $('#barrage').val();
      ws.send(barrage);
    });
    $('#barrage').bind('keypress', function (event) {
      if (event.keyCode == "13") {
        var barrage = $('#barrage').val();
        ws.send(barrage);
      }
    });
  });
</script>

</html>

PHP代码

<?php
 
 
class Socket
{
    const BIND_NUM = 20;

    private $master;
    private $sockets = [];
    private $handshake = false; // 握手

    public function __construct($address, $port)
    {
        try {
            // 创建
            $this->master = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
            // 参数
            socket_set_option($this->master, SOL_SOCKET, SO_REUSEADDR, 1);
            socket_set_nonblock($this->master);
            // 绑定
            socket_bind($this->master, $address, $port);
            // 监听
            socket_listen($this->master, static::BIND_NUM);

            $this->sockets[] = $this->master;


            $pid = posix_getpid();
            // 输出
            $this->say("Server Started : " . date('Y-m-d H:i:s'));
            $this->say("Listening on   : " . $address . " port " . $port);
            $this->say("Pid   : " . $pid);
            $this->say("Master socket  : " . $this->master . PHP_EOL);
        } catch (\Exception $e) {
            $this->error();
        }


        while (true) {
            try {
                // 慢点
                usleep(200000);
                $this->doServer();
            } catch (\Exception $e) {
                $this->error();
            }
        }

    }

    /**
     * 开始服务
     */
    public function doServer()
    {
        $write = $except = NULL;
        socket_select($this->sockets, $write, $except, NULL);  //自动选择来消息的socket 如果是握手 自动选择主机

        foreach ($this->sockets as $socket) {
            // 主机
            if ($this->master == $socket) {
                $client = socket_accept($this->master);
                if ($client < 0) {
                    $this->notice("socket_accept() failed");
                    continue;
                } else {
                    $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);
                        $buffer='server say:'.$buffer;
                        $this->send($socket, $buffer);
                    }
                }
            }
        }
    }

    /**
     * 连接
     *
     * @param $socket
     */
    public function connect($socket)
    {
        array_push($this->sockets, $socket);
        $this->say("\n" . $socket . " CONNECTED!");
        $this->say(date("Y-n-d H:i:s"));
    }

    /**
     * 断开连接
     *
     * @param $socket
     */
    public function disConnect($socket)
    {
        $index = array_search($socket, $this->sockets);
        socket_close($socket);
        $this->say($socket . " DISCONNECTED!");
        if ($index >= 0) {
            array_splice($this->sockets, $index, 1);
        }
    }

    /**
     * 握手
     *
     * @param $socket
     * @param $buffer
     * @return bool
     */
    function doHandShake($socket, $buffer)
    {
        $this->say("\nRequesting handshake...");
        $this->say($buffer);
        $key = '';
        if (preg_match("/Sec-WebSocket-Key: (.*)\r\n/", $buffer, $match)) {
            $key = $match[1];
        }
        $this->say("Handshaking...");
        $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";  //必须以两个回车结尾
        $this->say($upgrade);
        socket_write($socket, $upgrade, strlen($upgrade));
        $this->handshake = true;
        $this->say($key);
        $this->say("Done handshaking...");
        return true;
    }

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

    /**
     * 解密
     *
     * @param $buffer
     * @return null|string
     */
    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;
    }

    /**
     * 发送消息
     *
     * @param $client
     * @param $msg
     */
    function send($client, $msg)
    {
        $this->say("> " . $msg);
        $msg = $this->frame($msg);
        socket_write($client, $msg, strlen($msg));
        $this->say("! " . strlen($msg));
    }

    /**
     * 数据帧
     *
     * @param $s
     * @return string
     */
    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;
    }

    /**
     * 标准输出
     *
     * @param string $msg
     */
    public function say($msg = "")
    {
        echo $msg . PHP_EOL;
    }

    /**
     * 异常错误输出
     */
    public function error()
    {
        $error = socket_last_error();
        $error_msg = socket_strerror($error);
        echo $error_msg . PHP_EOL;
    }

    /**
     * 普通错误输出
     *
     * @param string $notice
     */
    public function notice($notice = "")
    {
        echo $notice . PHP_EOL;
    }


}

new Socket('127.0.0.1', 9777);

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Essential PHP Security

Essential PHP Security

Chris Shiflett / O'Reilly Media / 2005-10-13 / USD 29.95

Being highly flexible in building dynamic, database-driven web applications makes the PHP programming language one of the most popular web development tools in use today. It also works beautifully wit......一起来看看 《Essential PHP Security》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具