下面先用一个图例来演示clinet和server之间建立websocket连接时握手部分,这个部分在nodejs中可以十分轻松的完成,因为node提供的net模块已经对socket套接字做了封装处理,开发者使用的时候只需要考虑数据的交互而不用处理连接的建立。而php没有,从socket的连接、建立、绑定、监听等,这些都需要自己去操作,如下
1和2实际上就是一个HTTP的请求和响应,只不过我们在处理的过程中拿到的是没有经过解析的字符串。如:
GET /chat HTTP /1.1
Host: server.example.com
Origin:  http://example.com
一、php中处理websocket
webSocket连接是由客户端主动发起的,所以一切要从客户端出发。第一步是要解析拿到客户端发过来的Sec-WebSocket-Key字符串。
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin:  Http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
client请求的格式(如上),首先php建立一个socket连接,监听端口的信息。
1. socket连接的建立
关于socket套接字的建立,下面一张连接建立的过程:
// 建立一个socket套接字
$master = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_option($master, SOL_SOCKET, SO_REUSEADDR, 1);
socket_bind($master, $address, $port);
socket_listen($master);
相比node,这个地方处理比较麻烦,上面几行代码并未建立连接,只不过这些代码是建立一个socket套接字必须要写的东西。由于处理过程稍微复杂,所以我把各种处理写进一个类中,方便管理和调用。
<?php
// demo.php
Class WS {
var $master; // 连接server的client
var $sockets = array(); // 不同状态的socket管理
var $handshake = false; // 判断是否握手
function __construct($address, $port) {
// 建立一个socket套接字
$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, 2) or die("socket_listen() failed");
$this->sockets[] = $this->master;
}
// debug
echo("Master socket :" . $this->master. "\n");
while(true) {
// 自动选择来消息的socket 如果是握手 自动选择主机
$write = NULL;
$except = NULL;
socket_select($this->sockets, $write, $except, NULL);
foreach ($this->sockets as $socket) {
// 连接主机的client
if ($socket == $this->master) {
$client = socket_accept($this->master);
if ($client < 0) {
// debug
echo "socket_accept() failed";
continue;
} else {
// connect($client);
array_push($this->sockets, $client);
echo "connect client\n";
}
} else {
$bytes = @socket_recv($socket, $buffer, 2048, 0);
if ($bytes == 0) return;
if (!$this->handshake) {
// 如果没有握手,先握手回应
// doHandShake($socket, $buffer);
echo "shakeHands \n";
} else {
// 如果已经握手,直接接受数据,并处理
$buffer = decode($buffer);
// process($socket, $buffer);
echo "send file\n";
}
}
}
}
}
上面这段代码如果要测试,可以在cmd命令行中键入php /path/to/demo.php,还需要新建一个实例。
$ws = new WS('localhost', 4000);
客户端代码可以稍微简单点:
创建一个websocket.html
<html>
     <head>
          <meta charset="utf-8" />
          <title>test</title>
     </head>
     <body>
          <script>
               var ws = new WebSocket("ws://localhost:4000");
               ws.onopen = function(){
                   console.log("握手成功");
               };
               ws.onerror = function(){
                   console.log("error");
               };
          </script>
     </body>
</html>
window doc环境下执行php demo.php
浏览器访问websocket.html页面时,doc环境下监听
通过上面代码能够看到整个流程,首先是建立连接,node中这一步已经封装到了net和http模块,然后判断是否握手,如果没有的话,就shakeHands。这里握手直接echo一个单词,表示进行这个东西。
2、计算Sec-WebSocket-Accpet值
function calcKey($key){
        //基于websocket version 13
        $accept = base64_encode(sha1($key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true));
        return $accept;
    }
3、将SHA-1加密后的字符串再进行一次base64加密。如果加密算法错误,客户端在进行校验的时候会直接报错:
4、应答Sec-WebSocket-Accept
function dohandshake($socket, $req) {
// 获取加密key
$acceptKey = $this->encry($req);
$upgrade = "HTTP/1.1 101 Switching Protocols\r\n" .
"Upgrade: websocket\r\n" .
"Connection: Upgrade\r\n" .
"Sec-WebSocket-Accept: " . $acceptKey . "\r\n" .
"\r\n";
// 写入socket
socket_write(socket, $upgrade.chr(0), strlen($upgrade.chr(0)));
// 标记握手已经成功,下次接受数据采用数据帧格式
$this->handshake = true;
}
这里要注意,每一个请求和相应的格式,最后有一个空行,也就是\r\n。
当客户端成功校验key后,会触发onopen函数:
5.数据帧处理
// 解析数据帧
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 send($client, $msg){$msg = $this->frame($msg);socket_write($client, $msg, strlen($msg));
}
客户端代码:
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");
};
ws.send("李靖");
在连通之后发送数据,服务器原样返回:
二、注意问题

1. websocket 版本问题

