帐前卒专栏

code, software architect, articles and novels.
代码,软件架构,博客和小说

游戏介绍:

这个能轻松到达 4096. 所以取名为 4096了。

github地址是: https://github.com/chilly/4096

如果想直接玩游戏访问 4096.chillyc.info

这个4096 是直接copy的2048。 这个游戏需要消耗比较多的脑力。当然费了脑力还要拼拼运气。原因在于生成随机方块的地方90%会生成2,10%会生成4.
最后一搏的那个方块很有可能会因为2,4生成的问题导致游戏失败。

为什么要做这个游戏

这个原因其实是为让LP打到更高的分数。当然还要有些难度,不能移动一个方块瞬间就变成8192。还得让LP不能因为运气不佳挂掉。戴着枷锁跳呀跳。所以直接改了20
48的源码。

2048这款游戏怎么做的?

其实非常细节的,我也没有仔细的研究。2048这个游戏代码量不大。最主要的两个类是game_manager.js 和 grid.js。游戏的主要流程:

  1. 界面接收到keyboard的请求
  2. 交给game_manager的move函数
  3. 判断游戏是否失败
  4. merge tile(也就是merge 色块)
  5. move tile
  6. 最后生成在随机的位置生成随机的 tile(2或者4)
  7. 继续监听keyboard请求

怎么做4096这款游戏

做这款游戏的重点是要去除霉运,增加好运

下面做了几点优化点:

  1. 真没有什么可以优化的点时,对4产生的概率增加一下。提高到50%。简单暴力
  2. 生成的2需要在2旁边,生成的4需要在4旁边:如下图:

在2的旁边生成2


2.png
在4旁边生成4

3. 误操作导致最大值移动。在误操作时需要弹个框帮助用户确认。不过为了不要频繁的弹窗,在最大值大于512后才开始提示。这里的算法是判断最大值是否有移动,如
果移动,是否会移入到四个角。是否是在边界处移动。而非向内部移动。




4. 随机生成的色块,可以帮助某些值merge.这里比较tricky的地方。如果某个比较大的两个值需要merge,
但是这两个值差了一个空格。那么就在相应的地方补上2、4,使得这两个比较大的值可以merge。例如下图中的两个32色块:




这个图中向左移动后,32和32会移动到左边。那么第二行的32的右边出现小方块,那么下一次向右移动,32就有机会和32合并。如下图所示:




然后继续右移,32可以和32合并了:




5. 在最空旷的地方生成2/4. 这个比较简单就是找4联通最大的空白所在的点。然后将此点设置为2、4。如下图所示:




再次优化

当然,经过这系列优化之后,难度有所降低,但不意味着你不动脑筋可以玩到4096. 另外还有一些值得优化的点。

  1. 例如第5点,在这一点中,需要生成的色块不应该和最大值在一条直线上。
  2. 另外还需要做的优化就是:如果你一直左右移,很有可能有一个状态导致无法再左右移动。那么就需要上下移动。这样的带来的问题就是最大色块一定会脱离原有的边界。这样的移动极有可能导致整个游戏挂掉。所以需要对这一状态进行优化。
  3. 在手机上玩这款游戏,其实灵敏度是个问题,总感觉慢了半拍(性能上还有值得优化的点)
  4. 在某一个状态,最大值一定在边界上,但不是在四个角,那么最好的情况是可以将最大值移入到四个角中。假如一直是左右移动变为的这个状态,最大值和次大值都都在底线上。之后的步骤一定要让最大值所在的色块不能向上移动,而其他的底线色块可以向上移动。这样上移后,底线就只有最大值,然后再次左右移动就可以将最大值放入到边角中。如果这时随机生成色块,很有可能次大值等再也无法移回底线。所以这样的状态也需要优化。









结束了吗?

这些优化会在以后慢慢放出来。现在游戏中bug有些多。像这种游戏也不知道怎么做自动化测试的。。。。。。慢慢做吧。

下面是黑PM的时间:PM们,你们能走查到 8192 这个状态吗? 。。。。。。算了,你们努力走查到1024就可以发布了吧~

开发有道云笔记发送邮件功能。这是一个非常古老的功能,这也是一个非常没有用处的功能。其实就是为了做一个和竞品一样的功能。这个功能就是将一篇笔记发送成邮件。关于
这个功能,每次测试时,总有测试人员来问,为什么我发的邮件被扔到了垃圾邮件里?每次回归测试都是如此,无一例外。

那么为什么会成为垃圾邮件呢?

1. 内容本身是垃圾,里面的内容写的都不是人话。都是“法3iron的萨芬女生大富豪ASF萨法” 这样的乱敲码。
2. 垃圾内容发送过多的账号,即使发送正常的内容,也可能会被标记为垃圾

