胡骊 发布的文章

本来一直在用Scrcpy,一个把手机屏幕显示在电脑屏幕的软件。作为一般的操作,可以接受。但是滑动太快,或者玩游戏,会出现马赛克。直到前几天发现了QtScrcpy,据称可以“吃鸡”!今晚终于在Lubuntu上编译出来,玩了下Minecraft,也看了下视频,非常不错~虽然已经在公司用了多天来划水。

详细的介绍,请查看项目的官方介绍。项目地址:https://github.com/barry-ran/QtScrcpy

应用场景:把手机屏幕的内容显示在电脑屏幕,支持 Windows、MAC、Linux 三大系统(其中 Linux 需要自己编译)。可以实现公司电脑上无痕划水(自备无限流量套餐、迷你蓝牙耳机,效果更佳),或者找个烂电脑作为手机伴侣(实现小屏转大屏)。

优点:
1)速度快! 720p可以玩游戏,1080p可以看电影。

2)有熄屏功能。手机画面投影在电脑上,手机屏幕同时可以关掉。

3)有功能完整的操作界面,基本的功能按钮都有,免得打命令。手机的输入可以直接用鼠标和键盘操作(跟 Scrcpy 一样)。

缺点:
1)输入法不能通过键盘选字、不能直接输入符号(这个缺点直接继承 Scrcpy )。直接用蓝牙键盘连手机的话,输入体验会好很多。

2)Linux上需要自己编译。装QtCreator、Android SDK、Android NDK……由于网络的问题,下载相关软件时折腾了一下。编译挺简单的,对于第一次接触QtCreator的我来说,一次编辑成功。对于没有编程经验的人来说,会有难度。但是这种人一般不会使用Linux。另外,QtCreator最好从 https://download.qt.io/archive/qt/ 下载安装包进行安装。

3)窗口模式不能随意调整大小(Scrcpy是可以的),只能固定大小,或者全屏。

4)手机声音不能通过电脑播放。不知道是不是我没找到设置的地方,所以需要配个蓝牙耳机。

在家使用还是有点浪费资源,毕竟要开两个机器来做一件事件。除了玩Minecraft PE,暂时想不到有什么应用场景。但是在公司划水的话,非常实用。

以前一直用的Email客户端是Foxmail,但是某客服邮箱的邮件太多(几千封),一接收,Foxmail就挂了。后来换上Thunderbird就没事了,一直用了几年也没什么问题。但是公司内的同事基本都是用Foxmail,一回复邮件就显示我很异类(Thunderbird回复邮件的默认格式确实也太单调了)。于是找了找解决方案,设置回复邮件的模板。最后虽然不能完全模仿Foxmail的格式,但看着还行,也就这样吧。

首先设置帐户的签名如下,其中·xxx@abc.com·要替换为对应的Email地址:

<hr style="width:210px;height:1px;" color="#b5c4d" size="1" align="left" />
<div style="margin:10px;font-size:10pt">
<div>xxx@abc.com</div>
</div>

然后安装两个“扩展”:ReFwdFormatterSmartTemplate4,并重启Thunder。ReFwdFormatter是用于删除回复邮件时引用原文出现的蓝色竖线。SmartTemplate4就是设置回复邮件的模板。设置步骤如下:

1)打开ReFwdFormatter,取消全部勾选,并只勾选“Remove the '|' prefix from quote in html mail.”,点“Save”按钮保存退出。

2)打开SmartTemplate4,选择对应的帐户。
2.1)点“回复”标签,勾选“将以下模板应用于回复消息”、“替换标准引用头文件”、“使用HTML(例如,bold)”,并在“模板”填入以下内容:

%sig%
<br>
<div style="border:none; border-top:solid #B5C4DF 1.0pt; padding:3.0pt 0cm 0cm 0cm">
<div style="padding-right: 8px; padding-left: 8px; font-size: 12px; font-family: tahoma; color: #000000; background: #efefef; padding-bottom: 8px; padding-top: 8px">
<div><b>发件人:</b> <a href="mailto:%from%" moz-do-not-send="true">%from%</a></div>
<div><b>发送时间:</b> %X:=sent% %Y%-%m%-%d% %H%:%M%:%S%</div>
[[<div><b>收件人:</b> %to(name, bracketMail(angle))%</div>]]
[[<div><b>抄送:</b> %cc(name, bracketMail(angle))%</div>]]
<div><b>主题:</b> %subject%</div>
</div>
</div>

2.2)点“高级” -> “全局设置”。在“邮件内容” -> “顶部换行符数量”,设为“0”。在“高级功能”,取消勾选“插入空格到高亮的光标”、“强制段落模式”。
2.3)点“确定”保存退出。

回复时,除了“收件人”和“抄送”不能定制显示格式,其它都跟Foxmail的一样了。

这星期做了个报表,统计一堆关键词在Solr的搜索结果数量。一开始是计划写Java代码去访问Solr,并获取各个关键词的查询结果数量。后来为了减轻服务器压力,把数据下载到本地并重建了Solr索引。那为什么不用简单快速的JavaScript?

JavaScript的数据请求,可用原生的XMLHttpRequest,jQuery的$.ajax,或者fetch。记得N年前看过吹嘘fetch的文章,于是就选了fetch玩玩。该文章如下:
传统 Ajax 已死,Fetch 永生
https://github.com/camsong/blog/issues/2

说说感受吧:

1)如果需要“同步”请求,需要配合asyncawait使用,里面还要用到function。一下子不适应,$.ajax只需设置async参数。

2)不支持跨域。尝试按教程去设置fetch请求的Header,仍是不行。简单来说,需要服务端设置可跨域相关。那就简单点,把包含代码的HTML文件丢到Solr的本地站点,然后Chrome访问。

3)Promise语法很新鲜。当然,写得不好,也可以很糟糕。

4)关于错误处理,就不写了,本地请求一般不会出错。

总的来说,没想象中那么牛X,也没那么爽。如果面对一般情况,不想写复杂的XMLHttpRequest,也不想引用庞大的jQuery,不用兼容老版浏览器,fetch是个好家伙。例如这个文章提到:
fetch 没有你想象的那么美
http://undefinedblog.com/window-fetch-is-not-as-good-as-you-imagined/

还有Mozilla的参考教程,说得比较详细:
使用 Fetch - Web API 接口参考 | MDN
https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API/Using_Fetch

近段时间做了几次糯米糍,挺好吃的。也曾拿去孩子的幼儿园做义卖,一下子就卖光了。记录一下制作。

主要材料:
1)淡牛奶,100g
2)椰浆,100g
3)糯米粉,120g
4)玉米淀粉,30g
5)糖粉,50g
6)黄油,15g
这些材料是做糯米糍的皮,大概能做16粒子。做出来的口感偏软。

其它材料:
1)馅料,可选红豆沙、鲜芒果肉等。
2)外皮,可选椰蓉、白芝麻等。如果选白芝麻,要先炒熟。

步骤:
把所有材料倒进碗里搅拌混合,形成没有颗粒的糊状,再去蒸15分钟。如果蒸后还有液体状,那就继续蒸吧。蒸熟后放凉,或者放到冰箱降温,然后就可以用来搓丸子了。

馅料可以按喜好自选。试过红豆沙,因为够甜,很好吃。也试过芒果,但是芒果不够甜,几乎吃出芒果味,可能少放点糖会好点。

包好后,放外皮料滚几下,沾满整个丸子,以免吃的时候粘手。

用Pro Micro做了个摇步器,感觉有点浪费。于是入手了个ATTiny85开发板(国外好像叫Digispark),计划把Pro Micro替换出来。

这个ATTiny85开发板非常迷你和便宜,面积比邮票还小(大约2cm*2.5cm),价格不到7rmb/个。自带Micro USB母口(插上USB就可以烧录程序),除了5v VCC、GND、VIN,还有6个针脚。采用ATTiny85芯片,集成8位CPU,主频最高20MHz,内存512B,闪存8KB等等。更详细的参数,参考以下PDF文档:
https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-2586-AVR-8-bit-Microcontroller-ATtiny25-ATtiny45-ATtiny85_Datasheet.pdf