客户端在握手时的请求中有Sec-WebSocket-Version: 13,这样的版本标识,这个是一个升级版本,现在的浏览器都是使用的这个版本。而以前的版本在数据加密的部分更加麻烦,它会发送两个key:

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Origin: http://example.comSec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Key1: xxxx
Sec-WebSocket-Key2: xxxx
如果是这种版本(比较老,已经没在使用了),需要通过下面的方式获取
function encry($key1,$key2,$l8b){//Get the numberspreg_match_all('/([\d]+)/', $key1, $key1_num);preg_match_all('/([\d]+)/', $key2, $key2_num);$key1_num = implode($key1_num[0]);$key2_num = implode($key2_num[0]);//Count spacespreg_match_all('/([ ]+)/', $key1, $key1_spc);preg_match_all('/([ ]+)/', $key2, $key2_spc);if($key1_spc==0|$key2_spc==0){ $this->log("Invalid key");return; }//Some math$key1_sec = pack("N",$key1_num / $key1_spc);$key2_sec = pack("N",$key2_num / $key2_spc);return md5($key1_sec.$key2_sec.$l8b,1);
}
nodeJs的websocket操作方式:
//服务器程序var crypto = require('crypto');
var WS = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
require('net').createServer(function(o){var key;o.on('data',function(e){if(!key){//握手key = e.toString().match(/Sec-WebSocket-Key: (.+)/)[1];key = crypto.createHash('sha1').update(key + WS).digest('base64');o.write('HTTP/1.1 101 Switching Protocols\r\n');o.write('Upgrade: websocket\r\n');o.write('Connection: Upgrade\r\n');o.write('Sec-WebSocket-Accept: ' + key + '\r\n');o.write('\r\n');}else{console.log(e);};});
}).listen(8000);

websocket-php相关推荐

  1. mqtt+htttp+websocket

    一.介绍 1.参考网址1:WebSocket协议:5分钟从入门到精通 2.参考网址2:WebSocket 教程(阮一峰) 二.应用 1.参考网址1:从 HTTP 到 MQTT:一个移动后端案例概述 2 ...

  2. Java后端WebSocket的Tomcat实现

    转自: http://blog.chenzuhuang.com/archive/28.html http://www.cnblogs.com/xdp-gacl/p/5193279.html 一.Web ...

  3. Java Websocket实例【服务端与客户端实现全双工通讯】

    Java Websocket实例[服务端与客户端实现全双工通讯] 现很多网站为了实现即时通讯,所用的技术都是轮询(polling).轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发 出HTTP ...

  4. 物联网协议对比(HTTP、websocket、XMPP、COAP、MQTT和DDS协议)

    目录 1.HTTP和websocket 2.XMPP 3.COAP 4.MQTT协议 5.DDS 对于物联网,最重要的是在互联网中设备与设备的通讯,现在物联网在internet通信中比较常见的通讯协议 ...

  5. node.js创建WebSocket服务,并使用原生js ES6完成对WebSocket数据交互

    注意,前情提示: 本代码基于<Node.js(nodejs)对本地JSON文件进行增.删.改.查操作(轻车熟路)> 传送门Node.js(nodejs)对本地JSON文件进行增.删.改.查 ...

  6. 使用CEfSharp之旅(7)CEFSharp 拦截 http 请求 websocket 内容

    使用CEfSharp之旅(7)CEFSharp 拦截 http 请求 websocket 内容 原文:使用CEfSharp之旅(7)CEFSharp 拦截 http 请求 websocket 内容 版 ...

  7. 使用Node.js快速搭建WebSocket server

    原文地址:http://my.oschina.net/yushulx/blog/309413 目录[-] 安装 服务端 客户端 参考 安装 ? 1 npm install ws 服务端 server. ...

  8. python java web_Python 与 Java 使用 websocket 通信

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

  9. Java项目:在线淘房系统(租房、购房)(java+SpringBoot+Redis+MySQL+Vue+SpringSecurity+JWT+ElasticSearch+WebSocket)

    源码获取:博客首页 "资源" 里下载! 该系统有三个角色,分别是:普通用户.房屋中介.管理员.普通用户的功能:浏览房屋信息.预约看房.和中介聊天.申请成为中介等等.房屋中介的功能: ...

  10. websocket心跳链接代码_WebSocket原理与实践(五)--心跳及重连机制

    在使用websocket的过程中,有时候会遇到网络断开的情况,但是在网络断开的时候服务器端并没有触发onclose的事件.这样会有:服务器会继续向客户端发送多余的链接,并且这些数据还会丢失.所以就需要 ...

最新文章

  1. Linux终端C语言实现图片拷贝
  2. Knockout 新版应用开发教程之visible绑定
  3. 5G NR — 基础知识
  4. 集群、分布式、负载均衡区别
  5. plaxis 2d 2020中文版
  6. 解决 Python pip 安装报错:Retrying (Retry(total=4, connect=None, read=None ...)) after connection
  7. uni.$emit和uni.$on用法;uni-app微信小程序页面通讯;微信小程序页面通讯
  8. 提速30倍!这个加速包让Python代码飞起来
  9. 【思科模拟器实验】交换机路由器基本配置
  10. CMFCMenuBar 的另类动态修改
  11. 二维卷积与一维卷积区别
  12. apache评分表的意义_APACHE 评分
  13. JAX-RS 从傻逼到牛叉 2:开发一个简单的服务
  14. 为什么要推销自己_推销自己:为什么? 如何!
  15. 《C Prime Plus》(第六版) 第05章 运算符、表达式和语句 例题集和编程练习
  16. c语言isfinite_visual-c-std :: isfinite在MSVC上
  17. 学习大数据,大数据专家写给大数据分析学习者的10个理由
  18. OpenCV视频篇——颜色跟踪
  19. vlan配置实例详解_网工知识角|MUXVLAN技术详解,基本原理一篇搞定
  20. 由IRR看超越方程求解

热门文章

  1. win8 如何摄像头测试软件,高手解说win8系统摄像头检测不到的设置教程
  2. 倾听:不只是听见(倾听有哪些挑战?如何利用倾听技巧更好的理解他人)
  3. 21届秋招记录——银行篇
  4. 无线蓝牙耳机手机端app开发_汪峰耗时1500天造了一款耳机,秒杀苹果AirPods!
  5. 无线网主域名服务器,主域名服务器和网关的关系
  6. Tuner及工作原理介绍
  7. flask学习笔记一:app.run
  8. java google map_java如何通过google map api实现地址解析
  9. 将网站封装成APP安卓应用
  10. 进程与程序的联系与区别