3. 含有“发票,代开,网店,xx”等特殊字样

4. 内容只有一张图片的

5. …
在这里总结一下。我要说的是:这不是功能的问题,这是因为你们发送的内容本身就是垃圾…

好吧,明白为啥被当做垃圾邮件的请点赞。

1. 首看看漏洞的起因:

有兴趣的你可以将下面的url贴到浏览器看看效果:

1
http://chillyc.info\.csdn.net  

或者这个url:

1
http://[email protected]?blog.csdn.net/

这些 Url 在不同的浏览器上表现可能不太一致。但是我在 Chrome上,输入第一个url 调整到了chillyc.info的一个404页面。但不管怎么说,大多程序员使用找到 '.‘和’/'来判断domain的方法十分不靠谱。so~~~~为什么呢?写这些Oauth的家伙们都没有看过 RFC 3986 (UniformResource Identifier (URI): Generic Syntax) .  或者看了一眼发现:我x,这么多英文。然后就跳过了。不过不光中国人实现的有问题, 各种版本浏览器实现的也都有些问题。也有可能大家都觉得不会有这么复杂的uri。

2. 对于Oauth平台来说如何修补漏洞?

好吧,下面看了下rfc,简单介绍一下uri的定义:

给大家一个rfc上的正则福利,这个正则是为了匹配上面的uri的定义。

下面是这个正则对url进行解析:


好吧。那除了Oauth平台用这个正则来真正找到domain还有什么更简单的方法来预防这个漏洞吗?嗯,其实加盟的第三方一开始只要遵循了Oauth2.0的规范。还是比较容易预防这个漏洞的。

3. 再简单介绍下Oauth2授权 :

针对Oauth2协议, Oauth2中有几种获取 accessToken的方式(就是response_type 这个域):

1. code, 这种是先获取到 code, 然后再用code 来获取accessToken

2. token, 这种是直接获取到accessToken ,这个比code更加直接。

而对于token这种方式进行授权的。 就会带来比code方式更加危险的问题。 造成这个问题的原因就是 redirectUrl 这个参数的domain在各大平台上判断的都有问题。 原因也很简单,大家都觉得这东西安全不是那么重要, 都https, 还在意什么安全问题。 另外授权流程那么的复杂,谁在意这鬼东西还能出来什么安全问题。再说即使是有问题,那肯定第三方自己就能修复,完全不需要各大平台修复。各大平台只需要保证你发来正确的信息,给你正确的返回就行了。所以这个漏洞等级很低。

但是这个漏洞,的确可以将code 或者 accessToken通过这个漏洞发送给恶意第三方。特别是第二种使用token的直接授权方式。除非Oauth平台修改。加盟的第三方应用无法做出有成效的修补。所以我下面只介绍使用第一种授权方式code的修补方案。

4. 加盟第三方如何修补和预防

首先,你这个应用需要是 Server应用(有自己的服务器或者有后台不可见代码)。 JS的app完全预防不了(之前指定Oauth2.0时说各个浏览器都要出份力,但好像也没有见到这对JS app的Oauth2.0有什么改进之处。)只要代码可见,不管明文密文,都一定是能获取到该应用的appKey和appSecret的。所以怎么都能获取到用户真正的accessToken,不管是钓鱼还是伪造。

不考虑直接客户端获取accessToken的形式。 Oauth说白了就是 Oauth平台和Server之间通信。如下图:


下面我们考虑浏览器:


下面我们考虑一下恶意程序,恶意程序只会出现在两个地方。一个是以脚本的形式出现在浏览器中(见恶意程序1)。另外一个是让信息流强制流向恶意服务器(见恶意程序2)。


下面我们分别就两个恶意程序来讨论一下如何修补规避漏洞。

  • 对于恶意程序1, 这里最简单的做法就是让JS只知道部分信息。例如登录中常用的shadow cookie的方法。JS用于显示用户名或者登录信息的cookie 不是httpOnly的,而真正作为服务器认证凭证的cookie则使用httpOnly cookie。这样恶意的JS无法获取到真正有用的cookie. 详见 Cookie的特性
  • 对于恶意程序2,这里需要在我们的server请求Oauth平台时,首先植入一个认证信息,例如cookie/session等。即使通过url漏洞跳转到恶意程序2, 恶意程序2这时可以获取到Oauth平台发来的code和state. 而浏览器通过cookie的domain属性保证开始植入的认证信息不会发送给恶意程序2.
    • 恶意程序2 通过其他方式和我们的server进行交互时,因没有认证信息,我们的server不会返回给它accessToken。

    • 恶意程序2 直接和Oauth平台交互,因为没有appSecret和appKey, 也无法获取到accessToken.

5. 结论

