PHP实现websocket通信的方法示例


Posted in PHP onAugust 28, 2018

本文实例讲述了PHP实现websocket通信的方法。分享给大家供大家参考,具体如下:

执行方法:

首先先修改server.php与index.html的ip
通过命令行执行 [php路径]\php.exe "[文件路径]\server.php"
然后通过浏览器打开index.html

server.php

<?php
include 'websocket.class.php';
$config=array(
 'address'=>'192.168.0.200',
 'port'=>'8000',
 'event'=>'WSevent',//回调函数的函数名
 'log'=>true,
);
$websocket = new websocket($config);
$websocket->run();
function WSevent($type,$event){
 global $websocket;
  if('in'==$type){
   $websocket->log('客户进入id:'.$event['k']);
  }elseif('out'==$type){
   $websocket->log('客户退出id:'.$event['k']);
  }elseif('msg'==$type){
   $websocket->log($event['k'].'消息:'.$event['msg']);
   roboot($event['sign'],$event['msg']);
  }
}
function roboot($sign,$t){
 global $websocket;
 switch ($t)
 {
 case 'hello':
  $show='hello,GIt @ OSC';
  break;
 case 'name':
  $show='Robot';
  break;
 case 'time':
  $show='当前时间:'.date('Y-m-d H:i:s');
  break;
 case '再见':
  $show='( ^_^ )/~~拜拜';
  $websocket->write($sign,'Robot:'.$show);
  $websocket->close($sign);
  return;
  break;
 case '天王盖地虎':
  $array = array('小鸡炖蘑菇','宝塔震河妖','粒粒皆辛苦');
  $show = $array[rand(0,2)];
  break;
 default:
  $show='( ⊙o⊙?)不懂,你可以尝试说:hello,name,time,再见,天王盖地虎.';
 }
 $websocket->write($sign,'Robot:'.$show);
}
?>

index.html

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>websocket_TEST</title>
</head>
<body>
<textarea class="log" style="width: 100%; height: 500px;">
=======websocket======
</textarea>
<input type="button" value="连接" onClick="link()">
<input type="button" value="断开" onClick="dis()">
<input type="text" id="text">
<input type="button" value="发送" onClick="send()">
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
<script>
function link(){
 var url='ws://192.168.0.200:8000';
 socket=new WebSocket(url);
 socket.onopen=function(){log('连接成功')}
 socket.onmessage=function(msg){log('获得消息:'+msg.data);console.log(msg);}
 socket.onclose=function(){log('断开连接')}
}
function dis(){
 socket.close();
 socket=null;
}
function log(var1){
 $('.log').append(var1+"\r\n");
}
function send(){
 socket.send($('#text').attr('value'));
}
function send2(){
 var json = JSON.stringify({'type':'php','msg':$('#text2').attr('value')})
 socket.send(json);
}
</script>
</body>
</html>

websocket.class.php