总体来说这,非常适合细小的项目。但是这货历史有点久远(网上很多资料都是2013年左右),相关资料不好找。踩了一些坑后,终于成功刷入程序并运行。

首先安装 Arduino IDE,版本是1.6以上,然后根据以下文章去配置:
Connecting and Programming Your Digispark
https://digistump.com/wiki/digispark/tutorials/connecting

遇到Linux的问题,主要是需要相关的安装包,可参考这个链接:
Linux Troubleshooting
https://digistump.com/wiki/digispark/tutorials/linuxtroubleshooting

然后,关于编写舵机控制程序,尝试了几个库,最后采用了SoftRcPulseOut并运行成功。参考以下链接:
Beginner Servo
https://digistump.com/board/index.php?topic=1157.0
Digispark (Attiny85) servo tester
http://www.circuitdb.com/?p=1203

最后再说说摇步机。摇步机的原理就是利用舵机进行单摆运动。原来使用手机摇的,现在入手了个二手小米手环2,解放了手机。接线图就不上了,简单描述如下:

ATTiny85 -> 9G舵机(SG90)
5V       -> 红(正极)
GND      -> 棕(负极)
P4       -> 橙(信号)

程序也很简单,舵机先复位到0度,再不断重复从0度转到120度后复位。代码如下:

#include <SoftRcPulseOut.h> 

SoftRcPulseOut myservo;
int pos = 0;
#define NOW  1

void setup() {
  myservo.attach(4); // P4,舵机信号
  myservo.setMaximumPulse(2200);

  for (int i = 0; i < 5; i++) { // 复位到0度
    myservo.write(pos);
    delay(100);
    SoftRcPulseOut::refresh(NOW);
  }
  delay(3000);
  SoftRcPulseOut::refresh(NOW);
} 

void loop() {
  for (pos = 0; pos < 120; pos += 20) {
    myservo.write(pos);
    delay(50);
    SoftRcPulseOut::refresh(NOW);
  }
  
  for (pos = 120; pos >= 0; pos -= 20) {
    myservo.write(pos);
    delay(50);
    SoftRcPulseOut::refresh(NOW);
  }
}


公司网站需要添加导出CSV文件的功能。一开始想用模板文件的方式导出,但是需要读取模板、分析需要替换的标识、格式化字符串、写文件等,太麻烦了。由于是web导出,就想用JSP实现。

首先,需要简单了解CSV文件规范:
1)可以采用UTF-8字符集编码,但是要带BOM,否则Excel打开后,Unicode字符会乱码。

2)一行一条数据,空行会当作一行空数据。同一行的各个数据之间用逗号分隔,每行的数据个数可以不相同。

3)每个数据用双引号括住,可以避免绝大部分的符号问题。比如在双引号里的换行符和逗号不会被解析。唯一需要转义的是双引号,转义符也是双引号。就是双引号里出现的每个双引号,要换成两个双引号。

示例代码:

<%@ page language="java" pageEncoding="UTF-8" trimDirectiveWhitespaces="true" contentType="application/x-download"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<% 
    response.addHeader("Content-Disposition","attachment;filename=ShoppingCart.csv"); // 导出的文件名
    out.print("\ufeff"); // UTF-8的BOM
%>"Name","Sex","Score"
<c:forEach items="${students}" var="student">"${fn:replace(student.name, "\"", "\"\"")}","${student.sex}","${student.score}"
</c:forEach>

简单解析:
1)第一行的trimDirectiveWhitespaces="true",设置自动清除空行。即多个<% %>行,执行完毕后只保留最后一个%>后面的内容,包括换行符。这个清除空行是不完全的,但这样设置最简单。

2)第一行的contentType="application/x-download",设置浏览器可识别的文件类型。这个设置,浏览器打开链接后会自动下载。