做了以上两步,redirect_uri的漏洞不管怎么样,都只能称为Oauth平台的bug。而你的服务是足够安全的。上面的安全是基于电脑没有中病毒,以及服务自身没有其他的隐患的假设。顺便一提,oauth2中redirect_url漏洞才会造成威胁。Oauth1.0a协议中这种漏洞不会造成威胁。

看了半天各种论坛,是在不知道从哪里下手,所以自己写一篇自己刷机的新的。凤凰那个软件好像已经挂了,每次打开就是service is not
authorized. 所以还是使用nokia自己的官方下载平台。见csdn的下载地址:

http://download.csdn.net/detail/cctt_1/7286659

然后是找要下什么,必须填两个一个是type,一个code.
这个在什么地方找,靠google/baidu/360都是没有用的。其实就在自己的手机里。将手机拆开,电池板卸下来。见下图:

![](http://img.blog.csdn.net/20140503141133562?watermark/2/text/aHR0cDovL2Jsb2
cuY3Nkbi5uZXQvY2N0dF8x/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/7
0/gravity/SouthEast)
其中两个红圈中的就是type和code.  type (RM-411) code:(0568193)

这里有type和code.下面就是将type和code填入到系统中search. 然后download.
那个诺基亚的东西下载速度奇慢,感觉是单线程TCP下载。

![](http://img.blog.csdn.net/20140503141244062?watermark/2/text/aHR0cDovL2Jsb2
cuY3Nkbi5uZXQvY2N0dF8x/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/7
0/gravity/SouthEast)
然后到下载的地方把package copy到JAF的search路径中(下面会说 这个JAF search路径是什么地方)。下载到哪里了呢?见下图:

![](http://img.blog.csdn.net/20140503141325343?watermark/2/text/aHR0cDovL2Jsb2
cuY3Nkbi5uZXQvY2N0dF8x/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/7
0/gravity/SouthEast)
先安装nokia套件,这个在 官网这里
找到自己的机型,然后选择安装套件即可。 如果还没有安装JAF, 那么到这里安装:

http://download.csdn.net/detail/cctt_1/7286737

安装完毕后, 运行见下图:

![](http://img.blog.csdn.net/20140503141538578?watermark/2/text/aHR0cDovL2Jsb2
cuY3Nkbi5uZXQvY2N0dF8x/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/7
0/gravity/SouthEast)
这里按照 1,2,3,4,5,6的执行顺序。另外看准确哪些是打勾的项目。 出现了"Select Phone Model"框。 在框中选择:

![](http://img.blog.csdn.net/20140503141718062?watermark/2/text/aHR0cDovL2Jsb2
cuY3Nkbi5uZXQvY2N0dF8x/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/7
0/gravity/SouthEast)
你看到上图中 Checking path:  C:\Program Files\Nokia\Phoenix\Products 这个路径了吗?
这就是JAF的search路径。然后将刚才下载的 RM-411 整个文件夹放入到 C:\Program
Files\Nokia\Phoenix\Products  这个路径中。然后点击 “Select Phone Model”中的RM-411 然后点击 OK.
见下图:

![](http://img.blog.csdn.net/20140503142010015?watermark/2/text/aHR0cDovL2Jsb2
cuY3Nkbi5uZXQvY2N0dF8x/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/7
0/gravity/SouthEast)
这个时候有三个亮灯。然后在你电脑上用UBS连上你的手机,然后点击上图中的Flash,就开始刷机了。 注意点击后圈中的一句话:PRESS POWER ON
NOW! 这个时候就按手机的开机键。然后就开始刷机了。注意手机电量需要充足。否则刷一般就挂掉了。不过挂掉也不怕,重来来一遍继续刷即可。

顺便说一句,我已经成功的把5220刷成板砖了。准备去送修了。第一次刷的时候,开机变成了 白屏+ Test mode字样。
第二次刷的时候就中间写flash的时候写出了 windows 访问内存异常。 第三次,嗯,就再也写不进去了。 flash failed 这样的提示。
查了一下,大概是说驱动装的都有问题, 需要新机器新环境之类的。
好吧,给大家一个学习错误的机会~~~ 另外JAF在win7 64位上没有合适的运行环境。总是 wincards.dll有问题。

最近在学习java8的新特性。 首先先来安装java8。 Java8 在 win7下很容易安装,但是官方不支持win XP. windows Xp java8无法安装。如果在xp下安装会报这样的错误:

无法定位程序输入点 RegDeleteKeyExA 于动态链接库 ADVAPI32.dll


如果非要在32位的xp系统,可以先下载java8的jdk的exe文件。然后使用7zip进行解压。再解压tools.zip文件。最后你能得到下面的目录结构:


在这个目录下执行如下的命令:

1
FOR /R %f IN (*.pack) DO "d:\java8\bin\unpack200.exe" -r -v "%f" "%~pf%~nf.jar"

