绕过广告拦截插件的解决方案
- 最近做广告联盟的一个项目,在页面插入广告代码时被广告插件给拦截了,这里简单的记录下解决方案。
方案一、代码直接写在页面上,不使用引入的方式。
- 以前直接引入是这样的:
<script src="http://xxxxxxx"> </script>
- 现在改成这样:
<script type="text/javascript">
//..................这里写src="http://xxxxxxx"那段js广告代码
</script>
- 测试了下这种是没有被插件拦截的(不过老板说代码量太大了,人家站长不会复制的,所以我放弃了这种);
方案二、广告代码使用websocket方式返回,然后追加到页面上(我们使用的这个方案)。
- 我在网上查到目前广告插件对websocket拦截的不是那么严,也有的网站实现过了,所以自己也来试试。
- 首先来写段websocket代码,做服务端 webSocket.php:
<?php
error_reporting(E_ALL ^ E_NOTICE);
ob_implicit_flush();
//地址与接口,即创建socket时需要服务器的IP和端口
$sk=new Sock('192.168.0.111',9090);
//对创建的socket循环进行监听,处理数据
$sk->run();
/**
* 模拟post进行url请求
* @param string $url
* @param array $post_data
*/
function request_post($url = '', $post_data = array()) {//url为必传 如果该地址不需要参数就不传
if (empty($url)) {
return false;
}
if(!empty($post_data)){
$params = '';
foreach ( $post_data as $k => $v )
{
$params.= "$k=" . urlencode($v). "&" ;
// $params.= "$k=" . $v. "&" ;
}
$params = substr($params,0,-1);
}
$ch = curl_init();//初始化curl
curl_setopt($ch, CURLOPT_URL,$url);//抓取指定网页
curl_setopt($ch, CURLOPT_HEADER, 0);//设置header
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);//要求结果为字符串且输出到屏幕上
//curl_setopt($ch, CURLOPT_POST, 1);//post提交方式 linux不传参数会返回400 Bad Request 错误所以暂时注释掉 ,windows不会
if(!empty($post_data))curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
$data = curl_exec($ch);//运行curl
curl_close($ch);
return $data;
}
//下面是sock类
class Sock{
public $sockets; //socket的连接池,即client连接进来的socket标志
public $users; //所有client连接进来的信息,包括socket、client名字等
public $master; //socket的resource,即前期初始化socket时返回的socket资源
private $sda=array(); //已接收的数据
private $slen=array(); //数据总长度
private $sjen=array(); //接收数据的长度
private $ar=array(); //加密key
private $n=array();
public function __construct($address, $port){
//创建socket并把保存socket资源在$this->master
$this->master=$this->WebSocket($address, $port);
error_log("Sock __construct ");
//创建socket连接池
$this->sockets=array($this->master);
}
//对创建的socket循环进行监听,处理数据
function run(){
//死循环,直到socket断开
while(true){
$changes=$this->sockets;
$write=NULL;
$except=NULL;
/*
//这个函数是同时接受多个连接的关键,我的理解它是为了阻塞程序继续往下执行。
socket_select ($sockets, $write = NULL, $except = NULL, NULL);
$sockets可以理解为一个数组,这个数组中存放的是文件描述符。当它有变化(就是有新消息到或者有客户端连接/断开)时,socket_select函数才会返回,继续往下执行。
$write是监听是否有客户端写数据,传入NULL是不关心是否有写变化。
$except是$sockets里面要被排除的元素,传入NULL是”监听”全部。
最后一个参数是超时时间
如果为0:则立即结束
如果为n>1: 则最多在n秒后结束,如遇某一个连接有新动态,则提前返回
如果为null:如遇某一个连接有新动态,则返回
*/
socket_select($changes,$write,$except,NULL);
foreach($changes as $sock){
//如果有新的client连接进来,则
if($sock==$this->master){
//接受一个socket连接
$client=socket_accept($this->master);
//给新连接进来的socket一个唯一的ID
$key=uniqid();
$this->sockets[]=$client; //将新连接进来的socket存进连接池
$this->users[$key]=array(
'socket'=>$client, //记录新连接进来client的socket信息
'shou'=>false //标志该socket资源没有完成握手
);
//否则1.为client断开socket连接,2.client发送信息
}else{
$len=0;
$buffer='';
//读取该socket的信息,注意:第二个参数是引用传参即接收数据,第三个参数是接收数据的长度
do{
$l=socket_recv($sock,$buf,1000,0);
$len+=$l;
$buffer.=$buf;
}while($l==1000);
//根据socket在user池里面查找相应的$k,即健ID
$k=$this->search($sock);
//如果接收的信息长度小于7,则该client的socket为断开连接
if($len<7){
//给该client的socket进行断开操作,并在$this->sockets和$this->users里面进行删除
$this->send2($k);
continue;
}
//判断该socket是否已经握手
if(!$this->users[$k]['shou']){
//如果没有握手,则进行握手处理
$this->woshou($k,$buffer);
}else{
//走到这里就是该client发送信息了,对接受到的信息进行uncode处理
$buffer = $this->uncode($buffer,$k);
if($buffer==false){
continue;
}
//error_log(" received buffer " . $buffer);
//如果不为空,则进行消息推送操作
$this->send($k,$buffer);
}
}
}
}
}
//指定关闭$k对应的socket
function close($k){
//断开相应socket
socket_close($this->users[$k]['socket']);
//删除相应的user信息
unset($this->users[$k]);
//重新定义sockets连接池
$this->sockets=array($this->master);
foreach($this->users as $v){
$this->sockets[]=$v['socket'];
}
//输出日志
$this->e("key:$k close");
}
//根据sock在users里面查找相应的$k
function search($sock){
foreach ($this->users as $k=>$v){
if($sock==$v['socket'])
return $k;
}
return false;
}
//传相应的IP与端口进行创建socket操作
function WebSocket($address,$port){
$server = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_option($server, SOL_SOCKET, SO_REUSEADDR, 1);//1表示接受所有的数据包
socket_bind($server, $address, $port);
socket_listen($server);
$this->e('Server Started : '.date('Y-m-d H:i:s'));
$this->e('Listening on : '.$address.' port '.$port);
return $server;
}
/*
* 函数说明:对client的请求进行回应,即握手操作
* @$k clien的socket对应的健,即每个用户有唯一$k并对应socket
* @$buffer 接收client请求的所有信息
*/
function woshou($k,$buffer){
//截取Sec-WebSocket-Key的值并加密,其中$key后面的一部分258EAFA5-E914-47DA-95CA-C5AB0DC85B11字符串应该是固定的
$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));
//对已经握手的client做标志
$this->users[$k]['shou']=true;
return true;
}
//解码函数
function uncode($str,$key){
$mask = array();
$data = '';
$msg = unpack('H*',$str);
$head = substr($msg[1],0,2);
if ($head == '81' && !isset($this->slen[$key])) {
$len=substr($msg[1],2,2);
$len=hexdec($len);//把十六进制的转换为十进制
if(substr($msg[1],2,2)=='fe'){
$len=substr($msg[1],4,4);
$len=hexdec($len);
$msg[1]=substr($msg[1],4);
}else if(substr($msg[1],2,2)=='ff'){
$len=substr($msg[1],4,16);
$len=hexdec($len);
$msg[1]=substr($msg[1],16);
}
$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;
$n=0;
}else if($this->slen[$key] > 0){
$len=$this->slen[$key];
$mask=$this->ar[$key];
$n=$this->n[$key];
$s = 0;
}
$e = strlen($msg[1])-2;
for ($i=$s; $i<= $e; $i+= 2) {
$data .= chr($mask[$n%4]^hexdec(substr($msg[1],$i,2)));
$n++;
}
$dlen=strlen($data);
if($len > 255 && $len > $dlen+intval($this->sjen[$key])){
$this->ar[$key]=$mask;
$this->slen[$key]=$len;
$this->sjen[$key]=$dlen+intval($this->sjen[$key]);
$this->sda[$key]=$this->sda[$key].$data;
$this->n[$key]=$n;
return false;
}else{
unset($this->ar[$key],$this->slen[$key],$this->sjen[$key],$this->n[$key]);
$data=$this->sda[$key].$data;
unset($this->sda[$key]);
return $data;
}
}
//与uncode相对
function code($msg){
$frame = array();
$frame[0] = '81';
$len = strlen($msg);
if($len < 126){
$frame[1] = $len<16?'0'.dechex($len):dechex($len);
}else if($len < 65025){
$s=dechex($len);
$frame[1]='7e'.str_repeat('0',4-strlen($s)).$s;
}else{
$s=dechex($len);
$frame[1]='7f'.str_repeat('0',16-strlen($s)).$s;
}
$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;
}
//用户加入或client发送信息
function send($k,$msg){
//将查询字符串解析到第二个参数变量中,以数组的形式保存如:parse_str("name=Bill&age=60",$arr)
parse_str($msg,$g);
$ar=array();
$fromDate = json_decode($msg);
error_log("json_decode(msg)[text] " . $fromDate->text);
if($g['type']=='add'){
//第一次进入添加聊天名字,把姓名保存在相应的users里面
$this->users[$k]['name']=$g['ming'];
$ar['type']='add';
$ar['name']=$g['ming'];
$key='all';
}else{
//发送信息行为,其中$g['key']表示面对大家还是个人,是前段传过来的信息
$ar['nrong']=$g['nr'];
$key=$g['key'];
}
$ar["requestUrl"] = $fromDate->text;
//推送信息
$this->send1($k,$ar,$key);
}
//对新加入的client推送已经在线的client
function getusers(){
$ar=array();
foreach($this->users as $k=>$v){
$ar[]=array('code'=>$k,'name'=>$v['name']);
}
return $ar;
}
//$k 发信息人的socketID $key接受人的 socketID ,根据这个socketID可以查找相应的client进行消息推送,即指定client进行发送
function send1($k,$ar,$key='all'){
$ar['code1']=$key;
$ar['code']=$k;
$ar['time']=date('m-d H:i:s');
//对发送信息进行编码处理
$str = $this->code(json_encode($ar));
//面对大家即所有在线者发送信息
error_log("ar value " . json_encode($ar));
if($key=='all'){
$users=$this->users;
//如果是add表示新加的client
if($ar['type']=='add'){
$ar['type']='madd';
$ar['users']=$this->getusers(); //取出所有在线者,用于显示在在线用户列表中
$str1 = $this->code(json_encode($ar)); //单独对新client进行编码处理,数据不一样
//对新client自己单独发送,因为有些数据是不一样的
socket_write($users[$k]['socket'],$str1,strlen($str1));
//上面已经对client自己单独发送的,后面就无需再次发送,故unset
unset($users[$k]);
}
//除了新client外,对其他client进行发送信息。数据量大时,就要考虑延时等问题了
foreach($users as $v){
socket_write($v['socket'],$str,strlen($str));
}
}else{
//单独对个人发送信息,即双方聊天
//socket_write($this->users[$k]['socket'],$str,strlen($str));
error_log(" ar[requestUrl]" .$ar["requestUrl"]);
//发起请求得到广告
$responseBody = request_post($ar["requestUrl"], array());
$arrays = array();
$arrays["responseBody"] = $responseBody;
$str = $this->code(json_encode($arrays));
//error_log("responseBody ".$str["responseBody"]);
socket_write($this->users[$k]['socket'],$str,strlen($str));
//socket_write($this->users[$key]['socket'],$str,strlen($str));
}
}
//用户退出向所用client推送信息
function send2($k){
$this->close($k);
$ar['type']='rmove';
$ar['nrong']=$k;
$this->send1(false,$ar,'all');
}
//记录日志
function e($str){
//$path=dirname(__FILE__).'/log.txt';
$str=$str."\n";
//error_log($str,3,$path);
//编码处理
echo iconv('utf-8','gbk//IGNORE',$str);
}
}
echo "1";
?>
这代码是抄的,然后改了点东西。
- 服务端有了,再来写客户端的东西。
//这段代码需要引入jQuery
<script type="text/javascript">
//ws连接
var websocket;
/***
*zhouzhongqing
* 2018年6月1日14:50:02
* 发送消息
* */
function sendHeartMessage(message) {
console.log("发送消息");
var data = {};
data["to"] = 0;
data["text"] = message;
websocket.send(JSON.stringify(data));
}
/**
* zhouzhongqing
* 2018年10月23日15:58:09
* 创建websocket连接
* **/
function createWebSocket() {
//webSocket地址
var wsLocation = "ws://192.168.0.111:9090";
try {
// 指定websocket路径
if ('WebSocket' in window) {
websocket = new WebSocket(wsLocation);
} else if ('MozWebSocket' in window) {
websocket = new MozWebSocket(wsLocation);
} else {
websocket = new SockJS(wsLocation);
}
initEventHandle();
}catch (e){
console.log("createWebSocket error " + e);
}
}
function initEventHandle(){
websocket.onopen = function (event) {
console.log("open start");
//这是把广告地址发给websocket
sendHeartMessage("http://zyiis.xxxx.com/s.php?id=12");
console.log("open end");
};
websocket.onclose = function () {
console.log("ws 关闭 !" + new Date().toUTCString());
};
websocket.onerror = function () {
console.log(" ws连接错误!" + new Date().toUTCString());
};
websocket.onmessage = function (event) {
var data = JSON.parse(event.data);
//console.log(data);
var responseBody = data.responseBody;
//返回的js追加到body标签里
$("body").append("<script>"+responseBody+"<\/script>");
websocket.close();
};
}
$(document).ready(function () {
createWebSocket();
});
</script>
- 基本思路就是这样实现的,下面一步就是封装代码。
- 封装web_socket.js
//ws连接
var websocket;
//任务个数
var taskCount = 0;
//判断是否存在websocket连接
var isExistenceWs = false;
/***
* 有可能有多个广告,所有后面的稍等下再发送
* */
function createWebSocketTask(message) {
taskCount++;
if(isExistenceWs){
setTimeout("sendHeartMessage('"+message+"')",taskCount + 1000);
}else {
createWebSocket(message );
}
}
setInterval("closeWebSocketConnection()",3000);
/***
* 关闭socket连接
* */
function closeWebSocketConnection() {
if(isExistenceWs && taskCount == 0){
setTimeout(" websocket.close()",3000);
}
}
/***
*zhouzhongqing
* 2018年6月1日14:50:02
* 发送 消息
* */
function sendHeartMessage(message) {
console.log("发送消息");
var data = {};
data["to"] = 0;
data["text"] = message;
websocket.send(JSON.stringify(data));
}
/**
* zhouzhongqing
* 2018年10月23日15:58:09
* 创建websocket连接
* **/
function createWebSocket(message) {
//webSocket地址
var wsLocation = "ws://192.168.0.111:9090";
try {
// 指定websocket路径
if ('WebSocket' in window) {
websocket = new WebSocket(wsLocation);
} else if ('MozWebSocket' in window) {
websocket = new MozWebSocket(wsLocation);
} else {
websocket = new SockJS(wsLocation);
}
initEventHandle(message);
isExistenceWs = true;
}catch (e){
console.log("createWebSocket error " + e);
isExistenceWs = false;
}
}
function initEventHandle(message){
websocket.onopen = function (event) {
console.log("open start");
sendHeartMessage(message);
console.log("open end");
};
websocket.onclose = function () {
console.log("ws 关闭 !" + new Date().toUTCString());
};
websocket.onerror = function () {
console.log(" ws连接错误!" + new Date().toUTCString());
};
websocket.onmessage = function (event) {
//收到消息则减-
taskCount--;
var data = JSON.parse(event.data);
//console.log(data);
var responseBody = data.responseBody;
//考虑到有的网站网页有可能不支持jQuery所以用这种方式追加
var newScript = document.createElement("script");
newScript.setAttribute("type","text/javascript");
newScript.innerHTML = responseBody;
document.getElementsByTagName("body")[0].appendChild(newScript);
//$("body").append("<script>"+responseBody+"<\/script>");
//websocket.close();
};
}
- 然后js页面调用,这也是站长要在页面加入的代码。
<!--注意这个web_socket.js一个页面只引入一次-->
<script src="http://xxxxxxx/js/web_socket.js"></script>
<script type="text/javascript">
createWebSocketTask("http://zyiis.xxxxx.com/s.php?id=12");
</script>
<script type="text/javascript">
createWebSocketTask("http://zyiis.xxxxx.com/s.php?id=11");
</script>
- 最后运行效果:
-
运行websocket服务端
-
页面:
-
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现