3)taglib引入需要用到的标签,例如JSTL或自定义的标签。

4)response.addHeader("Content-Disposition","attachment;filename=ShoppingCart.csv");是设置导出的文件名。

5)out.print("\ufeff");输出UTF-8的BOM。

6)剩下的就是文件内容了。注意换行的问题就可以了。

入手斐讯(PHICOMM)K2P这个无线路由差不多3个月,终于把问题都解决了,于是把折腾过程记录一下。

原来一直在用仕牌(SeaPai)WR1200,当时(2016年)号称屌丝专用的超高性价比千兆路由。其价格在100RMB以下,支持千兆有线以太网,2.4GHz和5GHz无线WiFi,信号稳定,网上也有很多刷机改造教程。后来发现无线设备多了以后,会卡,单反拍的1080p视频无法通过WiFi访问DLNA播放等等,于是在一番搜索后,入手了K2P。这货开卖时,带有挖矿属性,甚至可以赚钱,可惜入手晚了。

简单总结一下K2P的使用感受。无线信号很好(中等强度可覆盖两层楼),性能很强(前面提到的1080p视频可以流畅播放了),功能丰富(当然是刷了固件之后),超出了对无线路由的认识和理解。缺点是内置存储容量不够(导致固件功能需要取舍),也没有USB口(导致不能扩展容量)。由于有自家的服务器,这两个缺点可以无视。

入手第一件事,当然是刷固件。搜了一圈,很多人都说官方定制固件(简称:官改)比较稳定,于是就选择这个。刷机过程没什么好说的,固件下载地址及详细刷机教程如下:
斐讯K2P MTK官方固件定制版
https://www.right.com.cn/forum/thread-221578-1-1.html

官改的问题如下:
1)“功能设置”没有端口映射,只有“端口转发”。解决方法很简单,就是ssh进去,手动设置防火墙(/etc/config/firewall)。详细的教程如下:
斐讯k2p 官改固件 设置端口映射
https://www.right.com.cn/forum/thread-304738-1-1.html

2)不能访问mindlna服务。服务器上运行着minidlna服务,刚启动该服务时,手机可以找到并访问该DLNA服务,但是过一段时间就不能访问了。这个不知道是不是官改固件的问题。后来把minidlna配置文件的notify_interval值改小一点,就解决了。

年初,检查公司某个网站的后台日志,发现圣诞节前夕的登录出错日志暴涨。检查了一下,绝大部分是Email错误,然后断定是黑客拿着一堆Email和密码在撞库。

简单统计了一下,网站的日志,超过200万条登录错误数据,一秒大概7~10个请求。第一反映是图形验证码被破解了,而且大概10秒就分析出来。

幸好Nginx有Access Log。请求的行为是,先访问网站主页,再访问登录页,最后调用登录按钮的请求,检查Email密码是否正确。随机挑选了几个相关IP,都是泰国的。由于没有记录User Agent,所以不知道黑客是用什么程序或者爬虫,不能进一步分析并排除相关访问来源。另外,再细心分析,相关的撞库访问记录,都没有访问获取图形验证码。那就是,黑客根本就没有破解验证码。检查了代码,网站登录时,同一Email输入三次密码错误后才要求输入验证码。而黑客是利用一堆IP,输入不用的Email,所以,算是绕过了验证码。

最后,目前的处理是同一IP,登录错误3次后,就需要输入验证码。其实图形验证码也不是想象中那么容易破解,就可以防一防。但是,这不是一劳永逸的。

在V2上跟网友沟通了一下,总结一些方案:

1)升级验证码,采用更强更复杂的验证码。但是复杂的验证码(例如扭曲的字符串),对用户不友好的,所以Google推出了reCAPTCHA v3。reCAPTCHA v3无需用户进行任何操作,Google会分析其行为并打分,让网站程序自己根据评分进行处理。比如评分为0.9则认为是人类,0.4分则要进一步验证之类。