注意d:\java8\bin\unpack200.exe 这个。这个是你的解压路径中的unpack200.exe。上面的命令就是使用解压中的unpack200.exe将整个解压路径中的.pack文件转换为.jar文件. 转换完之后,d:\java8这个路径就已经是JAVA_HOME路径。

下面就是IDE也能使用java1.8的新特性。下载一个新版本的eclipse。另外如果是eclipse kepler 可以在eclipse market中下载一个java8的插件"Eclipse Java Development Tools Patch with Java 8 support(for Kepler SR2)"。 然后在tab中windows->Pererfence->java->Installed JREs => “add…”=> Standard VM => JRE home选择之前的那个解压路径。剩下的内容eclipse会帮你填充。然后一路点击确认就可以了。


然后将execute Environments 选中 javaSE-1.8, 然后选中 Java8 ,点击确定。


新建立一个工程。然后将Java Compiler选中 1.8的版本。

然后新建一个interface。编写如下代码。如果IDE没有报错。那么eclipse IDE就已经可以使用java8的新特性了。

1
2
3
4
5
6
7

public interface TestDefault {
default void pirnt(){
System.out.println();
}
}

下面的图里没有编译错误

对于java8, 网上说了55条新特性。但详细看来,最有用的也就那么几条。下面罗列一下:

  1. 消除持久代. 再也没有OutOfMemoryError. PermGen Space 这样的错误了。持久代(Interned 字符串,类元数据和类静态变量)合并入Heap Space或者direct buffer. 以后可能更多的会OutOfMemoryError: Heap space.
  2. Annotation更加强大。可以在函数参数中加入@notnull 这样的标记,来判断参数是否为null. 另外还有可以用来消除public,private 这些关键字。
  3. 加密方面,AES使用了CPU指令,可以更快加解密。用SHA-224代替SHA-1.支持了64位windows的PKCS#11,加解密AES,RSA支持更多的PKCS。
  4. 加入JS 引擎。 可以运行js脚本。这对node.js, webview的使用意义更大
  5. Http URL, 支持Http GET, POST 操作,这个是要在未来代替HttpClient吗?
  6. 使用平衡树处理HashMap中的冲突,对于频繁的冲突会将List替换成平衡树。这样做会提高HashMap的插入速度。
  7. Base64终于可以名正言顺的使用。java.util.Base64.Encoder 和java.util.Base64.Decoder.而不要使用过去的sun.misc.BASE64Encoder等。
  8. 并发的支持。增加适用于频繁更新但非频繁读取的原子变量。ConcurrentHashMap更新,Fork-Join框架更新。Arrays.sort有了并发的sort => Arrays.parallelSort,可以很好的利用多核的资源。据说性能在数据均衡的情况下最少提升了30%。
  9. lamda表达式引入,接口中的default function以及stream

对于以上几点主要说说第8和第9点。对于第8点做了个小实验验证了一下并发情况下的排序性能提升:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

public class TestJava8 {
public static void main(String[] args) {
int count =10000000;
int[] k = new int[count];
Random r = new Random();
for (int j= 0; j < 5; j++) {
for (int i = 0;i<count; i++) {
k[i] = r.nextInt();
}
int [] g = Arrays.copyOf(k, k.length);
int [] t = Arrays.copyOf(k, k.length);
long start = System.currentTimeMillis();
Arrays.parallelSort(g);

System.out.println(System.currentTimeMillis() - start+":parallel:");

start = System.currentTimeMillis();
Arrays.sort(t);
System.out.println(System.currentTimeMillis() - start+":serial:");
}

}
}

在公司里的伪双核的机器上跑。反复几次,当如也有可能期间夹杂了gc()的影响。结果基本是这样的:

1
2
3
4
5
6
7
8
9
10
11
12

1398:parallel:
1373:serial:
1224:parallel:
1287:serial:
1261:parallel:
1274:serial:
1304:parallel:
1314:serial:
1234:parallel:
1480:serial:

从结果上看,基本上并发还是需要使用在数据量较大的情况下。但是如果数据量大是否能使用内存排序还是个问题。对于parallelSort,Java8还是使用的Fork-Join的模式。对于小数据量效果并不佳,因为开启线程还是要费些时间的。我个人认为java8 对于Arrays.sort()这个接口,还是默默的做并发/非并发判断比较好。毕竟多核都对java程序员隐藏的话,并发sort()啥的最好也能对java程序员隐藏。否则又要在上面包一层先判断一下数据量大小,然后再执行sort() or parallelSort().  另外官方所说的数据均衡的情况下最少提升了30%。这个意思是说官方的测试在比较均衡的数据集跑出来的,但是一般在生产环境中的数据集是不均衡的。

