PHP webSocket实现网页聊天室

一.简介

http请求只能由客户端主动发起,服务器响应的模式, 服务器无法主动向客户端推数据,websocket的出现完美的解决了这一问题。
websocket和http处于同一层,都是基于TCP协议的,客户端和服务器使用websocket通讯的时候需要握手和传输数据两步,
握手借助http状态码101 switch protocol从http协议转换到websocket协议,之后便和http协议无关了。

二.握手

websocket首先由浏览器主动发起一个http请求,主要请求头内容如下:
Connection: 告知服务器当前请求连接是升级的
Upgrade: websocket Upgrade 告诉服务器这个http链接是升级的websocket链接
Sec-WebSocket-Version: 13 协议版本
Sec-WebSocket-Key: IYiYjdXLDgHybP4teMOnsQ== 验证key

服务器响应头如下
HTTP/1.1 101 Switching Protocols 表示变换协议
Upgrade: websocket
Connection:Upgrade 服务器返回的告知客户端同意使用升级并使用websocket协议,用来完善HTTP升级响应
Sec-WebSocket-Accept:Ev/nT3aIpWH9deAfyYMPbBwkQWo= 客户端 Sec-WebSocket-Key经过加密后的字符串算法 base64_encode(sha1(Sec-WebSocket-Key + 258EAFA5-E914-47DA-95CA-C5AB0DC85B11));

三.数据幀构造和解析

  0                   1                   2                   3
  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 +-+-+-+-+-------+-+-------------+-------------------------------+
 |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
 |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
 |N|V|V|V|       |S|             |   (if payload len==126/127)   |
 | |1|2|3|       |K|             |                               |
 +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
 |     Extended payload length continued, if payload len == 127  |
 + - - - - - - - - - - - - - - - +-------------------------------+
 |                               |Masking-key, if MASK set to 1  |
 +-------------------------------+-------------------------------+
 | Masking-key (continued)       |          Payload Data         |
 +-------------------------------- - - - - - - - - - - - - - - - +
 :                     Payload Data continued ...                :
 + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
 |                     Payload Data continued ...                |
 +---------------------------------------------------------------+

构造协议文本幀的算法(PHP)

其中的opcode为1代表一个文本帧

private function encode($data){
    $len = strlen($data);
    $encode = ‘‘;
    if($len < 126){
        $encode = chr(0x81) . chr($len) . $data;
    }else if($len  >= 126 && $len <= 65535){
        $low = $len & 0x00FF;
        $high = ($len & 0xFF00) >> 8;
        $encode = chr(0x81) . chr(0x7F) . chr($high) . chr($low) . $data;
    }else{
        encode = chr(0x81) .
    }
    return $encode;
}

如果playload len < 126,则playloadlen 是数据的真实长度
如果playload len = 126,数据的长度等于playload len后面2个字节对应的无符号整数就是数据的真实长度
如果playload len = 127,数据的长度等于playload len后面8个字节对应的无符号整数就是数据的真实长度

之前对位运算并不熟悉,这里也写下构建数据帧详细的步骤
php使用chr将数据转换为标准ascii所指定的单个字符
长度 < 126 FIN + RSV1 + RSV2 + RSV3 + opcode = 0x81 = 10000001, 再加上数据长度和数据
长度 >=126 <= 65535 FIN + RSV1 + RSV2 + RSV3 + opcode = 0x81 = 10000001 加上 Payload len = 0x7E = 126 由于ASCII范围为 0-127即1个字节,所以必须将2个字节拆分成单个字节即高位$high和低位$low来表示
$low = $len & (11111111 = 0x00FF)这样就取得了$len第二个字节的值。 因为$len是两个字节 取第一个字节的值需要 $len & (1111111100000000 = 0xFF00) 然后向右移8个位

解析文本帧的算法

