默认配置下SourceTree 使用 putty 做 git ssh 登陆,每次重启后只会保留一个密钥…次次都需要手动添加其他的密钥,很麻烦。
从网上查了一下,SourceTree 支持多密钥需要修改为 openssh ,并且修改多个配置文件。照着做了,结果还是不行。
最后发现并不需要麻烦的手写配置文件,直接修改使用 openssh ,然后直接点击 工具 => 添加ssh密钥 将多个密钥都加进来,实际使用时将自动选择密钥。不需要麻烦的修改配置文件。
默认配置下SourceTree 使用 putty 做 git ssh 登陆,每次重启后只会保留一个密钥…次次都需要手动添加其他的密钥,很麻烦。
从网上查了一下,SourceTree 支持多密钥需要修改为 openssh ,并且修改多个配置文件。照着做了,结果还是不行。
最后发现并不需要麻烦的手写配置文件,直接修改使用 openssh ,然后直接点击 工具 => 添加ssh密钥 将多个密钥都加进来,实际使用时将自动选择密钥。不需要麻烦的修改配置文件。
今天碰到了 crontab 执行命令半截中断的情况。查看日志中断的位置并没有什么特殊操作,查看内存,并没有内存不足的情况。记不清楚来源的资料说过太多的标注输出也有可能造成 crontab 命令中断,遂将标准输出完全重定向到log文件,经测试没有再次出现中断现象。问题解决。
*/5 * * * * root /usr/bin/命令 > /log.log 2>&1
原始的是socks5收到tcp代理请求立刻回复已连接到远程主机。
现在改为只有建立了到远程ss的连接后才回复链接已建立。
坑死啊,之前使用 shadowsocks-csharp 作为上层代理就发现有些不对劲,这次仔细研究下代码,发现被坑了…
shadowsocks-csharp 并没有按标准 socks5 协议实现…
socks5 接受新连接是在这里:
https://github.com/shadowsocks/shadowsocks-windows/blob/master/shadowsocks-csharp/Controller/Service/Listener.cs#L83
1 2 3 4 5 6 7 | // Start an asynchronous socket to listen for connections. Console.WriteLine("Shadowsocks started"); _tcpSocket.BeginAccept( new AsyncCallback(AcceptCallback), _tcpSocket); UDPState udpState = new UDPState(); _udpSocket.BeginReceiveFrom(udpState.buffer, 0, udpState.buffer.Length, 0, ref udpState.remoteEndPoint, new AsyncCallback(RecvFromCallback), udpState); |
开始第一次数据接收,这里有个坑,第一次会接受 buf.Length (4096) 直接的数据,但是后面时却只处理了前几个字节,后面的内容被抛弃了。我上次为了降低延迟鉴定及请求命令一起发过来,结果只收到了鉴定回应,现在知道原因了,ss 只处理了鉴定,连接请求被丢弃了…
https://github.com/shadowsocks/shadowsocks-windows/blob/master/shadowsocks-csharp/Controller/Service/Listener.cs#L159
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | public void AcceptCallback(IAsyncResult ar) { Socket listener = (Socket)ar.AsyncState; try { Socket conn = listener.EndAccept(ar); byte[] buf = new byte[4096]; object[] state = new object[] { conn, buf }; conn.BeginReceive(buf, 0, buf.Length, 0, new AsyncCallback(ReceiveCallback), state); } catch (ObjectDisposedException) |
这里遍历自身支持的服务,确定是用什么服务来处理。
https://github.com/shadowsocks/shadowsocks-windows/blob/master/shadowsocks-csharp/Controller/Service/Listener.cs#L198
1 2 3 4 5 6 7 | foreach (Service service in _services) { if (service.Handle(buf, bytesRead, conn, null)) { return; } } |
TCPRelay 为每个连接建立一个 Handler 来处理请求。
https://github.com/shadowsocks/shadowsocks-windows/blob/master/shadowsocks-csharp/Controller/Service/TCPRelay.cs#L22
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | public bool Handle(byte[] firstPacket, int length, Socket socket, object state) { if (socket.ProtocolType != ProtocolType.Tcp) { return false; } if (length < 2 || firstPacket[0] != 5) { return false; } socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); Handler handler = new Handler(); handler.connection = socket; handler.controller = _controller; handler.Start(firstPacket, length); return true; } |
将第一次收到的数据保存到内部变量,HandshakeReceive里面去处理。
https://github.com/shadowsocks/shadowsocks-windows/blob/master/shadowsocks-csharp/Controller/Service/TCPRelay.cs#L94
1 2 3 4 5 6 | public void Start(byte[] firstPacket, int length) { this._firstPacket = firstPacket; this._firstPacketLength = length; this.HandshakeReceive(); } |
这里就是有问题的地方,对第一次收到的数据只检查了下头就把数据抛弃了…
https://github.com/shadowsocks/shadowsocks-windows/blob/master/shadowsocks-csharp/Controller/Service/TCPRelay.cs#L175
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | private void HandshakeReceive() { if (closed) { return; } try { int bytesRead = _firstPacketLength; if (bytesRead > 1) { byte[] response = { 5, 0 }; if (_firstPacket[0] != 5) { // reject socks 4 response = new byte[] { 0, 91 }; Console.WriteLine("socks 5 protocol error"); } connection.BeginSend(response, 0, response.Length, 0, new AsyncCallback(HandshakeSendCallback), null); } else { this.Close(); } } catch (Exception e) { Logging.LogUsefulException(e); this.Close(); } } |
这里对收到的代理请求直接回复连接成功,而没有管是不是成功。这又是一个坑啊…造成测速失败…
https://github.com/shadowsocks/shadowsocks-windows/blob/master/shadowsocks-csharp/Controller/Service/TCPRelay.cs#L232
1 2 3 4 5 6 7 8 9 10 | command = connetionRecvBuffer[1]; if (command == 1) { byte[] response = { 5, 0, 0, 1, 0, 0, 0, 0, 0, 0 }; connection.BeginSend(response, 0, response.Length, 0, new AsyncCallback(ResponseCallback), null); } else if (command == 3) { HandleUDPAssociate(); } |
接上一篇,android 本身提供了2个简单的办法修改第一屏画面,但是我这居然都不能用。
第一个办法是通过 fastboot flash splash1 写入新开机画面,但是我这里发现中兴是将图片保存在splash分区,并且是多个画面保存在同一个分区。直接将单个图片刷入 splash ,直接把手机变成转了,由于 FTM 开机画面也保存在 splash ,造成连 FTM 都进不去,无奈只能返厂,现在还没到…手里的这个s291还得用,等返厂回来有空闲的机器在测试。
1 2 3 4 5 6 7 8 | del splash1.bmp.nb nbimg -F splash1.bmp -w 480 h 800 del splash1.img rename splash1.bmp.nb splash1.img adb reboot bootloader # 等待手机重启进入 fastboot fastboot flash splash1 splash1.img fastboot reboot |
另一个办法是通过 load_oemlogo 命令修改,但是手机上没有这个命令,拷贝上去又怕出兼容问题,没用这个办法。
最后通过对比不同开机图案刷机包的 splash.mbn 文件,发现图片是以 24位 BMP 位图的形式保存在 splash.mbn 里面的。文件前面填充0,从1024开始是第一个图片,所有图片连续存放,有些包是2个图片,有些是3个图片。
写了个工具,可以自动导出、导入 splash.mbn 文件内的图片,地址是:https://github.com/GameXG/SImage 。导出、修改、导入、fastboot 刷入即可实现更改开机画面。
附件:
nbimg-1.1win32
参考:
http://android.tgbus.com/lab/break/201207/444432.shtml
http://www.programgo.com/article/73812043212/
boot.img 存放了 boot 分区的内容,boot分区负责存放系统正常启动时linux zImage内核及 ramdisk 临时根文件系统。
可以使用mkbootimg、unpackbootimg 打包、解包。
https://github.com/osm0sis/mkbootimg 有mkbootimg、unpackbootimg 的源码,git 取出狗make即可生成可执行文件。
我这里make出错,手工去掉 makefile 文件内的 Werror 既可。
1 2 | mkdir out unpackbootimg -i boot.img -o .\out |
即可解包,注意需要预先创建 out 目录,否则解包失败…
通过下面的命令可以把 ramdisk 解压出来。
1 2 3 4 5 | gamexg@gamexg-VirtualBox:~/mkbootimg/out$ cp boot.img-ramdisk.gz ramdisk.cpio.gz gamexg@gamexg-VirtualBox:~/mkbootimg/out$ gzip -d ramdisk.cpio.gz gamexg@gamexg-VirtualBox:~/mkbootimg/out$ mkdir ramdisk gamexg@gamexg-VirtualBox:~/mkbootimg/out$ cd ramdisk gamexg@gamexg-VirtualBox:~/mkbootimg/out/ramdisk$ cpio -i -F ../ramdisk.cpio |
打包就没再操作,本来计划修改 boot.img 把开机画面改掉,结果发现这里保存的画面是android原版的,证明真正的开机画面还是没在这里保存…
fastboot flash splash splash1.img 刷完后开机画面还是老样子,实在头疼啊…
不过能看到fstab ,也算有点作用吧(虽然mount也可查出来)。
参考:
http://blog.csdn.net/wh_19910525/article/details/8200372
http://blog.csdn.net/ttxgz/article/details/7742696
android 开机画面实际上有3个,但是默认第一个画面不显示。其中最后一个画面是动画版的,这里修改的就是最后一个动画开机画面。
最后一个画面保存在 /system/media/bootanimation.zip ,/data/local/bootanimation.zip也可以放置开机画面,并且优先级更高,但是复位时将清除掉。
bootanimation.zip 内保存多张图编并循环显示达到动画效果,图片是24位png格式,尺寸=屏幕分辨率。
这个很简单,并没有什么特殊的地方,照着系统原本的开机画面修改即可。
具体规则参考:http://blog.csdn.net/victoryckl/article/details/8104694
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | adb shell su cd /data mkdir ttt chmod 777 /data/ttt exit exit adb push D:\开机画面\bootanimation.zip /data/ttt/bootanimation.zip # 未知原因造成即使remount + chmod 后也没办法直接 push 到/system,只能先 push 到 data 在 cp 了。 adb shell su mount -o remount /dev/block/platform/msm_sdcc.1/by-name/system /system cp /data/ttt/bootanimation.zip //system/media rm -r /data/ttt exit exit |
参考:
Android系统的开机画面显示过程分析
http://blog.csdn.net/luoshengyang/article/details/7691321
android 修改开机logo、动画
http://blog.csdn.net/jimbo_lee/article/details/12064367
一般的抽奖都是使用 rand 按一定几率来确认是否中奖,但是在未知总抽奖人数时中将比率不好安排,很容易出现时间未结束奖就全部抽完了,或者抽奖时间结束奖还有的现象。
照成上面问题的主要原因是在拿未知的总抽奖人数作为抽奖几率的参数,算出来的几率很难正确。但是其实我们手里面还有一个参数可以用来算中奖几率,那就是抽奖时间。我们可以将每一个奖项随机的分配到抽奖时间里面,在某个奖项中奖时间后第一个抽奖的即可中当前奖项。这样避开了抽奖总人数未知的问题。当然,夜间可能没有人或很少有人抽奖,所以需要在总抽奖时间里面减去夜间,或根据夜间抽奖人数少降低分配到夜间的奖项。
大体做法是:
建立一个”中奖时间”表,含有中奖时间、奖励、状态 等字段。
每个奖励分配办法:
奖励数/总抽奖小时 = 平均每小时中奖数
for (int h = 0;h<总抽奖小时;h++) { for (int i=0;i<每小时中奖次数;i++) { 本次中奖时间 = 抽奖开始时间 + h小时 + Random(0,60*60*1000) } } 懒得写了,大体意思写出来即可。
说出来很简单,官方提供的中兴天机S291中文Recovery(分区合并第二版)带刷机包验证(会验证是否合并分区版刷机包),gapps包无法通过,所以要刷入需要安装第三方 中兴天机S291中文TWRP合并分区后专用版 刷入 gapps 。
0620中兴天机S291 合并分区后专用版中文TWRP cwm-recovery刷机工具
http://www.myzte.cn/thread-233254-1-1.html
http://wiki.cyanogenmod.org/w/Google_Apps
[GAPPS][CM11][CM12.x][4.4.4][5.0.2][5.1.x] Delta Gapps With Modular Addons (All DPI)
http://forum.xda-developers.com/android/software/app-minimal-gapps-gapps-lp-20150107-1-t2997368
现在已经有windwos 预编译的 thrift 了,可以直接下载。先到 https://thrift.apache.org/download 页面把thrift-0.9.2.tar.gz 、Thrift compiler for Windows (thrift-0.9.2.exe) 下载下来。
1.把 thrift-0.9.2.exe 改名为 thrift.exe 并放到 path 里面。
2. 解压 thrift-0.9.2.tar.gz
3.进入thrift-0.9.2\lib\py目录,执行 setup.py install 安装python扩展。
4.现在到 thrift-0.9.2\tutorial 目录,执行 thrift.exe –r –gen py tutorial.thrift 会在 gen-py 目录生成 thrift 代码。
5.现在可以尝试执行 python 例子了。
服务器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | D:\thrift-0.9.2\tutorial>c:\Python27\python.exe py\PythonServer.py Traceback (most recent call last): File "py\PythonServer.py", line 24, in <module> sys.path.insert(0, glob.glob('../../lib/py/build/lib.*')[0]) IndexError: list index out of range #这个错误直接注释掉出错行即可。 D:\thrift-0.9.2\tutorial>c:\Python27\python.exe py\PythonServer.py Starting the server... ping() add(1,1) calculate(1, Work(comment=None, num1=1, num2=0, op=4)) calculate(1, Work(comment=None, num1=15, num2=10, op=2)) getStruct(1) ping() add(1,1) calculate(1, Work(comment=None, num1=1, num2=0, op=4)) calculate(1, Work(comment=None, num1=15, num2=10, op=2)) getStruct(1) ping() add(1,1) calculate(1, Work(comment=None, num1=1, num2=0, op=4)) calculate(1, Work(comment=None, num1=15, num2=10, op=2)) getStruct(1) ping() add(1,1) calculate(1, Work(comment=None, num1=1, num2=0, op=4)) calculate(1, Work(comment=None, num1=15, num2=10, op=2)) getStruct(1) ping() add(1,1) calculate(1, Work(comment=None, num1=1, num2=0, op=4)) calculate(1, Work(comment=None, num1=15, num2=10, op=2)) getStruct(1) |
客户端:
1 2 3 4 5 6 7 8 | d:\thrift-0.9.2\tutorial>c:\Python27\python.exe py\PythonClient.py ping() 1+1=2 InvalidOperation: InvalidOperation(what=4, why='Cannot divide by 0') 15-10=5 Check log: 5 d:\thrift-0.9.2\tutorial> |