php基于websocket搭建简易聊天室实践


Posted in PHP onOctober 24, 2016

本文实例讲述了php基于websocket搭建简易聊天室实践。分享给大家供大家参考。具体如下:
1、前言

公司游戏里面有个简单的聊天室,了解了之后才知道是node+websocket做的,想想php也来做个简单的聊天室。于是搜集各种资料看文档、找实例自己也写了个简单的聊天室。

http连接分为短连接和长连接。短连接一般可以用ajax实现,长连接就是websocket。短连接实现起来比较简单,但是太过于消耗资源。websocket高效不过兼容存在点问题。websocket是html5的资源

2、前端

前端实现websocket很简单直接

//连接websocket

var ws = new WebSocket("ws://127.0.0.1:8000");

//成功连接websoc的时候

ws.onopen = function(){}

//成功获取服务端输出的消息

ws.onmessage = function(e){}

//连接错误的时候
ws.onerror = function(){}

//向服务端发送数据

ws.send();

3、后台

websocket的难点主要在后台

3.1websocket连接过程
websocket 通信图解 这是一个简易的客户端和服务端的通信图解,php主要就做的就是接受加密key  并返回 其中完成套接字的创建和握手操作

php基于websocket搭建简易聊天室实践

下图是一张详细的服务端处理websocket的流程图

php基于websocket搭建简易聊天室实践

3.2 代码实践

服务端做的流程大致是:

  1. 挂起一个socket套接字进程等待连接
  2. 有socket连接之后遍历套接字数组
  3. 没有握手的进行握手操作,如果已经握手则接收数据解析并写入缓冲区进行输出

下面是示例代码(我写的是一个类所以代码是根据函数分段的),文底给出github地址以及自己遇到的一些坑
 1、首先是创建套接字
 

//建立套接字
    public function createSocket($address,$port)
    {
      //创建一个套接字
      $socket= socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
      //设置套接字选项
      socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1);
      //绑定IP地址和端口
      socket_bind($socket,$address,$port);
      //监听套接字
      socket_listen($socket);
      return $socket;
    }

2、将套接字放入数组

public function __construct($address,$port)
    {
      //建立套接字
      $this->soc=$this->createSocket($address,$port);
      $this->socs=array($this->soc);

    }

3、挂起进程遍历套接字数组,主要操作都是在这里面完成的

public function run(){
      //挂起进程
      while(true){
        $arr=$this->socs;
        $write=$except=NULL;
        //接收套接字数字 监听他们的状态
        socket_select($arr,$write,$except, NULL);
        //遍历套接字数组
        foreach($arr as $k=>$v){
          //如果是新建立的套接字返回一个有效的 套接字资源
          if($this->soc == $v){
            $client=socket_accept($this->soc);
            if($client <0){
              echo "socket_accept() failed";
            }else{
              // array_push($this->socs,$client);
              // unset($this[]);
              //将有效的套接字资源放到套接字数组
              $this->socs[]=$client;
            }
          }else{
            //从已连接的socket接收数据 返回的是从socket中接收的字节数
            $byte=socket_recv($v, $buff,20480, 0);
            //如果接收的字节是0
            if($byte<7)
              continue;
            //判断有没有握手没有握手则进行握手,如果握手了 则进行处理
            if(!$this->hand[(int)$client]){
              //进行握手操作
              $this->hands($client,$buff,$v);
            }else{
              //处理数据操作
              $mess=$this->decodeData($buff);
                //发送数据
              $this->send($mess,$v);
            }
          }
        }
      }
    }

4、进行握手 流程是接收websocket内容从Sec-WebSocket-Key:中获取key并通过加密算法写入缓冲区客户端会进行验证(自动验证不需要我们处理)