private function decode($data){
    if(!$data) return array();
    //第一个字节和00001111按位与运算取得的后4位数据就是opcode
    $opcode = ord(substr($data, 0, 1)) & 0x0f;
    //第二个字节和10000000按位与运算,保留第一位的值,然后右移7位取得的就是ismask
    $ismask = (ord(substr($data, 1, 1)) & 0x80) >> 7;
    //第二个字节和01000000按位与运算取得后7位的值就是playloadlen
    $playloadlen = ord(substr($data, 1, 1)) & 0x7f;
    $cdata = $maskkey = $decode = ‘‘;
    if($playloadlen < 126){
        $maskkey = substr($data, 2, 4);
        $cdata = substr($data, 6);
    }else if($playloadlen == 126){
        $maskkey = substr($data, 4, 4);
        $cdata = substr($data, 8);
    }else if($playloadlen == 127){
        $maskkey = substr($data, 10, 4);
        $cdata = substr($data, 14);
    }
    if($cdata && $maskkey){
        for($i = 0; $i < strlen($cdata); $i++){
            $decode{$i} = $cdata{$i} ^ $maskkey[$i % 4];
        }
        $decode = join(‘‘, $decode);
    }
    return array($opcode, $ismask, $decode);
}

websocket规定客户端发送给服务端的数据必须经过掩码处理,服务器端发送给客户端的数据无需掩码处理,
解码算法: 将playload的原始数据的每个字符下标与4取模,然后将这个原始字符与前面取模后相应位置的掩码字符进行异或运算即可
data[i] = source[i] ^ maskkey[i / 4];

四.PHP服务端

之前对于socket的select方法也不是很了解,

function socket_select (array &$read, array &$write, array &$except, $tv_sec, [, int $tv_usec = 0 ])

1.新连接到来时,被监听的端口是活跃的,如果是新数据到来或者客户端关闭链接时,活跃的是对应的客户端socket而不是服务器上被监听的端口
2.如果客户端发来数据没有被读走,则socket_select将会始终显示客户端是活跃状态并将其保存在read数组中
3.如果客户端先关闭了,则必须手动关闭服务器上相对应的客户端socket,否则socket_select也始终显示该客户端活跃(这个道理跟"有新连接到来然后没有用socket_access把它读出来,导致监听的端口一直活跃"是一样的)

$read是一个引用变量,每次执行的时候传入我们需要监听的socket资源,执行过后,返回活跃的socket资源,核心伪代码如下

$socket = socketcreate();
$socket_select = array($socket);
while(true){
  $read = $socket_select;
  socket_select($read, $write, $except, null);
  foreach($read as $sock){
    if($sock == $socket){//新连接到来时

    }else{//客户端发送数据或者客户端关闭的时候

    }
   }
}

五.客户端

客户端websocket api就很简单了

// 创建一个 websocket 连接
var ws = new WebSocket("ws:XXXXX");

// websocket 创建成功事件
ws.onopen = function () {
};

// websocket 接收到消息事件
ws.onmessage = function (e) {
    var msg = JSON.parse(e.data);
}

// websocket 错误事件
ws.onerror = function () {
};

//websocket 关闭事件
ws.close = function () {
};

完整代码在我的github

参考文章

http://www.cnblogs.com/zhenbianshu/p/6111257.html

http://blog.csdn.net/u010487568/article/details/20908427?utm_source=tuicool&utm_medium=referral

时间: 2024-10-14 02:05:23

PHP webSocket实现网页聊天室的相关文章

Java和WebSocket开发网页聊天室

小编心语:咳咳咳,今天又是聊天室,到现在为止小编已经分享了不下两个了,这一次跟之前的又不大相同,这一次是网页聊天室,具体怎么着,还请各位看官往下看~ Java和WebSocket开发网页聊天室 一.项目简介 WebSocket是HTML5一种新的协议,它实现了浏览器与服务器全双工通信,这里就将使用WebSocket来开发网页聊天室,前端框架会使用AmazeUI,后台使用Java,编辑器使用UMEditor. 二.涉及知识点 网页前端(HTML+CSS+JS)和Java 三.软件环境 Tomcat