java8 最大的亮点就是lamda表达式。可以使用java进行函数式编程。这样的好处是什么呢?

  1. 如果lamda表达式只对变量进行只读操作,那一定是threadSafe. 非只读的情况需要特殊判读是否是threadSafe.
  2. 如果是threadSafe 一定可以并发执行
  3. 从参数推出结果,是针对API接口编程。(这点有些虚)
  4. 返回的结果也可以是函数

一段代码这样写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

// 求一个List中的最大值
public static void main(String[] args) {
List<Integer> k = new ArrayList<Integer>();
k.add(1);
k.add(2);
k.add(3);
k.add(8);
k.add(11);
k.add(20);
k.add(50);
int max = Integer.MIN_VALUE;
for (int j : k) {
if (max < j) {
max = j;
}
}
System.out.println(max);
}

假如这段代码中查找最大值成了性能瓶颈,那么需要将这段代码改为并发操作。想想就不容易。那现在使用lamda表达式这样写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

// 求一个链表中的最大值
public static void main(String[] args) {

List<Integer> k = new ArrayList<Integer>();
k.add(1);
k.add(2);
k.add(3);
k.add(8);
k.add(11);
k.add(20);
k.add(50);
Optional<Integer> max = k.stream().max((a,b) -> {return a-b;});
System.out.println(max);
}

想变成并发操作这样写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

// 求一个链表中的最大值
public static void main(String[] args) {

List<Integer> k = new ArrayList<Integer>();
k.add(1);
k.add(2);
k.add(3);
k.add(8);
k.add(11);
k.add(20);
k.add(50);
Optional<Integer> max = k.parallelStream().max((a,b) -> {return a-b;});
System.out.println(max);
}

很简单吧。就是将k.stream() 改成 k.parallelStream() 而这个之所以简单是基于这样几点。

  1. 编写ThreadSafe的函数或者lamda表达式, 本身就很容易变为多线程操作。

  2. Stream方式处理数据。

  3. 传入的参数数据只关系上一步的结果,而不关系其他数据在本步所做操作产生的中间数据。

  4. Fork-Join提供了很好的多线程框架

这里八卦一下Java8 为什么做了除了lamda表达式之外的一些事情。

  1. interface中的default function接口。 原因是lamda表达式是为了并发做的。那么并发包concurrent中已经有了很多接口,希望和过去的版本兼容,减少迁移的工作量。如果interface中新增了接口,那么使用jdk8以前的代码就会有编译错误。要解决这个编译错误,要么抽象出一个abstract class实现接口,并加入缺省的实现。要么在以前的所有的实现中都加入这个接口。所以听听就觉得这次迁移是个大的工作量。那default function就诞生了。为了不写abstract class, 并且使用jdk5,jdk6,jdk7的代码可以方便迁移到jdk8中。default function 这样使用:
1
2
3
4
5
6
7
8
9
10
11
12
13
14

public class TestF {
public interface TestDefault {
default void print(){
System.out.println("ew2we");

}
}
public static void main(String[] args) {
// 写个匿名类
new TestDefault(){}.print();
}
}

另外值得注意的一点就是:如果有两个interface实现了同名的default function. 那编译会报错。

  1. SAM (single abstract method) 也就是这个Annotation @FunctionalInterface.如果带上这个Annotation,那么这个接口仅且只有一个未实现的方法。当然可以有多个已经实现的default方法。这个是为了lamda表达式做的。lamda表达式是形如: (param list)-> {code statements;},基本上可以看作是一个函数。所以要求仅且只有一个函数方法没有实现就是为了代码好看。@FunctionalInterface 写法如下:
1
2
3
4
5
6
7
8
9
10
11

@FunctionalInterface
public interface DefaultInterface {
default void hello() {
System.out.println("hello");
}
default void zzz() {
System.out.println("zzz");
}
void z(); // 只有一个未实现的函数
}
  1. Stream 是为了更好的使用Fork-Join框架。Stream就是pipeline. 对于只关心输入和输出的API来说。编写Stream方式的代码非常容易实现并发。其实就是把一组相似的数据丢到一个池中,然后线程池针对这些数据开始做事情。在执行完毕之前,需要join所有的线程,将最终结果放入到最终返回的池中并返回。而流式的操作最简单的就是调用方便。可以理解为以前所说的链式调用。但是对于这种编写代码的Style,还是尽量将每次调用写在不同的行中,这样会方便调试。下面是Stream的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

