WebSocket 是什么?

摘抄网上的一些解释:

WebSocket 协议是基于 TCP 的一种新的网络协议。它实现了浏览器与服务器全双工(full-duplex)通信——允许服务器主动发送信息给客户端。

WebSocket 通信协议于2011年被 IETF 定为标准 RFC 6455,并被 RFC7936 所补充规范。

—— 百度百科

WebSocket 是一个持久化的协议,这是相对于 http 非持久化来说的。

举个简单的例子,http1.0 的生命周期是以 request 作为界定的,也就是一个 request,一个 response,对于 http 来说,本次 client 与 server 的会话到此结束;而在 http1.1 中,稍微有所改进,即添加了 keep-alive,也就是在一个 http 连接中可以进行多个 request 请求和多个 response 接受操作。然而在实时通信中,并没有多大的作用,http 只能由 client 发起请求,server 才能返回信息,即 server 不能主动向 client 推送信息,无法满足实时通信的要求。而 WebSocket 可以进行持久化连接,即 client 只需进行一次握手,成功后即可持续进行数据通信,值得关注的是 WebSocket 实现 client 与 server 之间全双工通信,即 server 端有数据更新时可以主动推送给 client 端。

上图是一个演示client和server之间建立WebSocket连接时握手部分

client 建立 WebSocket 时向服务器端请求的信息

GET /chat HTTP/1.1 
  Host: server.example.com 
  Upgrade: websocket //告诉服务器现在发送的是WebSocket协议
  Connection: Upgrade 
  Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw== //是一个Base64 encode的值,这个是浏览器随机生成的,用于验证服务器端返回数据是否是WebSocket助理
  Sec-WebSocket-Protocol: chat, superchat 
  Sec-WebSocket-Version: 13 
  Origin: http://example.com

服务器获取到 client 请求的信息后,根据 WebSocket 协议对数据进行处理并返回,其中要对 Sec-WebSocket-Key 进行加密等操作

HTTP/1.1 101 Switching Protocols 
  Upgrade: websocket //依然是固定的,告诉客户端即将升级的是Websocket协议,而不是mozillasocket,lurnarsocket或者shitsocket
  Connection: Upgrade 
  Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk= //这个则是经过服务器确认,并且加密过后的 Sec-WebSocket-Key,也就是client要求建立WebSocket验证的凭证
  Sec-WebSocket-Protocol: chat

PHP 服务端

<?php

if(($socket = socket_create(AF_INET,SOCK_STREAM,SOL_TCP)) < 0) {
    echo "socket_create() 失败的原因是:".socket_strerror($sock)."\n";
}
if(($ret = socket_bind($socket,'127.0.0.1','9090')) < 0) {
    echo "socket_bind() 失败的原因是:".socket_strerror($ret)."\n";
}
if(($ret = socket_listen($socket,3)) < 0) {
    echo "socket_listen() 失败的原因是:".socket_strerror($ret)."\n";
}

$all_sockets = [$socket];    // socket 集合