<?php
/*
创建类websocket($config);
$config结构:
$config=array(
 'address'=>'192.168.0.200',//绑定地址
 'port'=>'8000',//绑定端口
 'event'=>'WSevent',//回调函数的函数名
 'log'=>true,//命令行显示记录
);
回调函数返回数据格式
function WSevent($type,$event)
$type字符串 事件类型有以下三种
in 客户端进入
out 客户端断开
msg 客户端消息到达
均为小写
$event 数组
$event['k']内置用户列表的userid;
$event['sign']客户标示
$event['msg']收到的消息 $type='msg'时才有该信息
方法:
run()运行
search(标示)遍历取得该标示的id
close(标示)断开连接
write(标示,信息)推送信息
idwrite(id,信息)推送信息
属性:
$users 客户列表
结构:
$users=array(
[用户id]=>array('socket'=>[标示],'hand'=[是否握手-布尔值]),
[用户id]=>arr.....
)
*/
class websocket{
  public $log;
  public $event;
  public $signets;
  public $users;
  public $master;
  public function __construct($config){
    if (substr(php_sapi_name(), 0, 3) !== 'cli') {
      die("请通过命令行模式运行!");
    }
    error_reporting(E_ALL);
    set_time_limit(0);
    ob_implicit_flush();
    $this->event = $config['event'];
    $this->log = $config['log'];
    $this->master=$this->WebSocket($config['address'], $config['port']);
    $this->sockets=array('s'=>$this->master);
  }
  function WebSocket($address,$port){
    $server = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
    socket_set_option($server, SOL_SOCKET, SO_REUSEADDR, 1);
    socket_bind($server, $address, $port);
    socket_listen($server);
    $this->log('开始监听: '.$address.' : '.$port);
    return $server;
  }
 function run(){
  while(true){
   $changes=$this->sockets;
   @socket_select($changes,$write=NULL,$except=NULL,NULL);
   foreach($changes as $sign){
    if($sign==$this->master){
     $client=socket_accept($this->master);
     $this->sockets[]=$client;
     $user = array(
      'socket'=>$client,
      'hand'=>false,
     );
     $this->users[] = $user;
     $k=$this->search($client);
     $eventreturn = array('k'=>$k,'sign'=>$sign);
     $this->eventoutput('in',$eventreturn);
    }else{
     $len=socket_recv($sign,$buffer,2048,0);
     $k=$this->search($sign);
     $user=$this->users[$k];
     if($len<7){
      $this->close($sign);
      $eventreturn = array('k'=>$k,'sign'=>$sign);
      $this->eventoutput('out',$eventreturn);
      continue;
     }
     if(!$this->users[$k]['hand']){//没有握手进行握手
      $this->handshake($k,$buffer);
     }else{
      $buffer = $this->uncode($buffer);
      $eventreturn = array('k'=>$k,'sign'=>$sign,'msg'=>$buffer);
      $this->eventoutput('msg',$eventreturn);
     }
    }
   }
  }
 }
 function search($sign){//通过标示遍历获取id
  foreach ($this->users as $k=>$v){
   if($sign==$v['socket'])
   return $k;
  }
  return false;
 }
 function close($sign){//通过标示断开连接
  $k=array_search($sign, $this->sockets);
  socket_close($sign);
  unset($this->sockets[$k]);
  unset($this->users[$k]);
 }
 function handshake($k,$buffer){
  $buf = substr($buffer,strpos($buffer,'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($this->users[$k]['socket'],$new_message,strlen($new_message));
  $this->users[$k]['hand']=true;
  return true;
 }
 function uncode($str){
  $mask = array();
  $data = '';
  $msg = unpack('H*',$str);
  $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));
   $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++;
   }
  }
  return $data;
 }
  function code($msg){
   $msg = preg_replace(array('/\r$/','/\n$/','/\r\n$/',), '', $msg);
   $frame = array();
   $frame[0] = '81';
   $len = strlen($msg);
   $frame[1] = $len<16?'0'.dechex($len):dechex($len);
   $frame[2] = $this->ord_hex($msg);
   $data = implode('',$frame);
   return pack("H*", $data);
  }
  function ord_hex($data) {
   $msg = '';
   $l = strlen($data);
   for ($i= 0; $i<$l; $i++) {
    $msg .= dechex(ord($data{$i}));
   }
   return $msg;
  }
  function idwrite($id,$t){//通过id推送信息
   if(!$this->users[$id]['socket']){return false;}//没有这个标示
   $t=$this->code($t);
   return socket_write($this->users[$id]['socket'],$t,strlen($t));
  }
  function write($k,$t){//通过标示推送信息
   $t=$this->code($t);
   return socket_write($k,$t,strlen($t));
  }
  function eventoutput($type,$event){//事件回调
   call_user_func($this->event,$type,$event);
  }
  function log($t){//控制台输出
   if($this->log){
    $t=$t."\r\n";
    fwrite(STDOUT, iconv('utf-8','gbk//IGNORE',$t));
   }
  }
}

希望本文所述对大家PHP程序设计有所帮助。