public static void main(String[] args) {

List<Integer> k = new ArrayList<Integer>();
k.add(1);
k.add(2);
k.add(3);
k.add(8);
k.add(11);
k.add(20);
k.add(50);
// 找到list中大于2的三个数,不要求顺序,最后打印出来
k.parallelStream().filter((a)->{return a>2;}).unordered().limit(3).forEach((a) -> {System.out.println(a);});
// 获取大于1的list元素中的最大值。如果list中的元素都不大于1,那么返回Optional.empty
Optional<Integer> max = k.parallelStream().filter((a)->{return a>1;}).max((a,b) -> {return a-b;});
System.out.println(max);

}

这里简单介绍一下lamda表达式中语法。 这个lamda表达式就是实现了一个抽象方法。所以需要注意那个抽象方法到底是不是void返回,这关系到你的表达式中是否有return.lamda的语法是前面是参数列表,0个参数或者多于一个参数时必须使用圆括号括起。如果只有一个参数,那么可以省略圆括号。然后是一个箭头->。再后面是函数体。函数体需要使用花括号括起,如果只有一条语句,可以省略花括号。下面的lamda表达式都是对的。

1
2
3
4
5
6

a -> return a+1;
(a) -> return a+1;
(a) -> {return a+1;}
(a,b) -> {int c = a+b; return c>>1;}
()->System.out.println("empty");

另外注意一下final 变量和this指针。

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
36
37
38
39
40
41
42
43
44

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

public class TestLamba {

public static class Score {
int score;
public List<Integer> findLarger(List<Integer> scores) {
List<Integer> tmp = new ArrayList<Integer>();
//注意这里的this是 Score Object的this
scores.stream().filter((a) -> {return a > this.score;}).forEach((a)->{tmp.add(a);});
return tmp;
}
public static List<Integer> findLarger(List<Integer> scores, Score myScore) {
List<Integer> tmp = new ArrayList<Integer>();
//myScore = new Score(); compile wrong
//这里的myScore在语义上是final的, 但是myScore中的域可以变化
//myScore.score = 3; 这个是正确的,放在lamda表达式中也正确。
//所以对于myScore而言lamda表达式并不是threadSafe
scores.stream().filter((a) -> {return a > myScore.score;}).forEach((a)->{tmp.add(a);});
return tmp;
}
}

public static void main(String[] args) {
Arrays.asList().forEach((a)->System.out.println("I'm "+a));
List<Integer> k = new ArrayList<Integer>();
k.add(1);
k.add(2);
k.add(3);
k.add(8);
k.add(11);
k.add(20);
k.add(50);
Score s = new Score();
s.score = 11;
s.findLarger(k).forEach(a->{System.out.println(a);});
System.out.println("dsdsd");
Score.findLarger(k, s).forEach(a->{System.out.println(a);});
}
}

参数里面没有final变量,java8在语法上并不要求外部引用参数是final,但是语义上还是final的,这个类似匿名内部类。另外this指针一定是外层类的指针。lamda表达式是没有this的。现在java的jdk已经可能非常容易的使用多核资源。所以非常期待java以后的jdk能够利用多机器的资源例如现在的map/reduce.这样写程序就越来越简单了。看起来就是这样一个趋势~~~继续进化吧~

有很多人反馈这样一个问题: 有道云笔记 Mac版 输入用户名密码登陆不上去。

这个问题的原因可能是这样的:你们公司的网络封掉了 note.youdao.com 443 端口。所以先试试 telnet note.youdao.com
443 看看是否能正常通信。如果提示连接不上,那就果断的找找你们公司的IT部门吧。

抓包如下:

1
2
3
4
5
6
7
8
9
10
11
12

14:39:00.587381 IP 10.242.48.210.63785 > note.youdao.com.https: Flags [S], seq 128747375, win 65535, options [mss 1460,nop,wscale 4,nop,nop,TS val 1067401063 ecr 0,sackOK,eol], length 0
14:39:01.690971 IP 10.242.48.210.63785 > note.youdao.com.https: Flags [S], seq 128747375, win 65535, options [mss 1460,nop,wscale 4,nop,nop,TS val 1067402161 ecr 0,sackOK,eol], length 0
14:39:02.793906 IP 10.242.48.210.63785 > note.youdao.com.https: Flags [S], seq 128747375, win 65535, options [mss 1460,nop,wscale 4,nop,nop,TS val 1067403258 ecr 0,sackOK,eol], length 0
14:39:03.896801 IP 10.242.48.210.63785 > note.youdao.com.https: Flags [S], seq 128747375, win 65535, options [mss 1460,nop,wscale 4,nop,nop,TS val 1067404357 ecr 0,sackOK,eol], length 0
14:39:04.901675 IP 10.242.48.210.63785 > note.youdao.com.https: Flags [S], seq 128747375, win 65535, options [mss 1460,nop,wscale 4,nop,nop,TS val 1067405357 ecr 0,sackOK,eol], length 0
14:39:05.906948 IP 10.242.48.210.63785 > note.youdao.com.https: Flags [S], seq 128747375, win 65535, options [mss 1460,nop,wscale 4,nop,nop,TS val 1067406357 ecr 0,sackOK,eol], length 0
14:39:07.916512 IP 10.242.48.210.63785 > note.youdao.com.https: Flags [S], seq 128747375, win 65535, options [mss 1460,nop,wscale 4,nop,nop,TS val 1067408357 ecr 0,sackOK,eol], length 0
14:39:12.429264 IP 10.242.48.210.63785 > note.youdao.com.https: Flags [S], seq 128747375, win 65535, options [mss 1460,sackOK,eol], length 0
14:39:20.701959 IP 10.242.48.210.63785 > note.youdao.com.https: Flags [S], seq 128747375, win 65535, options [mss 1460,sackOK,eol], length 0
14:39:36.845313 IP 10.242.48.210.63785 > note.youdao.com.https: Flags [S], seq 128747375, win 65535, options [mss 1460,sackOK,eol], length 0
14:40:08.967998 IP 10.242.48.210.63785 > note.youdao.com.https: Flags [S], seq 128747375, win 65535, options [mss 1460,sackOK,eol], length 0

