项目需要使用websocket推送最新订单,客户服务器非linux不支持swoole,因此使用原生,直接上代码
(1).PHP服务端
<?php
ini_set('error_reporting', E_ALL ^ E_NOTICE);
ini_set('display_errors', 1);
set_time_limit(0);
$address = '127.0.0.1';
$port = 6936;
//01.创建一个套接字(参数1是支持ipv4的TCP/UDP,参数2是套接字使用的类型,参数3是domain下具体协议)
$sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP) or die('Create Socket Failed');
//02.设置socket的参数(参数1是资源句柄,参数2是ip地址,参数3是挂起本脚本的端口)
socket_bind($sock, $address, $port) or die('Could not bind to address');
//03.开始监听来自本端口的所有连接
socket_listen($sock);
//04.设置为非阻塞
socket_set_nonblock($sock);
//05.保存所有的客户端state记录是否握手,shopid记录店铺id
/*
array(
array('client'=>$sock1,'state'=>0,'shopid'=>0),
array('client'=>$sock2,'state'=>0,'shopid'=>1),
)
*/
$client = array();
//06.while挂起进程来处理webscoket
while (true) {
//检测到新client,加入client组,并记录是否握手
if ($newsock = @socket_accept($sock)) {
if (is_resource($newsock)) {
//给新检测到的client回应
socket_write($newsock, "", 2) . chr(0);
//并且将其加入client队伍中
array_push($client,array('client'=>$newsock,'state'=>0));
}
}
if (count($client)) {
foreach ($client AS $k => $v) {
//是否获取到数据
if (@socket_recv($v['client'], $string, 1024, 0) === 0) {
unset($client[$k]);
socket_close($v['client']);
} else {
//已经握手可以直接发送消息
if ($v['state'] == 1) {
//握手完成的第一次,可以直接拿到client发送来的完整数据
if($string!=''){
$clientmsg=unmask($string);
echo $clientmsg;//这就是client发送来的数据
}
$data = array('12345', '上山打老虎','客户端的小妹妹你好');
$data = json_encode($data);
$data = mask($data);
@socket_write($v['client'], $data, strlen($data));
}
//未握手先完成握手
else {
doHandShake($v['client'], $string);
$client[$k]['state']=1;
}
}
}
}
sleep(1);
}
//握手方法
function doHandShake($socket, $buffer) {
list($resource, $host, $origin, $key) = getHeaders($buffer);
$upgrade = "HTTP/1.1 101 Switching Protocol\r\n" . "Upgrade: websocket\r\n" . "Connection: Upgrade\r\n" . "Sec-WebSocket-Accept: " . calcKey($key) . "\r\n\r\n";
//必须以两个回车结尾
socket_write($socket, $upgrade, strlen($upgrade));
}
//获取请求头
function getHeaders($buffer) {
$r = $h = $o = $key = null;
if (preg_match("/GET (.*) HTTP/", $buffer, $match)) { $r = $match[1];
}
if (preg_match("/Host: (.*)\r\n/", $buffer, $match)) { $h = $match[1];
}
if (preg_match("/Origin: (.*)\r\n/", $buffer, $match)) { $o = $match[1];
}
if (preg_match("/Sec-WebSocket-Key: (.*)\r\n/", $buffer, $match)) { $key = $match[1];
}
return [$r, $h, $o, $key];
}
//验证socket
function calcKey($key) {
$accept = base64_encode(sha1($key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true));
return $accept;
}
//解码数据(解析client发送的数据)
function unmask($text) {
$length = ord($text[1]) & 127;
if ($length == 126) {
$masks = substr($text, 4, 4);
$data = substr($text, 8);
} elseif ($length == 127) {
$masks = substr($text, 10, 4);
$data = substr($text, 14);
} else {
$masks = substr($text, 2, 4);
$data = substr($text, 6);
}
$text = "";
for ($i = 0; $i < strlen($data); ++$i) {
$text .= $data[$i] ^ $masks[$i % 4];
}
return $text;
}
//编码数据(编码向client发送的数据)
function mask($text) {
$b1 = 0x80 | (0x1 & 0x0f);
$length = strlen($text);
if ($length <= 125)
$header = pack('CC', $b1, $length);
elseif ($length > 125 && $length < 65536)
$header = pack('CCn', $b1, 126, $length);
elseif ($length >= 65536)
$header = pack('CCNN', $b1, 127, $length);
return $header . $text;
}
socket_close($sock);
?>(2).Javascript客户端
<script type="text/javascript">
if("WebSocket" in window) {
var ws = new WebSocket("ws://127.0.0.1:6936");
//socket握手自动触发推送消息
ws.onopen = function() {
var data = {
id: 287,
name: '某终端'
}
data = JSON.stringify(data);
ws.send(data);
};
//监听来自服务端的消息
ws.onmessage = function(evt) {
console.log(evt.data);
};
//客户端或服务端断开自动触发
ws.onclose = function() {
console.log('websocket已经断开!');
};
} else {
console.log('您的浏览器不支持h5,无法启动websocket');
}
</script> 应用场景:PHP模拟购买,商品数量大于0才能购买常见代码:<?php //连接数据库 $con=mysqli_connect("localhost","ihuohuo","927464cy","ihuohuo");...
php多进程应用场景主要是非web端,fpm下是不支持多进程的,非类linux操作系统都不支持,请在cli模式使用.可以使用多进程做任务分发,批量计算,批量文件处理,批量爬虫,网络运维等等。下面看一份简单的入门demo//创建子进程 $pid=pcntl_fork(); //返回-1,创建失败,不...
array_merge是最常用的数组合并方法,+号同样也可以,但是却有很大不同。array_merge遇到相同字符串key,后面数组的key会覆盖前面数组的key,+号正好相反。$a = [ 'one' => 'A on...
ThinkPHP中有一个debug调试功能,能输出报错文件的信息,并能看到这个函数被哪些函数调用,从框架的启动开始记录,特别方便调试。于是研究了下它的底层给予了实现。<?php //--框架核心--Start //框架内置错误处理 function errDealWith($er...
重构框架的时候想要考虑支持下cli模式,于是参考了thinkphp的底层。/** * 获取应用根目录 * @return string */ public static function getRootP...
最近在公司开发一个新的项目假设项目域名是a.com,需要接入b.com的单点登陆系统。(1).首先我们会在a.com的登陆页面用iframe引入b.com来显示登陆界面,实际上登陆验证操作都是在b.com上面(2).当b.com验证通过,会在前端ajax请求a.com的回调地址,这个回调地址目的就是...