PHP 相关文章推荐
php 中英文语言转换类代码
Aug 11 PHP
PHP autoload与spl_autoload自动加载机制的深入理解
Jun 05 PHP
php实现兼容2038年后Unix时间戳转换函数
Mar 18 PHP
ThinkPHP中limit()使用方法详解
Apr 19 PHP
mac系统下为 php 添加 pcntl 扩展
Aug 28 PHP
PHP实现递归目录的5种方法
Oct 27 PHP
php实现的XML操作(读取)封装类完整实例
Feb 23 PHP
php利用云片网实现短信验证码功能的示例代码
Nov 18 PHP
PHP实现微信退款功能
Oct 02 PHP
PHP中localeconv()函数的用法
Mar 26 PHP
PHP实现的pdo连接数据库并插入数据功能简单示例
Mar 30 PHP
PHP INT类型在内存中占字节详解
Jul 20 PHP
Yii2压缩PHP中模板代码的输出问题
Aug 28 #PHP
PHP实现的XXTEA加密解密算法示例
Aug 28 #PHP
PHP两个n位的二进制整数相加问题的解决
Aug 26 #PHP
PHP连接sftp并下载文件的方法教程
Aug 26 #PHP
PHP操作Redis数据库常用方法示例
Aug 25 #PHP
PHP实现基于3DES算法加密解密字符串示例
Aug 24 #PHP
PHP与以太坊交互详解
Aug 24 #PHP
You might like
Windows IIS PHP 5.2 安装与配置方法
2009/06/08 PHP
基于PHP遍历数组的方法汇总分析
2013/06/08 PHP
php二分查找二种实现示例
2014/03/12 PHP
PHP FATAL ERROR: CALL TO UNDEFINED FUNCTION BCMUL()解决办法
2014/05/04 PHP
PHP合并数组函数array_merge用法分析
2017/02/17 PHP
thinkphp框架类库扩展操作示例
2019/11/26 PHP
JS解密入门之凭直觉解
2008/06/25 Javascript
常见效果实现之返回顶部(结合淡入、淡出、减速滚动)
2012/01/04 Javascript
jquery 实现密码框的显示与隐藏示例代码
2013/09/18 Javascript
jQuery 写的简单打字游戏可以提示正确和错误的次数
2014/07/01 Javascript
JavaScript中最简洁的编码html字符串的方法
2014/10/11 Javascript
JS字符串的切分用法实例
2016/02/22 Javascript
基于javascript实现泡泡大冒险网页版小游戏
2016/03/23 Javascript
让DIV的滚动条自动滚动到最底部的3种方法(推荐)
2016/09/24 Javascript
jQuery实现全选、反选和不选功能
2017/08/16 jQuery
简单实现vue验证码60秒倒计时功能
2017/10/11 Javascript
推荐10款扩展Web表单的JS插件
2017/12/25 Javascript
vue + vuex todolist的实现示例代码
2018/03/09 Javascript
p5.js码绘“跳动的小正方形”的实现代码
2019/10/22 Javascript
js实现的在本地预览图片功能示例
2019/11/09 Javascript
微信小程序wx.getUserInfo授权获取用户信息(头像、昵称)的实现
2020/08/19 Javascript
python daemon守护进程实现
2016/08/27 Python
Python排序搜索基本算法之堆排序实例详解
2017/12/08 Python
python使用Tesseract库识别验证
2018/03/21 Python
利用Python读取txt文档的方法讲解
2018/06/23 Python
在django admin中添加自定义视图的例子
2019/07/26 Python
python批量读取文件名并写入txt文件中
2020/09/05 Python
python给指定csv表格中的联系人群发邮件(带附件的邮件)
2019/12/31 Python
Python threading模块condition原理及运行流程详解
2020/10/05 Python
医学专业个人求职自荐信格式
2013/09/23 职场文书
餐饮管理自我介绍信
2014/01/15 职场文书
新闻发布会主持词
2014/03/28 职场文书
感恩寄语大全
2014/04/11 职场文书
法定代表人授权委托书范本
2014/10/07 职场文书
农业项目投资意向书
2015/05/09 职场文书
读《推着妈妈去旅行》有感1500字
2019/10/15 职场文书