发现seq一样,说明第一个包一直在重传。所以是网络问题导致的。
另外还需要看一下note.youdao.com的域名IP是否正确。这个域名很有可能被小的运营商劫持。正确的IP是:

1
2
3

ping note.youdao.com
PING note.youdao.com (123.58.182.252) 56(84) bytes of data.

《布满贫民窟的星球》读后感
根据文中贫民窟的定义: "低成本,生活贫穷的高密度的生活空间。"感觉自己已然置身其中。刚毕业群居的大学生和临时棚户住着的农民工当然也算在其中。看完那本书最大的感觉不是对贫民窟悲惨命运的同情,不是对贫民窟基础设施的叹息。而是恐慌。巨大的恐慌。感觉未来巨大的人间地狱其实距离自己并不遥远。如果不努力,自己随时都可能拉入这巨大地狱的中心。而自己现在做出的努力,只是尽力在这巨大地狱边缘挣扎。所以与其同情别人,不如先同情自己。

恐慌是必然的。最终的最终是否如同该书所说的如此悲惨?最终的最终是否有有一剂良药?反正该书指出,曾经为改善贫民窟做的努力都是镜花水月。嗯,除了让自己努力跻身精英集团,别无他法。所以,这样看来,这本书本是一本励志书。

年后一直在忙活跳槽的事情。要不是家里有大事发生,估计跑出去面试会更勤快些。

这些公司不知道为什么,距离我住的地方实在是太遥远了。每每从家里出来都要一个多小时才能到这些公司。然后面了一轮后就又要回公司上班。感觉每天都比较折腾。所以面的
公司不算多。offer也不多。平庸的公司开出的价格与工作年限是成正比的。不过也好,给不起钱的公司就不去好了。不过也学到一招,开始和Hr沟通时就给年限和薪水期
望,给不起的就省得去面了。

从面试的经历来看,对自己的工作经验的增长确实有好处。有些公司的面官会让你知道自己哪些东西还没有掌握。有些公司的面官会让你知道使用哪些技术会让自己的项目更加成
功。我个人认为每次面试都是一次技术交流。当然从开始到最后一直面低级算法的除外。

面试的这些天忙碌也很充实。每天都要面对一堆自己完全没有考虑的问题,每天都要学习一堆自己从未掌握的知识。想想蛮不错的。

看了酷壳的这篇帖子: http://coolshell.cn/articles/11021.html (从黑掉 github 学web安全开发)

觉得有几点需要学习或者了解:

1. ff/chrome 都认为 <img src=”///attackersite.com”> 这个是一个绝对路径, 最终会转换为 <img src=”attackersite.com”> , 但是server的开发可能会用正则匹配,认为这个是一个相对路径。赞呀~~就我个人开发而言,我一般看到 开头是 “/”, 就认为是一个相对路径,当然还会做一个补刀操作就是在 “/” 之前加入自己网站的sheme, 整个域,port, web content path等。 例如 ** ///attackersite.com ** 就会补为:http://chillyc.info ** ///attackersite.com ** , 然后我估计就跳转到一个404的页面了。大家可以试试。

2. Referer 漏洞。 这个观念应该加强。 主要原因是,如果有外域的图片或者其他的什么信息,当前的url路径就会变为 reference 送给外域。特别是喜欢在 url中加入特殊认证信息的地方。例如sid啥的。 这里有两点可以规避, 第一是内域使用Https, 对于外域一般使用http,这样隐秘信息就不会通过reference 发送给外域。当然浏览器的某些版本也可能会禁止访问从Https页面发送给http页面的请求。第二个就是在HTML5, 使用, 标签可以加入 noreferer 这个属性,表示点击这个链接不会有Referer header发送。但是酷壳这篇文中使用的是 img 标签, 并且还是个内域的。这就无能为力了。

