独立 Watch App 真机调试无网络的问题

问题

现在我正在开发一个独立的 Watch App 应用,但是我发现在模拟器上调试时是可以正常连接网络的。但是当我上传到真机时,就会出现无网络连接的问题。具体的报错如下:

2021-11-20 21:42:27.156338+0800 wTodo WatchKit Extension[1068:2225475] PDTask <29C198EA-480A-459F-B5B9-421D9C26C7D8>.<3> finished with error [-1009] Error Domain=NSURLErrorDomain Code=-1009 "The Internet connection appears to be offline." UserInfo={_NSURLErrorFailingURLSessionTaskErrorKey=LocalDataPDTask <29C198EA-480A-459F-B5B9-421D9C26C7D8>.<3>, NSLocalizedDescription=The Internet connection appears to be offline., _kCFStreamErrorCodeKey=50, _NSURLErrorRelatedURLSessionTaskErrorKey=(
    "LocalDataPDTask <29C198EA-480A-459F-B5B9-421D9C26C7D8>.<3>",
    "LocalDataTask <29C198EA-480A-459F-B5B9-421D9C26C7D8>.<3>"
), NSErrorFailingURLStringKey=https://baidu.com, _kCFStreamErrorDomainKey=1, NSErrorFailingURLKey=https://baidu.com}

测试机型

Apple Watch S7

系统版本: 8.1

网络环境: WIFI

原因

在watchOS 8中,GPS版的Apple Watch是不能单独设置APP的网络的,他会镜像iOS侧的App网络设置,比如设置支付宝可以用Wi-Fi,则Watch侧的支付宝App也可以用Wi-Fi。现在陷入的一个僵局仅仅是存在于新推出的独立App,独立App顾名思义就是只存在于Watch侧,iOS侧是没有App的。因此也不存在镜像iOS侧App网络设置的可能。这导致了如果App默认被设置成了不能联网,就会导致用户没法更改App联网状态(因为iOS侧和Watch侧都不能更改App的网络设置)。

解决方案

  1. 彻底关闭手机
    该方法是为了让 Apple Watch 不去同步手机的网络设置(默认关闭网络),促使独立应用能够正常联网,但该方法仅适用于网络连接少或者无的独立应用,比如小说、阅读器等。
  2. 开发 iOS 配套应用
    如果 iOS 侧有配套应用,则用户就可以在 iOS 侧对应用的网络权限做控制,进而实现 Watch 侧应用联网,该方法适合对于网络请求较多的应用。

PS:这个 Bug 的出现真的是很无语,花了我一整天的时间才解决,心累啊😣。

[WP插件] WPComment2Bark

背景 🏞

最近大半年都在搞实习和雅思,其实没做多少实用的工具出来,有点手痒痒了。因此接着博客重建的契机,动手搞了一个评论信息推送的小插件。

思考 🤔

技术指标:

  1. 高触达率 🚀
  2. 开箱即用 📦
  3. 高度安全 🔐

解决方案:

1. 邮件推送

原来的推送方式就是通过Email的形式来推送,有可能会出现消息推送不及时或者被拒信,无法满足高触达率的技术要求,故摒弃这种推送方式。

2. Server酱

Server酱年初也因为各种外部原因,降级成了企业微信推送,其实不是特别方便,用户还得去装一个企业微信,然后配置Bot,再去配置APIKEY。对于我们做开发的用户来说,已经是挺繁琐的步骤了,对于普通用户而言简直就是噩梦。无法满足开箱即用的要求,故放弃。

3. Bark

最后我将目光投到了Bark身上,Bark是V站的一个dalao搞的一套利用Apple消息推送机制做的Web信息推送框架。Bark也同时满足我们三项技术要求:

1. 高触达率

借助Apple推送机制,我们甚至可以在息屏的情况下,都能正常收到推送消息。无视任何垃圾回收机制,绝对在线。

2. 开箱即用

用户只要下载Bark客户端,博客安装插件,配置插件,即可投入实际使用。

3. 高度安全

Bark提供免费服务器的同时,也提供了源代码供用户进行审查。如果是对于隐私比较敏感的用户,还能选择通过Docker部署自己的私有推送服务器。

综合上述优点,我选择了使用Bark作为消息推送的核心功能支持。

作用 🏄🏼‍♀️

每当有人评论你的文章时,可以推送到你的 Bark App。

配置指南 🧭

1. 从AppStore下载Bark客户端

2. 上传 & 安装插件

3. 配置推送链接

首先从客户端上复制出推送API和API密钥

第二步,切换到博客后台,依次点击【设置->讨论】,滚动到底部,找到【Bark推送设置】

至此就完成了全部配置工作,只要有新的评论被发出,就会调用API想您推送消息。

WordPress 插件市场

https://wordpress.org/plugins/wpcomment2bark/

插件仓库 ⛺️

https://github.com/7gugu/WPComment2Bark

点击【Code -> Download ZIP】下载压缩包后,按照配置指南,一步一步的安装即可。

[油猴脚本] V2EX 自动切换深色模式

起因

V 站从去年年中就好像开放深色模式了,但是切换样式一直都需要手动去切换,个人感觉有点麻烦。最近入手了 12,发现 iOS 的根据日出日落时间自动切换浅色深色模式的功能很好用,但是电脑端(Windows平台)不使用额外的软件不能实现自动切换样式,所以萌生了基于日出日落时间来自动切换 V 站样式模式的想法。

简介

目前脚本是基于定位获取当前设备的坐标,基于坐标计算出当地当日的日出日落时间,自动切换浅色与深色模式。 (日出后日落前就是一直保持浅色模式,日落后日出前就是一直保持深色模式)

脚本链接

https://greasyfork.org/zh-CN/scripts/421557-v2ex-autodarkmode

使用方法

第一次使用时,将会弹出询问❓是否允许获取定位📍,选择”允许”,并勾选✔”不再询问”即可完成初始设定。
之后的使用中,将默默运行在后台,没有任何的提示。

联系我

Email: gz7gugu@qq.com
Blog: https://www.7gugu.com
(由于把脚本放在了 GreasyFork,您也可以直接在 GreasyFork 上面私信我,看到就会回复)

Powered by 7gugu

[NodeJS]非抢占式多级反馈队列调度算法

前序

《操作系统》课程最近布置了一个大作业,要求我们每人实现一个非抢占式多级反馈队列调度算法的模拟程序,作为期末考核的一部分。(u1s1,真的是爽到,老师开心,我们也写的开心!)

实现代码

//Powered By 7gugu

//每一级队列的可运行时间
let timeSlice = [1, 2, 3];

//准备运行的队列
let preRunQueue = [];
//3级运行队列
let runQueue = [
  [],
  [],
  []
];

//准备运行的程序[开始时间, 运行时间]
let progs = [
  [0, 8],
  [1, 4],
  [5, 1],
  [3, 7],
  [4, 2]
];

for (let i = 0; i < progs.length; i++) {
  let prog = {};
  prog.id = "P" + i;
  prog.startTime = progs[i][0];
  prog.runTime = progs[i][1];
  prog.priority = 0; //设置最高优先级
  preRunQueue.push(prog); //把待运行的程序导入运行序列
}

//总运行时间
let totalTime = 0;

while (true) {
  //如果待运行队列中仍然有程序 & 程序已到达开始时间
  if (preRunQueue.length > 0) {
    let prog = preRunQueue[0];
    if (prog.startTime <= totalTime) {
      preRunQueue.shift(); //直接弹出队头的元素
      runQueue[0].push(prog); //把程序加入0级运行队列中
      console.log(prog.id + "开始运行,开始时间为:" + totalTime);
    }
  }

  for (let i = 0; i < runQueue.length; i++) {
    //如果各级队列还有程序的话,就继续运行
    if (runQueue[i].length > 0) {
      let prog = runQueue[i].shift(); //获取各级队列中第一个程序
      if (prog.runTime > timeSlice[i]) {
        //程序运行时间比时间片大
        totalTime = totalTime + timeSlice[i]; //总运行时间累加
        prog.runTime = prog.runTime - timeSlice[i]; //减去每一次运行的时间
        if (i != runQueue.length - 1) {
          //如果未处于最低优先级,则把程序放在下一个优先级队列中
          runQueue[i + 1].push(prog);
        } else {
          //如果处于最低优先级,则把程序放回最低优先级中运行
          runQueue[i].push(prog);
        }
      } else {
        //程序运行时间比时间片小
        totalTime = totalTime + prog.runTime;
        console.log(prog.id + "运行完成,目前时间为:" + totalTime);
      }
      break;
    }
  }
} 

运行结果

仓库地址

参考资料

计算机操作系统(第四版) 西安电子科技出版社

Vue import不识别 Unexpected Token (xx:xx)

Bug出现

最近在给工作室打工的时候,Vue做了个静态路由懒加载。

然后编译的时候死活不认这个import,截图如下,完全没有解决的思路。

解决思路

  1. 同事编译莫得问题,遂排除是代码问题
  2. 清除node_module,重新npm install,无效,排除是Node的问题
  3. 因为其他位置的import工作正常,遂排除babel未启用
  4. 最后通过下载安装”syntac-dynamic-import”,在配置后,问题解决

[小程序] GCU课表+

简介

一款专属于华广人的课表小程序。专注于课表信息查询,极简,优雅。不做论坛不做商城,节省流量保护隐私。

特性

  1. 支持教务系统导入课表
  2. 支持添加编辑课表
  3. 支持成绩查询
  4. 支持考试安排查询
  5. 支持深色模式
  6. 支持课表信息推送
  7. 公告信息
  8. 离线存储
  9. 弱网加载
  10. 自定义壁纸
  11. 无障碍访问
  12. 自带使用指南

小程序码

微信小程序码

截图

服务支持

现在后端已经完全迁移到了腾讯云的SCF上,每个月消耗的都是免费额度,如果未来使用人数不激增以及教务系统不升级,理论上是可以继续运行5年+的。另外目前打码已经使用Numpy等机器学习框架,帮助自动识别,某种程度上,是不需要外部维护就能持续运行的。目前小程序的最长无维护记录是4个月,希望日后离校之后,可以实现4-5年无维护都能正常运行吧🤷‍♂️。

JavaScript OOP笔记[ES3]

简介

最近开始要学习ES6了,翻出JS看了看,发现OOP部分还没有掌握,所以就赶紧进行了补课。下面是这次学习的一些,个人认为重要的知识点。

对象

每次使用JavaScript的构造器时,都会创建一个对象。一个初始化的对象中将会含有一个属性集,称之为prototype(对象属性),还有一个constructor(构造器)。而属性集里面中则会默认存在一个属性_proto_(原型)。这个属性存储的是父类的prototype(可以理解为指向父类的一个指针)。

对象属性

访问对象属性

对象.prototype.属性名

创建对象属性

对象.prototype.属性名 = 值

继承

JavaScript中实现继承主要是通过修改对象的_proto_(原型)指向到父原型来实现的继承。一旦理解了就会很简单,但是这个设计真的不好,而且原型这个称谓真的太容易混淆了。

注意!

//错误的继承
student.prototype = person.prototype;

解释:因为如果是这么赋值的话,在后续的操作中,比如给student增加属性或者方法时,收student的本质还是person,这样子修改的话,本质上还是修改的person的对象属性。

//正确的继承
student.prototype = Object.create(person.prototype)
//此时就可以正确把student的_proto_正确的指向person.prototype了

(JavaScript在ES6中貌似已经引入了extend应该能改善当前这个反人类的设计了)

原型

原型就是对象上一个存储父类的属性,称之为_proto_。由于Object是顶层对象,所以它的原型就是NULL。

原型链

bosn的原型指向Student,Student的原型又会指向Person,Person的原型又会指向Object,则称这条联系为原型链。

InstanceOf

该方法用于判断方法右边的函数是否存在于左边对象的原型链中,返回一个Bool值。其原理还是通过遍历prototype来看看左边的原型链上面是否存在右边函数的prototype。

用途:判断对象是否存在该方法(函数)

尾言

说实话,平时对于JavaScript的应用还停留在普通的事件应用,函数闭包这种层面,第一次了解了关于JS的面对象过程,也是受益匪浅,希望可以帮助到后续的ES6的学习,如果你觉着这篇文章好的话,不妨点一个赞吧,如果我理解有错误,也欢迎在下方评论区中学习交流owo。

[BAS弹幕动画] Bad Apple

前序

其实这个BAS动画我一直很想写的了,只是一直咕咕咕,还有身边的琐事,一直没抽出空来写一下弹幕动画。这次终于抽出空来写了一个(AV88558525),欢迎来看看。这篇博文主要是会分享一下这个BAS弹幕动画是怎么做出来的,以及一些BAS的个人看法。

处理流程

  1. 使用ffmpeg把BadApple.mp4分割成图片
  2. 使用PHP*将图片转换成字符画
  3. 将字符画拼接成BAS字符串
  4. 计算每一组BAS的起始时间
  5. 在视频上发布
  6. 微调**

*:语言没啥所谓,重点一定是能处理图片,还有就是顺手,python虽然有很多库,语法很优雅,但我是一个phper,所以还是倾向用php整活(PHP天下第一)

**:音乐视频可以找鼓点来定位,如果你放的视频的节奏型不是特别强无对话的话,你就随缘调吧,反正最后还是会有偏差的。

代码仓库

代码解析

视频转换

视频转换,我使用的是这个dalao的代码。地址:https://blog.csdn.net/somehow1002/article/details/77600186

切割视频

切割视频的码率一定要设置成30帧一秒,不然会卡到爆炸。

txt转BAS

其实BAS转换非常简单,你可以看到我的代码也就是跑了两个循环就拼接好了。每个BAS弹幕的开口是def c{属性},然后是set c{content=””},再然后的就都是then set c{xxxxx}。

这个c是可以换的仅仅是一个函数名而已,你换成啥都行。这里主要是要控制多少个bas组成一组。这次的经验是:

  • 0.033s为一帧
  • 三个为一秒
  • 201/402为一组

目前测试单次弹幕为408KB,就是已知的B站容许POST的数据量。再大的话服务器那边会提示服务器错误(就是不允许你发这么大个的弹幕了),但如果本地测试的话,播放器单次怼1206个16×32的字符画也是OK的。

时间计算

这一步主要是要标定弹幕的开始时间,按照402个为一组的话,一段的时间是13266ms,那么第一段的时间就是0ms开始,第二段就是0+13266+1开始,偏移1ms让弹幕不要叠在一起。

还有就是要对鼓点,不然整个视频的节奏就会很有问题。除非你的视频没有啥节奏,不然就很有必要对多几次,这里有一些想吐槽的就放到最后再写。

成果

吐槽

语法缺失

其实最最大的缺点就是这个BAS语法没有加入一些for,if之类的语法定义,这个script甚至连Boolean都没有定义,可以玩的东西实在有限。最多只能写一些动画出来玩玩。

标准缺失

这个标准指的是弹幕数据的一些标准,虽然平常发弹幕实际能触顶的机会十分的少,但是对于BAS制作者来说,这些标准的指定可以大大减少我们的开发时间。之所以这么耗费功夫其中的一个原因就是总是要测试标准的上限在哪,客服一问三不知,也没法实质的解决,希望B站以后可以写出来。(已反馈到B站)

批量弹幕发送困难

对于技术宅来说可以用自动化测试套件/窗口捕获/模拟POST来实现批量提交弹幕,但这些基础脚本的编写也是要花费时间的,导致整个制作周期会拉长实在是非常讨厌,而这也就是我想说的另外一个耗费功夫的原因,实在是太麻烦了。就算是目前有多Tab,来发送还是繁复的不得了。其实解决方案应该是创建一个API中心,开发者/UP主可以针对单一视频通过API来提交弹幕。而且可以限制API单次使用时间,确保站点安全。

总结

上述的几个建议如果都能改进或者加入,相信BAS还有更多的空间发展,当初的代码弹幕的辉煌也会回来的。最近的互动视频的动作其实就不错,如果可以把BAS也加入其中,想必会给互动视频加入更多有意思的元素,B站黄油,B站FPS指日可待。

感谢你看到这里,希望你喜欢OWO

探究php的MD5函数输出的原始二进制数据是啥?

前序

最近我的朋友(Ge15emium)在研究CTF,发给我一个关于使用MD5函数实现sql注入的博文。文章通过构造特殊的字符串通过md5函数输出后,可以构造成注入SQL,拿到Flag。
博文链接:http://mslc.ctf.su/wp/leet-more-2010-oh-those-admins-writeup/?tdsourcetag=s_pcqq_aiomsg
通过文章我们知道作者通过使用md5()对ffifdyop进行加密处理后可以构造出‘or’6<trash>这样一个字符串,来规避掉php的代码检查,进而获得Flag。

本文主要探究的是md5函数怎么把32位16进制的报文摘要输出成为目标字符串,不涉及CTF的解密流程以及MD5的加密流程


1.怎么对字符串进行的加密?

php的md5()具有输出原始二进制数据的特性。

通过文章我们得知,博主通过使用php的md5(“ffifdyop”,true)输出其加密后的原始二进制数据得到目标字符串。从这个流程中我们进而引出下一个问题原始二进制数据是啥?怎么来的?


2.原始二进制数据是啥?怎么来的?

原始二进制数据不是指100111这些二进制数据,而是原始字符串转换成ascii码后组成的字符串。
接下来就使用前序中所说的字符串来演示如何转换。

  1. 使用md5(“ffifdyop”)进行加密
    通过加密后将会得到之后的32位16进制字符串:276f722736c95d99e921722cf9ed621c
  2. 将32位16进制字符串按照2个字符为一组切割成为16组16进制的字符串
    切割成27,6f,72,27,36,c9,5d,99,e9,21,72,2c,f9,ed,62,1c
  3. 将每一组16进制的数值转换成2进制
    100111,1101111,1110010,100111,110110,11001001,1011101,10011001,11101001,100001,1110010,101100,11111001,11101101,1100010,11100
  4. 将每一组2进制数值转换称为10进制数值
    39,111,114,39,54,201,93,153,233,33,114,44,249,237,98,28
  5. 最后对照如下的ASCII码表即可翻译的出最终的原始二进制字符串

    以前四组为例39=>’,111=>o,114=>r,39=>’,最终的组成’or’这一个字符串
    (其实理论上我们可以直接把16进制转换成10进制就好了,但是此处是为了展示我的思考过程才会有2进制这一步,希望写细一点可以帮助大家的理解和认识)

结尾

CTF的注入玩法真的是神仙玩法,平常使用md5函数都还没思考过这个问题,借由此机会也提升了我的php基础知识,使其更加扎实可靠,作为2020第一篇原创文,希望日后也能多写一些这样的文章吧。