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 相关文章推荐
不用mod_rewrite直接用php实现伪静态化页面代码
Oct 04 PHP
第四章 php数学运算
Dec 30 PHP
PHP中计算字符串相似度的函数代码
Dec 29 PHP
浅析HTTP消息头网页缓存控制以及header常用指令介绍
Jun 28 PHP
浅析php插件 Simple HTML DOM 用DOM方式处理HTML
Jul 01 PHP
浅谈PHP解析URL函数parse_url和parse_str
Nov 11 PHP
php防止恶意刷新与刷票的方法
Nov 21 PHP
php按字符无乱码截取中文的方法
Mar 27 PHP
php session 写入数据库
Feb 13 PHP
Windows2003下php5.4安装配置教程(IIS)
Jun 30 PHP
Thinkphp微信公众号支付接口
Aug 04 PHP
php操作路径的经典方法(必看篇)
Oct 04 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
删除无限级目录与文件代码共享
2006/07/12 PHP
PHP 危险函数全解析
2009/09/09 PHP
php urlencode()与urldecode()函数字符编码原理详解
2011/12/06 PHP
php判断终端是手机还是电脑访问网站的思路及代码
2013/04/24 PHP
PHP实现单例模式最安全的做法
2014/06/13 PHP
ThinkPHP查询返回简单字段数组的方法
2014/08/25 PHP
thinkphp自定义权限管理之名称判断方法
2017/04/01 PHP
优化网页之快速的呈现我们的网页
2007/06/29 Javascript
jQuery插件开发基础简单介绍
2013/01/07 Javascript
js函数排序的实例代码
2013/07/01 Javascript
Javascript setInterval的两种调用方法(实例讲解)
2013/11/29 Javascript
JavaScript解析JSON格式数据的方法示例
2017/01/24 Javascript
使用 NodeJS+Express 开发服务端的简单介绍
2017/04/07 NodeJs
vue使用echarts实现水平柱形图实例
2020/09/09 Javascript
[01:16:16]DOTA2-DPC中国联赛定级赛 RNG vs Phoenix BO3第二场 1月8日
2021/03/11 DOTA
Python使用xlrd读取Excel格式文件的方法
2015/03/10 Python
python使用三角迭代计算圆周率PI的方法
2015/03/20 Python
python实现图片变亮或者变暗的方法
2015/06/01 Python
Python中字典的浅拷贝与深拷贝用法实例分析
2018/01/02 Python
Python面向对象程序设计OOP入门教程【类,实例,继承,重载等】
2019/01/05 Python
win10系统下python3安装及pip换源和使用教程
2020/01/06 Python
Python如何爬取qq音乐歌词到本地
2020/06/01 Python
Python错误的处理方法
2020/06/23 Python
matplotlib部件之矩形选区(RectangleSelector)的实现
2021/02/01 Python
Python爬取你好李焕英豆瓣短评生成词云的示例代码
2021/02/24 Python
html5标记文字_动力节点Java学院整理
2017/07/11 HTML / CSS
用html5的canvas和JavaScript创建一个绘图程序的简单实例
2016/07/06 HTML / CSS
百思买美国官网:Best Buy
2016/07/28 全球购物
英国最大线上综合鞋类商城:Office
2017/12/08 全球购物
职业技术学校毕业生推荐信
2013/12/03 职场文书
秋季运动会广播稿大全
2014/02/17 职场文书
公司会议策划方案
2014/05/17 职场文书
找工作求职信
2014/07/07 职场文书
教师批评与自我批评(群众路线)
2014/10/15 职场文书
员工试用期工作总结
2019/06/20 职场文书
关于React Native 无法链接模拟器的问题
2021/06/21 Javascript