public function hands($client,$buff,$v)
    {
      //提取websocket传的key并进行加密 (这是固定的握手机制获取Sec-WebSocket-Key:里面的key)
      $buf = substr($buff,strpos($buff,'Sec-WebSocket-Key:')+18);
      //去除换行空格字符
      $key = trim(substr($buf,0,strpos($buf,"\r\n")));
       //固定的加密算法
      $new_key = base64_encode(sha1($key."258EAFA5-E914-47DA-95CA-C5AB0DC85B11",true));
      $new_message = "HTTP/1.1 101 Switching Protocols\r\n";
      $new_message .= "Upgrade: websocket\r\n";
      $new_message .= "Sec-WebSocket-Version: 13\r\n";
      $new_message .= "Connection: Upgrade\r\n";
      $new_message .= "Sec-WebSocket-Accept: " . $new_key . "\r\n\r\n";
      //将套接字写入缓冲区
      socket_write($v,$new_message,strlen($new_message));
      // socket_write(socket,$upgrade.chr(0), strlen($upgrade.chr(0)));
      //标记此套接字握手成功
      $this->hand[(int)$client]=true;
    }

5、解析客户端的数据(我这里没有进行加密,如果有需要也可以自己加密 )

//解析数据
    public function decodeData($buff)
    {
      //$buff 解析数据帧
      $mask = array(); 
      $data = ''; 
      $msg = unpack('H*',$buff); //用unpack函数从二进制将数据解码
      $head = substr($msg[1],0,2); 
      if (hexdec($head{1}) === 8) { 
        $data = false; 
      }else if (hexdec($head{1}) === 1){ 
        $mask[] = hexdec(substr($msg[1],4,2)); 
        $mask[] = hexdec(substr($msg[1],6,2)); 
        $mask[] = hexdec(substr($msg[1],8,2)); 
        $mask[] = hexdec(substr($msg[1],10,2)); 
          //遇到的问题 刚连接的时候就发送数据 显示 state connecting
        $s = 12; 
        $e = strlen($msg[1])-2; 
        $n = 0; 
        for ($i=$s; $i<= $e; $i+= 2) { 
          $data .= chr($mask[$n%4]^hexdec(substr($msg[1],$i,2))); 
          $n++; 
        }
        //发送数据到客户端
          //如果长度大于125 将数据分块
          $block=str_split($data,125);
          $mess=array(
            'mess'=>$block[0],
            );
        return $mess;          
      }

6、将套接字写入缓冲区

//发送数据
    public function send($mess,$v)
    {
      //遍历套接字数组 成功握手的 进行数据群发
      foreach ($this->socs as $keys => $values) {
        //用系统分配的套接字资源id作为用户昵称
          $mess['name']="Tourist's socket:{$v}";
          $str=json_encode($mess);
          $writes ="\x81".chr(strlen($str)).$str;
          // ob_flush();
          // flush();
          // sleep(3);
          if($this->hand[(int)$values])
            socket_write($values,$writes,strlen($writes));
        }
    }

7、运行方法

github地址git@github.com:rsaLive/websocket.git

①最好在控制台运行server.php

转到server.php脚本目录(可以先php -v 看下有没有配置php如果没有Linux配置下bash windows 配置下path)

php -f server.php

php基于websocket搭建简易聊天室实践

如果有错误会提示

php基于websocket搭建简易聊天室实践

②通过服务器访问html文件

php基于websocket搭建简易聊天室实践

php基于websocket搭建简易聊天室实践

8、踩过的坑,打开调试工作方便查看错误

①server.php 挂起的进程中可以打印输出的,如果出现问题可以在代码中加入打印来调试

可以在各个判断里面做标记在控制台查看代码运行在哪个区间

不过每次修改完代码之后需要重新运行脚本 php server.php

②如果出现这种错误可能是

php基于websocket搭建简易聊天室实践

1、在与服务器初始套接字的时候发送数据 (在第一次与服务器验证握手的时候不能发送内容)

2、如果已经验证过了但是客户端没有发送或者发送的消息为空也会出现这样的情况

所以要检验已连接的套接字的数据

 php基于websocket搭建简易聊天室实践

③可能浏览器不支持或者服务端没有开启socket开始之前最好验证下

if (window.WebSocket){
  console.log("This browser supports WebSocket!");
} else {
  console.log("This browser does not support WebSocket.");
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

PHP 相关文章推荐
php db类库进行数据库操作
Mar 19 PHP
并发下常见的加锁及锁的PHP具体实现代码
Oct 12 PHP
php 高性能书写
Dec 11 PHP
PHP中用正则表达式清除字符串的空白
Jan 17 PHP
php配合jquery实现增删操作具体实例
Dec 12 PHP
PHP base64编码后解码乱码的解决办法
Jun 19 PHP
浅析get与post的一些特殊情况
Jul 28 PHP
Thinkphp中volist标签mod控制一定记录的换行BUG解决方法
Nov 04 PHP
Yii2.0中的COOKIE和SESSION用法
Aug 12 PHP
php  PATH_SEPARATOR判断当前服务器系统类型实例
Oct 28 PHP
THINKPHP3.2使用soap连接webservice的解决方法
Dec 13 PHP
CMSPRESS 10行代码搞定 PHP无限级分类2
Mar 30 PHP
详解php中 === 的使用
Oct 24 #PHP
使用PHP免费发送定时短信的实例
Oct 24 #PHP
浅谈php和js中json的编码和解码
Oct 24 #PHP
php注册和登录界面的实现案例(推荐)
Oct 24 #PHP
php 使用html5实现多文件上传实例
Oct 24 #PHP
php 多文件上传的实现实例
Oct 23 #PHP
php 修改上传文件大小限制实例详解
Oct 23 #PHP
You might like
PHP数组 为文章加关键字连接 文章内容自动加链接
2011/12/29 PHP
php fsockopen伪造post与get方法的详解
2013/06/14 PHP
关于svn冲突的解决方法
2013/06/21 PHP
PHP编程之设置apache虚拟目录
2016/07/08 PHP
PHP使用redis消息队列发布微博的方法示例
2017/06/22 PHP
Mootools 1.2教程 Fx.Morph、Fx选项和Fx事件
2009/09/15 Javascript
js控制CSS样式属性语法对照表
2012/12/11 Javascript
动态加载js和css(外部文件)
2013/04/17 Javascript
JavaScript 语言基础知识点总结(思维导图)
2013/11/10 Javascript
详解JS 比较两个Json对象的值是否相等的实例
2013/11/20 Javascript
javascript解三阶幻方(九宫格)
2015/04/22 Javascript
js判断空对象的实例(超简单)
2016/07/26 Javascript
AngularJS 依赖注入详解和简单实例
2016/07/28 Javascript
小程序指纹验证的实现代码
2018/12/04 Javascript
实例分析Array.from(arr)与[...arr]到底有何不同
2019/04/09 Javascript
解决vue 单文件组件中样式加载问题
2019/04/24 Javascript
微信小程序new Date()方法失效问题解决方法
2019/07/29 Javascript
vue项目在线上服务器访问失败原因分析
2020/08/14 Javascript
通过滑动翻页效果实现和移动端click事件问题
2021/01/26 Javascript
[01:37]PWL S2开团时刻DAY1&2——这符有毒
2020/11/20 DOTA
爬山算法简介和Python实现实例
2014/04/26 Python
Python入门_浅谈字符串的分片与索引、字符串的方法
2017/05/16 Python
Python爬虫抓取代理IP并检验可用性的实例
2018/05/07 Python
python实现多层感知器
2019/01/18 Python
基于TensorFlow的CNN实现Mnist手写数字识别
2020/06/17 Python
完美解决torch.cuda.is_available()一直返回False的玄学方法
2021/02/06 Python
windeln官方海外旗舰店:德淘超人气母婴超市
2017/12/15 全球购物
美国嘻哈文化生活方式品牌:GLD
2018/04/15 全球购物
初一生物教学反思
2014/01/18 职场文书
法律系毕业生自荐信范文
2014/03/27 职场文书
小学优秀辅导员事迹材料
2014/05/11 职场文书
公务员考察材料范文
2014/12/23 职场文书
婚礼答谢礼品
2015/01/20 职场文书
学校食品安全责任书
2015/01/29 职场文书
《秋天的图画》教学反思
2016/02/19 职场文书
Python深度学习之Pytorch初步使用
2021/05/20 Python