使用WebSocket实现网页聊天室

知道WebSocket挺久了,但是一直没提起兴趣去了解它. 今天听@成熟的毛毛虫 说到slack.小小的试用了一下,发现slack的聊天功能做得相当强大,看了下网络请求,发现是基于WebSocket实现的.顿时提起兴趣,想了解下这强大的WebSocket. 先了解下WebSocket. 开源中国的介绍:http://www.oschina.net/p/websocket 百度百科的介绍:http://baike.baidu.com/link?url=yMTBVobtkp1E1a0wrRJlnEX

html5 websocket + node.js 实现网页聊天室

1 client:    socket.io server:   node.js +  express  + socket.io 一个简单的聊天室  demo,没有注册,内置了一些测试用户 2 client 关键代码 var socket = io.connect('http://localhost:8080'); socket.on('connect',function(){ console.log('connected to server'); socket.on('login succes

WebSocket 网页聊天室的实现(服务器端:.net + windows服务,前端:Html5)

websocket是HTML5中的比较有特色一块,它使得以往在客户端软件中常用的socket在web程序中也能轻松的使用,较大的提高了效率.废话不多说,直接进入题. 网页聊天室包括2个部分,后端服务器+前端页面. 1.后端服务部分:.net4.0 + windows服务.相比寄宿在iis中,寄宿在进程中的windows服务更加的稳定可靠(文章中的例子用windows控制台程序演示,后面给出完整的windows服务的代码). 2.前端部分:html5 + jQuery + bootstrap.基本

网页聊天室制作步骤分享

结合网页与Asp来实现一个简单的网页聊天室制作案例,有在线聊天.在线人员名单.在线时间等功能.在这篇教程的最后,还比较详细地介绍了Flash与Asp结合使用的平台环境和常用的调试手段. 一.原理 主要的流程是先在网页端发送一个请求到服务器端,然后等待从服务器端的返回值,当值返回网页端后,根据相应的值做相应的操作.在网页部 分,跟前面的教程一样,用到的主要都是loadVariables()函数和循环等待的技巧.在Asp部分,主要用到了global.asa文件和 application()属性,先定

基于flask的网页聊天室(二)

基于flask的网页聊天室(二) 前言 接上一次的内容继续完善,今天完成的内容不是很多,只是简单的用户注册登录,内容具体如下 具体内容 这次要加入与数据哭交互的操作,所以首先要建立相关表结构,这里使用flask-sqlalchemy来辅助创建 首先修改之前的init文件为: from flask import Flask from flask_sqlalchemy import SQLAlchemy db = SQLAlchemy() from web_chatroom.models impor

Html Websocket搭建右下角聊天室

最近闲来无事,为我的网站增加了聊天室功能,这里主要用到了websocket技术,这时html5的一种新技术 controller部分 package main.java.web.news; import java.io.IOException; import java.util.concurrent.CopyOnWriteArraySet; import javax.websocket.*; import javax.websocket.server.ServerEndpoint; /** *

php+websocket搭建简易聊天室实践

1.前言 公司游戏里面有个简单的聊天室,了解了之后才知道是node+websocket做的,想想php也来做个简单的聊天室.于是搜集各种资料看文档.找实例自己也写了个简单的聊天室. http连接分为短连接和长连接.短连接一般可以用ajax实现,长连接就是websocket.短连接实现起来比较简单,但是太过于消耗资源.websocket高效不过兼容存在点问题.websocket是html5的资源 如果想要详细了解websocket长连接的原理请看https://www.zhihu.com/ques

JAVA WebSocKet ( 简单的聊天室 )

1, 前端代码 登入页 -> login.html <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>聊天室登入接口</title> <script type="text/javascript" src="JavaScript/jQuery.js"></script> <