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 相关文章推荐
PHP4实际应用经验篇(7)
Oct 09 PHP
php设计模式 Adapter(适配器模式)
Jun 26 PHP
用PHP实现的四则运算表达式计算实现代码
Aug 02 PHP
利用PHP实现短域名互转
Jul 05 PHP
php生成图形验证码几种方法小结
Aug 15 PHP
php删除文本文件中重复行的方法
Apr 28 PHP
php将远程图片保存到本地服务器的实现代码
Aug 03 PHP
PHP数学运算与数据处理实例分析
Apr 01 PHP
PHP安装BCMath扩展的方法
Feb 13 PHP
laravel-admin 管理平台获取当前登陆用户信息的例子
Oct 08 PHP
关于laravel 子查询 &amp; join的使用
Oct 16 PHP
php 使用ActiveMQ发送消息,与处理消息操作示例
Feb 23 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
《PHP边学边教》(04.编写简易的通讯录――视频教程1)
2006/12/13 PHP
SMARTY学习手记
2007/01/04 PHP
php 常用类整理
2009/12/23 PHP
php缩放图片(根据宽高的等比例缩放)实例介绍
2013/06/09 PHP
php获取远程图片体积大小的实例
2013/11/12 PHP
ThinkPHP之M方法实例详解
2014/06/20 PHP
Zend Framework教程之动作的基类Zend_Controller_Action详解
2016/03/07 PHP
PHP-FPM实现性能优化
2016/03/31 PHP
PHP+Ajax实现无刷新分页实例详解(附demo源码下载)
2016/04/07 PHP
jQuery的Ajax时无响应数据的解决方法
2010/05/25 Javascript
jquery中ajax学习笔记一
2011/10/16 Javascript
jquery实现的带缩略图的焦点图片切换(自动播放/响应鼠标动作)
2013/01/23 Javascript
jQuery选择器中含有空格的使用示例及注意事项
2013/08/25 Javascript
javascript实现简单的分页特效
2015/08/12 Javascript
jQuery validate插件submitHandler提交导致死循环解决方法
2016/01/21 Javascript
JS实现隔行换色的表格排序
2017/03/27 Javascript
手把手教你用Node.js爬虫爬取网站数据的方法
2018/07/05 Javascript
JavaScript工具库之Lodash详解
2019/06/15 Javascript
vuex中store存储store.commit和store.dispatch的用法
2020/07/24 Javascript
[32:30]夜魇凡尔赛茶话会 第一期01:谁是卧底
2021/03/11 DOTA
小结Python用fork来创建子进程注意事项
2014/07/03 Python
在Python下进行UDP网络编程的教程
2015/04/29 Python
Python使用pygame模块编写俄罗斯方块游戏的代码实例
2015/12/08 Python
Numpy中转置transpose、T和swapaxes的实例讲解
2018/04/17 Python
浅谈Scrapy网络爬虫框架的工作原理和数据采集
2019/02/07 Python
Python-Seaborn热图绘制的实现方法
2019/07/15 Python
Python Mock模块原理及使用方法详解
2020/07/07 Python
CSS3 真的会替代 SCSS 吗
2021/03/09 HTML / CSS
HTML5 body设置全屏背景图片的示例代码
2020/12/08 HTML / CSS
美国按摩椅批发网站:Titan Chair
2018/12/27 全球购物
Regatta官网:英国最受欢迎的户外服装和鞋类品牌
2019/05/01 全球购物
新闻记者实习自我鉴定
2013/09/19 职场文书
大学毕业感言一句话
2014/02/06 职场文书
经典毕业生求职信
2014/07/12 职场文书
2014年大学宣传部工作总结
2014/12/19 职场文书
二十年同学聚会感言
2015/07/30 职场文书