do {
    $copy_sockets = $all_sockets;   // 单独拷贝一份

    // 因为客户端是长连接,如果客户端非正常断开,服务端会在 socket_accept 阻塞,现在使用 select 非阻塞模式 socket
    if(socket_select($copy_sockets, $write, $except, 0) === false)
        exit('sosket_select error!');

    // 接收第一次 socket 连入,连入后移除服务端 socket
    if(in_array($socket, $copy_sockets)) {
        $client = socket_accept($socket);
        if($client) {
            $buf = socket_read($client, 1024);
            echo $buf;

            // 匹配 Sec-Websocket-Key 标识
            if (preg_match("/Sec-WebSocket-Key: (.*)\r\n/i",$buf,$match)) {
                // 需要将 Sec-WebSocket-Key 值累加字符串,并依次进行 SHA-1 加密和 base64 加密
                $key = base64_encode(sha1($match[1] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11',true));
                // 拼凑响应内容
                $res= "HTTP/1.1 101 Switching Protocol".PHP_EOL
                    ."Upgrade: WebSocket".PHP_EOL
                    ."Connection: Upgrade".PHP_EOL
                    ."WebSocket-Location: ws://127.0.0.1:9090".PHP_EOL
                    ."Sec-WebSocket-Accept: " . $key .PHP_EOL.PHP_EOL;  // 注意这里,需要两个换行
                // 向客户端应答 Sec-WebSocket-Accept
                socket_write($client, $res, strlen($res));
                // 向客户端发送消息
                socket_write($client, buildMsg('socket ok'), 1024);

                // 加入客户端 socket
                $all_sockets[] = $client;
            }
            // 移除服务端 socket
            $key = array_search($socket, $copy_sockets);
            unset($copy_sockets[$key]);

            // socket_close($client);
        }
    }

    // 循环所有客户端 sockets
    foreach ($copy_sockets as $s) {
        // 获取客户端发给服务端的内容
        $buf = socket_read($s, 8024);
        echo strlen($buf).'---'.PHP_EOL;
        // 代表客户端主动关闭
        if(strlen($buf) < 9) {
            $key = array_search($s, $all_sockets);
            unset($all_sockets[$key]);
            socket_close($s);
            continue;
        }
        // 输出
        echo getMsg($buf).PHP_EOL;
    }

}while(true);
socket_close($socket);

// 编码服务端向客户端发送的内容
function buildMsg($msg) {
    $frame = [];
    $frame[0] = '81';
    $len = strlen($msg);
    if ($len < 126) {
        $frame[1] = $len < 16 ? '0' . dechex($len) : dechex($len);
    } else if ($len < 65025) {
        $s = dechex($len);
        $frame[1] = '7e' . str_repeat('0', 4 - strlen($s)) . $s;
    } else {
        $s = dechex($len);
        $frame[1] = '7f' . str_repeat('0', 16 - strlen($s)) . $s;
    }
    $data = '';
    $l = strlen($msg);
    for ($i = 0; $i < $l; $i  ) {
        $data .= dechex(ord($msg{$i}));
    }
    $frame[2] = $data;
    $data = implode('', $frame);
    return pack("H*", $data);
}

// 解析客户端向服务端发送的内容
function getMsg($buffer) {
    $res = '';
    $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  ) {
        $res .= $data[$index] ^ $masks[$index % 4];
    }
    return $res;
}

客户端

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script>
        // 创建一个Socket实例
        var socket = new WebSocket('ws://localhost:9090');

        // 打开Socket
        socket.onopen = function(event) {
            // 发送一个初始化消息
            socket.send("init msg");

        };
        socket.onmessage = function(event) {
            console.log('收到消息',event);

        };

        // 监听Socket的关闭
        socket.onclose = function(event) {
            console.log('关闭监听',event);
        };

        function  send()
        {
            socket.send("client msg");
        }
    </script>
</head>
<body>
<button onclick="send()">发送消息</button>
</body>
</html>

运行测试:

Client

Server

到此这篇关于PHP实现WebSocket实例详解的文章就介绍到这了,更多相关PHP实现WebSocket内容请搜索Devmax以前的文章或继续浏览下面的相关文章希望大家以后多多支持Devmax!

PHP实现WebSocket实例详解的更多相关文章

  1. 五分钟学会HTML5的WebSocket协议

    这篇文章主要介绍了五分钟学会HTML5的WebSocket协议,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  2. 前端监听websocket消息并实时弹出(实例代码)

    这篇文章主要介绍了前端监听websocket消息并实时弹出,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  3. HTML5 WebSocket实现点对点聊天的示例代码

    这篇文章主要介绍了HTML5 WebSocket实现点对点聊天的示例代码的相关资料,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  4. ios – Websockets可以在移动电话上工作吗?

    相关地,我怀疑长轮询客户端可能是实现类似功能的好方法,但我想知道我可能遇到的移动特定问题.到目前为止,我已经读过长时间的轮询请求可能会对电池寿命产生相当大的影响.我还听说iOS以某种方式限制了对单个服务器的连接数量,这可能是个问题.有没有人在使用实时组件的移动应用程序上工作?

  5. ios-swift,objective-c协议实现

    作为隐式解开的可选项.

  6. 从iOS应用程序发送帖子到PHP脚本不工作…简单的解决方案就像

    我之前已经做了好几次了但是由于某些原因我无法通过这个帖子…我尝试了设置为_POST且没有的变量的PHP脚本……当它们未设置为发布时它工作精细.这是我的iOS代码:这里是PHP的一大块,POST变量不在正确的位置?我想这对于更有经验的开发人员来说是一个相当简单的答案,感谢您的帮助!解决方法$_POST是一个数组,而不是一个函数.您需要使用方括号来访问数组索引:

  7. ios – 红蜘蛛代表没有被召集

    变量不是nil,我有一个很好的连接,url是正确的,但没有调用委托方法.我也正在实现WebSocketDelegate解决方法套接字应该是您的类的属性或变量,以确保它附近.如果仅在函数堆栈上分配它,它将超出范围,并且永远不会调用委托以下是我在项目中使用的代码,以防万一这是link到故事板,以防万一你想要

  8. swift 实现websocket与后台通信(swift 如何构建简单的json字符串)

    一个应用不可避免要与服务器进行通信,主要有,http与socket。http暂时不论,我们先看看socket下面衍生的websocket,今天我就把自己怎么利用websocket与服务器进行交互记录下来:首先你需要集成websocket到自己的项目,如果不明白如何集成,请看上一篇《swift集成websocket库》集成websocket到自己项目后还需要添加SwiftyJSON到自己项目,具体步骤和集成websocket一样。首先打开你项目,记得通过cocoapods生成的.xcworkspace文件打

  9. swift学习2 元组 tuples

    swift中出现了一种新的数据结构,非常牛掰的元组tuples如果懂PHP的猿,会发现这个元组和PHP的数组非常类似,同样是可以默认不指定key,也可以指定key目前的学习疑问是,如何进行元组的遍历?

  10. 尝试使用swift mailer,gmail smtp,php发送邮件

    这里是我的代码:在运行时出现此错误…

随机推荐

  1. PHP个人网站架设连环讲(一)

    先下一个OmnihttpdProffesinalV2.06,装上就有PHP4beta3可以用了。PHP4给我们带来一个简单的方法,就是使用SESSION(会话)级变量。但是如果不是PHP4又该怎么办?我们可以假设某人在15分钟以内对你的网页的请求都不属于一个新的人次,这样你可以做个计数的过程存在INC里,在每一个页面引用,访客第一次进入时将访问时间送到cookie里。以后每个页面被访问时都检查cookie上次访问时间值。

  2. PHP函数学习之PHP函数点评

    PHP函数使用说明,应用举例,精简点评,希望对您学习php有所帮助

  3. ecshop2.7.3 在php5.4下的各种错误问题处理

    将方法内的函数,分拆为2个部分。这个和gd库没有一点关系,是ecshop程序的问题。会出现这种问题,不外乎就是当前会员的session或者程序对cookie的处理存在漏洞。进过本地测试,includes\modules\integrates\ecshop.php这个整合自身会员的类中没有重写integrate.php中的check_cookie()方法导致,验证cookie时返回的username为空,丢失了登录状态,在ecshop.php中重写了此方法就可以了。把他加到ecshop.php的最后面去就可

  4. NT IIS下用ODBC连接数据库

    $connection=intodbc_connect建立数据库连接,$query_string="查询记录的条件"如:$query_string="select*fromtable"用$cur=intodbc_exec检索数据库,将记录集放入$cur变量中。再用while{$var1=odbc_result;$var2=odbc_result;...}读取odbc_exec()返回的数据集$cur。最后是odbc_close关闭数据库的连接。odbc_result()函数是取当前记录的指定字段值。

  5. PHP使用JpGraph绘制折线图操作示例【附源码下载】

    这篇文章主要介绍了PHP使用JpGraph绘制折线图操作,结合实例形式分析了php使用JpGraph的相关操作技巧与注意事项,并附带源码供读者下载参考,需要的朋友可以参考下

  6. zen_cart实现支付前生成订单的方法

    这篇文章主要介绍了zen_cart实现支付前生成订单的方法,结合实例形式详细分析了zen_cart支付前生成订单的具体步骤与相关实现技巧,需要的朋友可以参考下

  7. Thinkphp5框架实现获取数据库数据到视图的方法

    这篇文章主要介绍了Thinkphp5框架实现获取数据库数据到视图的方法,涉及thinkPHP5数据库配置、读取、模型操作及视图调用相关操作技巧,需要的朋友可以参考下

  8. PHP+jquery+CSS制作头像登录窗(仿QQ登陆)

    本篇文章介绍了PHP结合jQ和CSS制作头像登录窗(仿QQ登陆),实现了类似QQ的登陆界面,很有参考价值,有需要的朋友可以了解一下。

  9. 基于win2003虚拟机中apache服务器的访问

    下面小编就为大家带来一篇基于win2003虚拟机中apache服务器的访问。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  10. Yii2中组件的注册与创建方法

    这篇文章主要介绍了Yii2之组件的注册与创建的实现方法,非常不错,具有参考借鉴价值,需要的朋友可以参考下

返回
顶部