3. 对于 Oauth2 协议而言,其实只需要限制域名就可以。有时候限制callback URL 会让API的使用者非常难受。

4. 看完Oauth2攻击的那篇帖子,其实也就是只针对github中的gist这个东西产生了奇妙的漏洞。token都放在callback url中一起传。一般access token 是有server端获取code之后,使用consumer_secret来获取的。也就是如果没有应用的consumer_secret, 只有access token是没有用的。其实最简易的攻击就是自己做个应用,直接从server端获取access_token.这个比上面那些漏洞更加靠谱。所以做钓鱼应用就好了,嗯嗯,就是这样。

今天写写 MongoDB的事情。这货是一个document-orientied, 基本上使用的json进行交互的。他把那个形式很像json的东西叫做BJSON. 里面有一些mongoDB自己定义的数据类型。这种数据形式在传输存储很方便。

首先介绍一下MongoDB的数据结构

  • database : 这个和mysql中的database一致。 默认使用的local, 可以自己创建新的。例如 在shell中写 db.xxx 然后就自动创建了xxx 这个collection

  • collection: 这个概念和mysql中的table保持一致。

  • document: 这个和mysql里的row或者 record保持一致。

这个是MongoDB的三个最主要的结构。

下面是如何使用。

下载安装

首先去 www.mongoDB.org 上下载一个mongoDB的最新版。然后解压到某个目录。然后使用命令行进入到该目录下。

创建 数据存储文件夹,这里可以直接创建一个 /data/db的文件夹(mkdir -p /data/db)。然后在命令行里执行如下命令启动mongodb server.

1
mongod --dbpath ./data/db

这样mongoDB就启动了,一般端口为 27017. 你可以使用help来修改Port等。这里就不过多介绍。

然后重新开一个命令窗口,使用命令进入到该目录下,执行下面语句启动mongoDB client.

1
mongo

如果使用的都是默认端口,这里就可以正常启动了。如果你修改了mongoDB server的端口,这里需要改成一致的。

数据库CRUD(Create, Retrieve, Update, Delete)操作

下面就是命令行选择database, 创建collection, 存储document。首先:

1
2
3
4
5
6
7
8
db
查看需要现有的database.
show dbs

使用ooo作为database
use ooo;

如果这里不写use ooo,那默认就是local.

然后是数据库的操作了。

插入

1
2
3
4
5
6
7
8
var a = {"value":1,"value2":['a','b']}
var b = {"value":2}
var c = {"str":‘aaa’}
插入数据,xxxx是collection
db.xxxx.save(a);
下面也是插入数据
db.xxxx.insert(b);
db.xxxx.insert(c);

查看数据

1
2
3
4
5
6
7
8
获取全部数据
db.xxxx.find()
获取一个数据
db.xxxx.findOne()

查询特定的记录

db.xxxx.find({"value":1})

更新操作

1
2
3
4
5
6
7
8
9

db.xxxx.update(
{ value: { $lt: 18 } },
{ $set: { status: "A" } },
{ multi: true }
)

这里 { value: { $lt: 18 } },是说 value < 18, { $set: { status: "A" } },是说要set status 为A, 如果没有那个域,则创建出来。
{ multi: true } 则是处理多个。

删除操作

1
2
db.xxxx.remove({"value":1})
删除value 为1 的document.

这里看起来非常简单。

结束语

MongoDB的强大之处在于 JSON串里想写什么就可以写什么。相同的collection中document可以完全不相同。(但是为了后期的维护,还是尽量相同。)这是稀疏数据库的一大优势。

建索引MongoDB有很多种,可以建立unique 索引, TTL 索引(数据会过期失效,默认为此索引), 多列组合索引,空间索引,全文索引和hash索引。这里的多列索引和mysql的一致。对于全文索引,mongoDB建议不要在生产环境(线上环境)中使用,因为这个建索引很慢,性价比不高。

对于mongoDB来说,他的索引可以使用{“backgroup”:true}来进行后台异步创建,也可以使用前台索引。但是前台索引会对性能造成极大的影响,所以mongoDB官方文档建议新起一个mongoDB server的实例进行创建。然后使用副本启动的方法将创建好的索引作为副本启动。

对于mongoDB,安全性做的比较粗略。也是对于操作,对于database等特殊数据需要分配权限。因为没有视图等概念,这些安全性做的比mysql来说粒度大了一些。但是mongoDB支持SSL方式访问,也支持kerberos认证。不过Kerberos认证现在只有微软那边在用,之前K3,K4被破,人心惶惶。现在不知道情况如何。

今天就先介绍这么多吧。

0%