2)登录限制规则。比如过滤掉某些有问题的IP(通过第三方接口判断或网站本身记录),过滤有问题的来源(通过User Agent识别),登录错误若干次后进行限制等等。

3)采用二步验证。包括但不限于手机短信验证码、Email验证链接、Google的身份验证器、WebAuthn标准的方案等等。

近来迁移了自家的服务器,顺便记录一下Flask项目的部署。

这里采用Nginx + Supervisor + Python3 + uWSGI + Flask的方案。其中建议把uWSGI替换成Gunicorn,据说采用纯Python实现的Gunicorn,更方便打包为Dockor镜像,并且性能几乎跟Supervisor一样。这个留待以后再研(折)究(腾)。

新服务器采用Debian 9,部署过程参考以下文章:
Flask+uwsgi+Nginx部署应用
https://www.jianshu.com/p/84978157c785


1. 安装项目虚拟环境

假设项目文件夹为`/opt/flask_proj`,安装命令如下:

sudo apt update
sudo apt install python3 python3-pip
sudo pip3 install virtualenv
cd /opt/flask_proj
virtualenv venv
pip3 install -r requirements.txt

其中:
1)安装virtualenv需要用root用户,否则不会安装到/usr/local/bin/virtualenv,并且需要自行添加到path。
2)requirements.txt为flask项目所需的库。这个要看项目是否需要安装。
3)如果是迁移项目,可以在迁移前生成requirements.txt文件:pip3 freeze > requirements.txt


2. 安装uWSGI

如果requirements.txt里已包含uWSGI,则不用重复安装。安装命令如下:

pip3 install uwsgi

在项目文件夹下,新建uWSGI配置文件config.ini,参考内容如下:

[uwsgi]
master = true
home = venv
wsgi-file = manage.py
callable = app
socket = :5001
processes = 4
threads = 2
buffer-size = 32768


3. 安装supervisor

安装命令如下:

sudo apt-get install supervisor

在文件夹/etc/supervisor/conf.d下新建项目对应的配置文件,例如flask_proj.conf,参考内容如下:

[program:flask_proj]
# 启动命令入口
command=/opt/flask_proj/venv/bin/uwsgi /opt/flask_proj/config.ini
# 命令程序所在目录
directory=/opt/flask_proj
# 运行命令的用户名
user=user
autostart=true
autorestart=true
# 日志地址
stdout_logfile=/opt/flask_proj/logs/uwsgi_supervisor.log

启动supervisor服务:

sudo service supervisor start

4. 安装Nginx

一般安装系统自带的版本就够用了:

sudo apt install nginx

在文件夹/etc/nginx/sites-available下新建配置文件flask_proj,参考内容如下:

server {
    listen 443 ssl http2 default_server;
    #listen [::]:443 ssl;
    server_name www.abc.xyz;

    ssl_certificate /etc/letsencrypt/live/www.abc.xyz/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/www.abc.xyz/privkey.pem;

    index index.html index.htm;

    gzip            on;
    gzip_min_length 1k;
    gzip_buffers    4 16k;
    #gzip_proxied    expired no-cache no-store private auth;
    gzip_comp_level 5;
    gzip_types      text/html text/css text/javascript text/json text/plain text/xml application/javascript application/json application/soap+xml application/x-javascript application/x-www-form-urlencoded application/xhtml+xml application/xml;

    location / {
        include uwsgi_params;
        uwsgi_pass 127.0.0.1:5001;
        uwsgi_param UWSGI_PYHOME /opt/flask_proj/venv;
        uwsgi_param UWSGI_CHDIR /opt/flask_proj;
        uwsgi_param UWSGI_SCRIPT manage:app;
        uwsgi_read_timeout 100;
    }  
}

注意:
1)这里只配置了https,相关的SSL证书,可以到https://letsencrypt.org/免费申请。
2)开启了gzip压缩。

最后添加链接文件,并重启Nginx:

sudo ln -s /etc/nginx/sites-available/flask_proj /etc/nginx/sites-enable/flask_proj
sudo service nginx restart