浏览器的渲染性能

Posted on

不知不觉,2018年的春节也要来临了。今年只是元旦回家了,春节并不打算回家。回家曾经是一件美好的事情,不知从何时开始,却是那么的揪心。最近也有读很多关于性能优化和底层的前端知识。不想就此停滞,只能不断前进。

浏览器渲染

我们都知道,现在主流显示器的频率是60Hz,也就是1秒要刷新60次。这样才能保持画面的流畅,特别是玩游戏的时候,我们非常在意帧数。前端开发也是一样,所以我们每一项操作都最好在10毫秒之内完成,否则会产生所谓的卡吨现象,影响用户体验。

渲染过程

render pipeline

浏览器的渲染主要是分为5个步骤,我们需要了解这些知识才能编写性能更好的代码。

  1. JavaScript 我们经常使用JS来实现一些复杂的视觉效果,数据排序,DOM操作等等。
  2. 样式计算 此过程是根据样式匹配选择器来计算哪些元素应用哪些CSS规则的过程。不过浏览器会对常用的选择器进行性能优化,例如类选择器。
  3. 布局 在知道每个元素的应用规则之后,浏览器开始计算所需要的空间大小以及其处在屏幕的位置。网页的布局中,一个元素的变化会影响到其他元素的位置。例如Body的宽度变窄之后其子元素的宽度也都会发生变化。
  4. 绘制 绘制是填充像素的过程。它涉及绘出文本、颜色、图像、边框和阴影,基本上包括元素的每个可视部分。绘制一般是在多个表面(通常称为层)上完成的。
  5. 合成 由于页面的各部分可能被绘制到多层,由此它们需要按正确顺序绘制到屏幕上,以便正确渲染页面。对于与另一元素重叠的元素来说,这点特别重要,因为一个错误可能使一个元素错误地出现在另一个元素的上层。

JS / CSS > 样式 > 布局 > 绘制 > 合成

render pipeline

如果修改了元素的布局属性,也就是改变了元素的几何属性(例如宽度,高度)。那么浏览器就必须检查所有元素,然后重新排版页面。任何受影响的部分都要重新绘制,再重新合成。

JS / CSS > 样式 > 绘制 > 合成

render pipeline no layout

如果之修改了元素的绘制属性,例如背景图片或者文字颜色,并不会对其他元素的布局造成影响。浏览器会跳过布局,但仍然执行绘制。

JS / CSS > 样式 > 合成

render pipeline no layout

目前既不要绘制也不要布局的属性只有transform属性和opacity属性。所以在实现CSS动画的时候,优先使用这两个属性。

如何查询CSS属性触发上面3个流程的哪一个,可以去CSS Triggers 查询。

参考:

Rendering Performance

JavaScript中的数据类型

Posted on

隔了一个月,我又来发博客了。最近的工作老是在写CSS和HTML。但是我更想学习JS啊!我一直都觉得HTML和CSS是属于设计范畴的,而JS才是真正属于工程师的逻辑范畴。 况且最近Github上有一个神奇的项目Screenshot-to-code-in-Keras可以把截图直接生成HTML代码,我觉得只是单纯的从PSD翻译成页面的工作迟早要被淘汰。

最近在读You Don't Know JS这本书。书上讲解了很多关于JS的细节知识,对于深入了解JS有很大帮助。所以想在读的过程中把一些觉得有意思的东西记下来,便于以后复习。

类型

JavaScript中有七种内置类型:

  1. 空值(null)
  2. 未定义(undefined)
  3. 布尔值(boolean)
  4. 数字(number)
  5. 字符串(string)
  6. 对象(object)
  7. 符号(symbol, ES6新增)

除了对象之外,通称基本类型。

JavaScript中的设计BUG

typeof null === "object"; // true

正确的返回结果应该是null。但这个BUG由来已久,修复反而会出问题。所以我们需要使用复合条件来判断:

(!a && typeof a === "object")

接下来是NaN的问题:

var a = 2 / "foo";
var b = "foo";
a; // NaN
b; "foo"
window.isNaN( a ); // true
window.isNaN( b ); // true
NaN === NaN // false

很显然"foo"不是NaN,但显然它也不是数字。这个BUG也存在很久了,在ES6时代,我们可以使用Number.isNaN来解决。 ES6之前的polyfill是:

if (!Number.isNaN) {
  Number.isNaN = function(n) {
    return (
      typeof n === "number" &&
      window.isNaN(n)
    );
  };
}

并且NaN是JS中唯一一个不等于自身的值。

值和类型

JavaScript中的变量是没有类型的,只有值才有。变量可以随时持有任何类型的值。

undefined 和 undeclared. 变量在未持有的时候为undefined, 此时typeof 返回 undefined. 大多数开发者倾向于将 undefined 等同于 undeclared(未声明),但在 JavaScript 中它们完全是两回事。已在作用域中声明但还没有赋值的变量,是 undefined 的。相反,还没有在作用域中声明过的变量,是 undeclared 的。

var a;
a; // undefined
b; // ReferenceError: b is not defined
typeof a; // "undefined"
typeof b; // "undefined"

虽然b 是一个 undeclared 变量,但 typeof b 并没有报错。这是因为 typeof 有一个特殊的安全防范机制。防止因为未定义导致程序终止运行。

值和引用

在许多编程语言中,赋值和参数传递可以通过值复制(value-copy)或者引用复制(reference-copy)来完成,这取决与我们使用什么语法。但是JavaScript 对值和引用的赋值 / 传递在语法上没有区别,完全根据值的类型来决定。

简单值(即标量基本类型值,scalar primitive)总是通过值复制的方式来赋值/传递,包括null 、 undefined 、字符串、数字、布尔和 ES6 中的 symbol 。

复合值(compound value)——对象(包括数组和封装对象)和函数,则总是通过引用复制的方式来赋值/传递。由于引用指向的是值本身而非变量,所以一个引用无法更改另一个引用的指向。

var a
var b
a; //
b; //
= [1,2,3];
= a;
[1,2,3]
[1,2,3]
// 然后
b = [4,5,6];
a; // [1,2,3]
b; // [4,5,6]

b=[4,5,6] 并不影响 a 指向值 [1,2,3] ,除非 b 不是指向数组的引用,而是指向 a 的指针,但在 JavaScript 中不存在这种情况!

参考:

You Don't Know JS

前端开发的技术栈

Posted on

我发现自己博客没次开头都是在说近况和废话。这次也不例外。哈哈哈,工作也算顺畅,顺便总结下最近的前端开发的技术栈来回顾下这半年学到的知识。

HTML开发

对于大型项目,直接书写HTML代码是一个非常繁琐和头疼的事情,因为HTML需要闭合,每次找匹配的HTML标签都要非常花功夫。所以现在的开发都是使用预处理器来书写代码,例如主流的Pug,通过缩进来控制元素的嵌套,还支持多种语法。非常适合大型项目的开发,再也不用担心修改代码的时候出现HTML元素标签没有闭合的情况发生了。而且还规避了一些语法错误,例如在p标签里嵌入block元素是非法的。如果强行嵌入的话,你会发现生成的HTML代码是错误的。

CSS开发

CSS开发更多的需要是良好的模块化功能和合理的作用域。这时候也需要通过预处理器来进行操作,推荐使用SCSS。这里很多人对sassscss之间的区别有疑问。简单的来说,SCSS的格式更接近CSS,所以比较容易上手。但是SASS是通过缩进来书写的,对新手不太友好。所以建议大家使用SCSS来书写模块化代码。

JavaScript开发

现在主流的浏览器支持的JavaScript版本是es5。但是众所周知,JavaScript(es5)有很多陷阱和缺点,例如this指针问题和异步处理等等。基于原型连的继承对于面向对象开发者来说也很不友好。所以推荐使用es6来书写代码。可以使用基于class的继承,和解决this指针问题。而且还能使用import进行模块化开发。虽然只是语法糖,但也提升了开发效率。

自动化构建工具

我们使用了预处理器来书写代码,并使用新版本的es6语法。但是目前浏览器并不支持直接解析这些内容。所以我们需要构建化工具来处理从Pug生成HTML,从SCSS生成CSS,把es6语法的JavaScript转换成es5语法。对于SPA网站推荐使用webpack,而对于普通网站的构建推荐使用Gulp。这里区别开的原因是,webpack必须指定入口文件,但是Gulp只需要指定需要处理的文件或文件夹就可以了,支持通配符匹配。对于多页面的传统网站来说非常便利。

浏览器兼容处理

这是每个前端工程师最头疼的地方了,因为每个浏览器支持程度都不一样。在使用比较新的API记得去Can I use查看下各个浏览器的支持情况,如果实在是需要这个功能的话,那就只能去寻找polyfill了。

参考:

Why p tag can't contain div tag

网站开发中的Modal问题

Posted on

最近在开发的网站基本上全是使用弹出的对话框(Modal)来进行UI交互的,所以对于Modal的处理也算积攒了一点经验。便想写下来供自己以后参考和学习。说实话,在目前响应式布局的主流开发方式下,Modal非常不适合作为一个良好的交互方式。因为对于手机触屏用户非常不友好。当然,我开发的这个网站也没有考虑手机用户。当前的主流方式还是采用SPA,JS软路由切换页面来交互才是正解。

Modal元素的位置

弹出对话框的方式基本是把对话框的z-index设置的比当前页面元素高,然后使用opacity: 0.5来半透明进行遮罩。但是,需要使用Modal的内容最好放在body的下层,而不是嵌套了好多层的某个div里面,因为子元素的z-index是不可能大于父元素的,会导致在某些情况下,其他元素比当前的Modalz-index更高。

Modal滚动条问题

很多情况下,弹出的Modal内容过多导致会产生滚动条。这时如果不处理好会导致出现双重滚动条。一条是页面本身的内容过多产生的滚动条,还有一条是Modal自身的。双重滚动条还有一个问题是当你在Modal里面进行滚动的时候,页面内容本身也会被滚动,这会导致关闭Modal的时候发现页面的位置已不是打开的位置了,用户体验非常不好。这时候有两种解决方案。

第一个方案是页面本身采用position:fixed进行固定,并用JS记住滚动位置,但必须保持页面本身和Modal是并列关系。例:

    <body>
    <div class="content"></div>
    <div class="modal"></div>
    </body>

这样,Modal采用position:absolute定位,当内容过长就会自动出现滚动条。但是当关闭Modal的时候,必须把网页内容的position:fixed属性去除,并用JS滚动到当初打开Modal的位置。如果Modal是透明的,那就必须在打开Modal的时候设置网页内容的topleft属性来保持位置不变。

第二个方案是,网页内容采用overflow: hidden来隐藏滚动条。Modal采用position:fixed方案进行定位,但是这时候Modal不得不设置overflow: auto来进行滚动。 第二个方案对于网页内容和Modal的位置并没有特殊的要求,比较灵活,而且不需要JS的介入。

浏览器重绘

在一个方案中,由于网页内容被设置成了position:fixed,滚动条自然消失。所以滚动位置回到了浏览器的最上面才对。但是有时候遇到打开Modal的时候发现Modal打开之后滚动条不在最上方,这时候的原因是因为我们虽然设置了CSS进行了变更,但是浏览器没有进行重新绘制,我们可以使用会导致浏览器重绘的JS API来让浏览器更新滚动条信息,使得打开的Modal处于浏览器的最上方。

参考:

Force reflow

Github团队协作

Posted on

社畜也快半年了,说实话工作确实没有学生生活有趣。每天基本都是坐在电脑前写代码,可能我是那种更喜欢新鲜生活的人吧。对于重复的生活很容易就厌倦了,但生活由不得自己,上班的理由很简单,仅仅是因为穷。学生时代写代码基本都是一个人单干,但进入公司就开始正式的团队协作,也算是学到很多团队合作的知识了吧。特别是利用Github进行高效的合作开发。

Github协作开发

首先是切换到需要开发的分支,这里我们假设要在dev分支上进行开发。

git checkout dev

然后,在dev分支上建立属于自己的分支。命名可以以功能命名也可以用解决的issue命名。例如:dev-add-page dev-issue20,之后再切换到自己建立的分支。

    git branch dev-issue20
    git checkout dev-issue20

这样,就可以在自己分支上开发了,开发之后push到服务器上,在请求pull request进行合并操作,在pull request的时候,可以让同事来进行代码review保证开发质量。

协作要用到的Github命令很简单,之后再说说经常用的其他命令。例如,当我们写了半天发现自己在错误的分支上进行了开发,该怎么处理呢?我们可以使用git stash命令来把临时修改隐藏起来。

    git stash
    git checkout dev-issue20
    git stash pop

如果我们不小心在dev分支上直接进行了开发,可以使用上面的命令迅速把自己的修改切换到自己的分支上。

还有一种情况是,我们在自己的分支上进行开发的过程中,dev分支上合并了其他同时的代码,我们需要和dev分支保持一致。这时可以使用git pull origin dev分支来同步其他同事的代码,避免自己的代码和主分支产生冲突。

自己的分支在上传到服务器并合并之后通常服务器会删掉这个分支,但本地还是会保留。我们需要定期删掉自己本地已经合并的分支,这里推荐使用下面的命令。

git branch --merged | egrep -v "(^\*|master|dev)" | xargs git branch -d

正则表达式里面是不需要删除的分支。

有时候发现某个分支做的功能被砍掉了,然后需要删掉这个分支。可以使用下面的命令:

    git push -d origin <branch_name>
    git branch -d <branch_name>

分别删掉远程服务器和本地的分支。

有时候发现自己写的思路是错的,需要舍弃现在所有的修改,可以使用reset命令来重置。

    git reset --hard

当然这个操作比较危险,你应该慎用。

当你需要移除所有新加的文件,但这些文件还没加入库当中。你可以使用clean命令来清除所有新加文件。

    git clean -f

目前经常使用的就是这么多,如果大家有更好的学习Git命令的推荐资料,欢迎留言。谢谢!

参考:

Stack Overflow

高质量的Git中文教程

Pro Git

tagged: github

CSS技巧和工作总结

Posted on

工作确实是两点一线的生活。而且偶尔还加班,不过也算是正规的做一些东西了,个人成长来说也还不错。很多稍微有深度的知识也有所接触。于是想写点东西记录下自己的成长。以后再回顾的时候也算有所收获。

CSS层叠水平

CSS的层叠看起来很简单,可以通过z-index来进行调整,但是实际上并没有那么简单。层叠水平总共有7阶,我们实际在使用中的时候要充分考虑。

  1. 形成堆叠上下文环境的元素的背景与边框
  2. 拥有负 z-index 的子堆叠上下文元素 (负的越高越堆叠层级越低)
  3. 正常流式布局,非 inline-block,无 position 定位(static除外)的子元素
  4. 无 position 定位(static除外)的 float 浮动元素
  5. 正常流式布局, inline-block元素,无 position 定位(static除外)的子元素(包括 display:table 和 display:inline )
  6. 拥有 z-index:0 的子堆叠上下文元素
  7. 拥有正 z-index: 的子堆叠上下文元素(正的越低越堆叠层级越低)

这里有个重点概念是堆叠上下文,那么元素是如何形成堆叠上下文呢?

  1. 根元素 (HTML)
  2. z-index 值不为 "auto"的 绝对/相对定位
  3. 一个 z-index 值不为 "auto"的 flex 项目 (flex item),即:父元素 display: flex|inline-flex
  4. opacity 属性值小于 1 的元素
  5. transform 属性值不为 "none"的元素
  6. mix-blend-mode 属性值不为 "normal"的元素
  7. filter值不为“none”的元素
  8. perspective值不为“none”的元素
  9. isolation 属性被设置为 "isolate"的元素
  10. position: fixed
  11. 在 will-change 中指定了任意 CSS 属性,即便你没有直接指定这些属性的值
  12. -webkit-overflow-scrolling 属性被设置 "touch"的元素

如果符合上面规则,会形成层叠上下文。层叠上下文的背景一定是最下面的,想要通过z-index来调整浮动在其他元素上方是不可能的。

DOM Ready

如果巧妙的在DOM加载完成之后来执行JS呢,最简单的方法是把JS放在body的最下方。但这样处理有些麻烦,最简单的方法是使用setTimeout方法,设置延迟时间为0。延迟时间为0并不意味着这段代码会立即执行,而是等到所有JS加载和执行完毕,DOM也加载完毕的时候才执行。

Safari Scroll

手机版Safari在滑动的时候如果有动画执行,会导致整个页面空白。当滑动结束的时候才会渲染。我们不得不采用给所有元素添加transform: translate3d(0,0,0)属性, 来使Safari强制使用GPU加速。

CSS动画性能加速

CSS动画尽量使用transform: translate3d(0,0,0)来实现,因为ransform3d api会强制开启GPU加速提高页面的流畅度。在公司的项目中,从使用background-position进行动画效果换成transform之后,动画流畅度得到了显著的改善。

参考:

Stack Overflow

The stacking context

层叠顺序(stacking level)与堆栈上下文(stacking context)知多少?

tagged: css

JavaScript对象的常见操作

Posted on

工作也算是稳定了,不过理想和现实的差距还是很大。程序员的职责是把枯燥的工作自动化,而不是去进行重复劳动。最近在写JavaScript程序的时候,遇到了很多对象相关的操作。所以写点东西来总结下这半个月的成长。

JavaScript对象复制

JavaScript对象默认全部是拷贝引用,也就是所谓的浅拷贝。所以我们在对象操作的时候,要记住是否需要进行拷贝。一般我们使用对象的时候,都是需要对其某个属性进行修改。所以正确的写法是:

    const bar = { a: 1, b: 2, c: {d: 4}}
    const foo = {...bar, b: 3}
    foo.c.d =10
    console.log(bar.c.d)
    // { a: 1, b: 2, c: {d: 10}}

这样就同时进行拷贝和修改。注意这里使用的是JavaScript的es6语法。如果要在浏览器运行,你需要 Babel 来进行转换。注意,如果对象里面还有对象的话,这种方式也仅仅是浅拷贝。深拷贝必须遍历所有属性进行复制,在效率上有很大问题,所以我们尽量不要去用深度拷贝来解决问题。 这里对象的复制是使用的Object.assign 针对对象的简单类型可以进行复制,但是对象还是引用。如果实际的应用场景确实需要进行深度拷贝,可以使用Lodash。提供了很多常用的类库。

JavaScript数组

针对数组,现在已经不推荐用for循环来进行处理了,请使用数组的map, filterreduce来处理数组的操作。相信这三个方法足以满足需求。

JavaScript开发规范

现在JavaScript已经是es6的时代,所以我们也需要顺应时代学习新知识。这里我建议大家读读airbnb的JavaScript Style Guide。这里不仅教会你正确的编码格式,更多的是优秀的写法。如何合理的拷贝对象,遍历数组等等。

目前就写到这里,工作之后并没有多少成长。写博客都发呆了很久该写什么。。。

CSS笔记和杂谈

Posted on

这应该是最长的一次博客断更吧。。。因为年后辞职加上来日本工作的原因。导致很长时间都没有更新自己的博客了。很是惭愧,本来定的目标就是每月一篇很简单的任务。但居然空档了这么长时间,这就是正所谓的生于忧患死于安乐吧。找到了工作就松懈了,所以今天又重新捡起来继续写。公司的新人课题是做一个简单的CMS网站,也算重新复习了一下网页制作吧,因为一直再写JS的项目,基本的HTML和CSS的设计都有点生疏了。。。

CSS一行多列设计

这是一个最基本的设计布局。在一行上有多列的内容,最简单的例子是导航栏。HTML的大致结构是这样的:

    <ul>
    <li>HTML/CSS</li>
    <li>JavaScript</li>
    <li>CMS</li>
    </ul>

默认的话,这三列内容是向下排列的,如何让它们横着排列呢?这时候有三种解决方案:第一,是采用float使得所有的li列表浮动表示在一行。但这时候记得使用clear去清除浮动属性。这种设计方案的好处是兼容性强,但新手容易处理不好float属性对布局的影响和忘记清除float属性。第二种解决方案是对li使用display:inline-block;使得li并排一行,这种方案的好处是容易理解和兼容性高。但如果li之间的HTML源代码有回车的话会导致相邻的li有很细微的间距。所以对像素级别的设计方案来说并不推荐。第三种方案是使用flex布局。这是目前来说最灵活的一种布局方案了,特别对于多屏幕自适应来说非常友好。但是对浏览器版本要求比较高,IE11以下都不支持。所以这个简单的布局方案要根据自己的业务情况进行具体选择,并没有银弹。

子DIV溢出方案

有些时候根据需求我们设计的子DIV要比父DIV超出一定范围,这时候要怎么设计才好呢?有时候会想到利用position: relative;来改变子DIV的文档流进行实现。但这种方式实在是不推荐,因为会影响父DIV在页面的布局。我建议的解决方案是使用margin来进行溢出,margin不仅可以输入正数,也可以使用负数。当使用负数的时候就会朝反方向扩展达到溢出的目的。

多行文字垂直居中

实际开发中,我们经常会遇到这种常见的需求,对于一行文字来说,很简单,我们只要把heightline-height设置成一样的高度就可以了。但是对于多行文字的话,我们还是需要一些技巧的。首先,我们需要在文字外面再套一层元素进行设置。HTML结构大致是这样:

    <div>
        <span>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</span>
      </div>

然后我们在div上设置heightline-height一样高,使得span元素保持垂直居中。然后再修改span元素的CSS属性,设置正常的line-height和display: inline-block;以及vertical-align: middle;

参考

remove-whitespace-inline-block

display

Vertical align multiple lines of text

tagged: css

Golang语言学习分享

Posted on

Golang语言是谷歌开发的编程语言,虽然运行效率比不上C语言那么高效,但是也算比C语言更容易开发,因为内置了很多数据结构和借鉴了其它语言的优点,容易学习和开发。在实习的阶段也使用Go语言开发了一个简单的项目。所以想把学习的一些经验记录下来。

Golang的安装

我推荐使用Linux系统来学习和使用Golang语言,各种发行版的包管理器应该默认都会有Golang的安装包。不过还是要说下如何手动安装,去官网下载Golang的安装包,至少要1.7版本以上。然后解压缩和配置环境变量。 过程很简单。

Golang入门

首先,请大家阅读官方的这篇文档How to Write Go Code。这篇文章介绍了如何写一个基本的Golang程序和类,这里要说的是GOPATH,使用Golang语言必须要设置GOPATH环境变量,可以通过使用go env来查询Golang相关的环境变量。GOPATH下一般有三个文件夹bin,src,pkg。src下存放我们写的Golang项目,bin下是编译好的二进制文件,pkg文件夹下是生成的库文件。 我们安装的第三方库也会存放在src文件夹下,记住 Golang 引用的包名的根文件夹是GOPATH的src文件夹,所以我们一般都要使用绝对路径来引用包避免出现错误。

还有要注意的一点是,需要编译的Golang项目必须放在GOPATH下的src文件夹下,否则编译的时候无法正确的找到相关的依赖包。Golang还有一个好处就是可以直接以二进制文件的方式运行,只依赖系统的glibc。所以部署的时候特别方便,不像其他语言一样必须安装相应的解释器。

Golang依赖问题

开发程序肯定会使用各种第三方依赖,目前Golang语言的各种库都是存放在github上,国内下载很不方便,所以要让每个开发者都去下载一份依赖确实很不友好。还好Golang在1.7版本以上直接vendor。如果项目里有vendor文件夹,Golang会首先读取这个文件夹作为依赖来使用。使用Golang vendor的方法很简单:

    go get -u -v github.com/kardianos/govendor
    govendor init
    govendor add +external

这三个步骤分别是安装,初始化,和添加依赖。这样就在项目本身里面添加了依赖,协同开发的时候大家也没必要再去更新一份,非常方便。

Golang性能分析

在开发高性能多并发程序的时候,对系统的实时响应要求很苛刻,这就需要使用专业的分析工具来进行代码分析,确定出耗时代码进行相应的优化。好在Golang本身提供了这个工具pprof,这个工具可以列出具体每个函数的耗时,还能进一步跟踪函数里面的具体细节,定位耗时代码。

Golang序列化

在开发程序的过程中,为了操作方便,我们会定义各种各样的结构体来表示数据。很多情况下,结构体需要进行网络通讯,这时候我们就不得不序列化结构体进行网络发送。就像前端开发中把JSON对象转换成字符串发送出去一样。但是序列化却是一个很耗时的过程,处理不当就会成为系统的瓶颈所在。所以我列举出了几种序列化方案,推荐大家使用。我最后选择的是gencode

好用的Golang项目推荐

Github有很多不错的Golang项目可以学习借鉴,例如Go Git Service,就是一个非常好用的代码托管项目。

参考

pprof

Golang Serializer Benchmark Comparison

awesome-go

tagged: Golang

逆向JavaScript经验分享

Posted on

最近心血来潮,想看个国漫。结果爱奇艺独播,还每集那么老长的广告,可是把我恶心的不要不要的。然后为了展现程序员的自我修养,我要分析下这个视频的源文件地址,直接观看,跳过广告!凭着我分析百度网盘的经验,花了三个晚上,总算搞定了。 顺便分享下整个逆向分析过程,和大家学习。

查看请求

前端无论怎么样的操作,本质上都是向服务器发送请求,然后解析请求数据,展现在页面上。所以说逆向的切入点就是查看网络请求!因为我要分析视频的源MP4文件而不是flash文件,所以我把浏览器模拟成手机让爱奇艺载入手机版界面以便抓包获取MP4文件。切换成手机版很简单,只需要在Chrome上按F12打开开发人员工具,点击左上角的 Toggle device toolbar图标就可以切换成手机设备了,我选择模拟的设备是Nexus 6P。这时候重新刷新页面,查看NetWork面板。里面会有这个页面载入发起的所有请求,我们重点找的是各种JS发起的请求和带参数的请求。最笨的办法就是一个请求一个请求看一遍,总有你需要的。

分析请求

通过一个个查找,我们找到了一个请求返回了一个JSONP格式的数据,这个数据里面包含了MP4源文件的地址。注意,很多人分析请求的时候经常喜欢找MP4文件的请求,这个可以算是一个切入点。但是一般网站不会把视频网址固定死的,所以找到MP4请求并没有什么实际意义,而是一定有一个请求去向服务器索取真实的MP4地址,一般这个数据都是JSON格式的。所以只要耐心找,一定找得到的。很多请求都只不过是资源文件,所以很容易过滤掉的。

接下来就请看我们找到的这个关键请求: Request 从这个请求的Response可以得知这是我们需要的请求,但这也只不过才是开始。我们知道了这个请求,就需要思考如何构造这个请求,来获取真实的MP4地址。一般请求的网址和路径都是固定的,所以我们的重点工作就是构造请求参数。 这个请求总共有17个请求参数,真是够多的。但是我们不知道所有请求参数是否是必须的。有可能服务器只校验其中的几个参数,所以我们没有必要对每个参数都分析一遍。

这时候就需要拷贝下来这些参数来进行分析,在界面的左侧面板,右键单击之后会出现 copy as cURL菜单。这样我们就可以在Linux的终端里调试这个请求,你可以先拷贝到记事本上,然后修改请求之后再终端里跑一遍看看能否返回正常的数据。有些服务器也会对Request Headers进行校验,所以可以试试去掉相关的headers例如cookie,UA。最终调试出可以得到合法数据的最简单请求。爱奇艺这个例子对Request Headers没有进行任何校验,反而对请求参数很苛刻,少一个都不行,最后我们会解释为什么爱奇艺对请求参数检查这么苛刻。

逆向JS文件找到相关请求如何构造

从上面我们知道了,这个请求只对参数进行了校验,Request Headers并不需要。那重点就是逆向JS找到如何构造参数即可。

通过Sources面板可以看到这个页面加载的所有JS,但是这个看起来也太多了吧。所以这个不是方法,重点就是查找关键的JS文件。Ctrl+Shift+F可以查找所有的文件,所以我们可以利用搜索大法来找到关键的JS文件。一般也可以通过Network查看请求的触发者,但是很多封装之后的JS,是无法通过请求追溯到构造的JS文件的。但是无论这个JS文件怎么压缩混淆,网址是必须要有的。所以我们搜索cache.m.iqiyi.com/jp/tmts就找到了关键的JS文件了!找到JS文件之后,会跳转到Sources面板,点击右下角的{}Pretty print会把压缩的JS自动进行换行和排版,方便分析。这时候通过分析这个JS文件,我们就知道如何构造这个请求了。

站在巨人的肩膀上

我们没有必要每个参数都自己用JS实现一遍,因为人家已经实现好了,我们只要调用就好。这个请求的关键是getTmtsVf函数。这个函数对请求进行了哈希,添加了一个vf参数,所以我对参数进行了任何的修改都会导致请求非法。无论怎么实现的JS,有些参数只能通过全局变量来共享,所以对于那些参数,我们可以直接使用。例如爱奇艺有个全局的Q变量,通过这个变量可以获得很多需要的参数。

终极解决方案

对参数的构造完全取决于最后个人对JS文件的理解的功底,能看懂压缩的JS的逻辑才能成功提取需要的请求参数。但也有些变态的JS文件很难理解,不进行动态调试分析的话很难明白。这时候我们就不得不请出Chrome的大杀器:webRequest,通过这个API,我们可以拦截请求并进行替换。我就使用这个API拦截了app_movie.js,注入了我带调试信息的JS。你就可以随意的调试了。关于这个API的使用DEMO,可能后续会放在我的Github上。

写在最后

其实可以分析桌面版本的flash视频,然后使用B站的这个flv.js来解决播放问题。最终我写了一个油猴脚本来一键输出MP4文件。有兴趣的可以私信我。

tagged: JavaScript

Linux高并发系统参数优化

Posted on

终于毕业证书到手,学生生涯也算是划上完美的句号了。不过工作强度还真的有点大,还经常加班。感觉自己懒散了许多,人果然是生于忧患,死于安乐。。。最近的项目里对业务处理的高并发要求比较高,因此也学习了相关的如何调整Linux内核参数让服务器可以处理非常高并发的业务。所以在博客上记录下相关的经验,以供参考。 修改的参数主要是Linux系统参数和网络协议栈参数。注意,以下命令都需要在root环境下运行。

Linux操作系统参数

系统全局允许分配的最大文件句柄数:

    # sysctl -w fs.file-max=2097152
    # sysctl -w fs.nr_open=2097152

允许当前会话/进程打开文件句柄数:

    # ulimit -n 1048576

但是以上命令只是临时修改,重启系统便会失效,所以我们需要持久化这些参数。

持久化’fs.file-max’设置到/etc/sysctl.conf文件:

    fs.file-max = 1048576

/etc/security/limits.conf持久化设置允许用户/进程打开文件句柄数:

    *      soft   nofile      1048576
    *      hard   nofile      1048576

TCP协议栈网络参数

并发连接backlog(定义了网络连接的队列的最大长度)设置:

    # sysctl -w net.core.somaxconn=32768
    # sysctl -w net.ipv4.tcp_max_syn_backlog=16384
    # sysctl -w net.core.netdev_max_backlog=16384

可用知名端口范围:

    # sysctl -w net.ipv4.ip_local_port_range='1000 65535'

TCP Socket读写Buffer设置:

    # sysctl -w net.core.rmem_default=262144
    # sysctl -w net.core.wmem_default=262144
    # sysctl -w net.core.rmem_max=16777216
    # sysctl -w net.core.wmem_max=16777216
    # sysctl -w net.core.optmem_max=16777216

    # sysctl -w net.ipv4.tcp_rmem='1024 4096 16777216'
    # sysctl -w net.ipv4.tcp_wmem='1024 4096 16777216'

TCP连接追踪设置:

    # sysctl -w net.nf_conntrack_max=1000000
    # sysctl -w net.netfilter.nf_conntrack_max=1000000
    # sysctl -w net.netfilter.nf_conntrack_tcp_timeout_time_wait=30

TIME-WAIT Socket最大数量、回收与重用设置:

    # net.ipv4.tcp_max_tw_buckets=1048576

FIN-WAIT-2 Socket超时设置:

    # net.ipv4.tcp_fin_timeout = 15

参考

测试调优

tagged: Linux

使用Latex书写论文

Posted on

最近忙着写论文,博客都快要长草了。。。但是我还是要坚持写下去!因为早就知道写论文排版会很麻烦,所以就选择了使用Latex来写论文。看到后期同学们在Word上一句一句的调整格式,我只能说学会了Latex真是磨刀不误砍柴工,后期真是太省心了,接下来就向大家介绍下如何使用Latex写论文,免除排版之忧。

Latex安装

推荐使用Tex Live来编写论文,从国内中科大的镜像源来下载每个平台对应的安装文件,Windows平台下载exe程序,Linux的各大发行版建议从官方镜像源下载。安装过程记住要首先选择国内的镜像源,根据网速来决定安装进度,基本上要安装很久,请耐心等待。

编辑器选择

Latex的主要功能是把tex文件编译成pdf文件,所以自带的编辑功能很难用。在此我推荐使用Sublime Text 2来编写论文,可以一键编译非常方便。安装好Sublime Text 2之后,我们还要安装相应的插件来配合Latex的使用,插件的启用请参考我之前的博文。在这里我们选择使用LaTeXTools来编译论文,虽然Github上有详细的配置说明,我还是简要介绍下,主要配置的就是Latex所对应的环境变量texpath。还可以配置默认的PDF阅读器进行反向搜索,Win下使用SumatraPDF,Linux使用Evince。配置完毕之后我们只需要在当前tex页面按下Ctrl+B 就可以一键编译成pdf文件。

Latex格式学习

网上有很多Latex的学习资料,不过我们也没必要详细学习,在此介绍一个简单的学习资料。一份不太简短的LATEX介绍基本上看完这个文档就可以大致明白如何利用Latex书写论文。

Latex模板

当然,我们也没必要一定要系统的学习,如果别人有做好现成的模板,我们直接套用就好,非常方便,中科大也有热心的小伙伴制作了模板。地址是USTC Thesis。近期抽时间的话,我会制作一个软件学院专用的论文模板。

相关学习资料

如何使用LaTeX排版论文

配置LaTEXTools

tagged: Latex

模块化JavaScript开发

Posted on

前端开发的节奏真是日新月异。。。短短几年竟然发展如此迅速,JavaScript的各种框架库可以说比其他语言的总和还多。Google家的AngularJS,和Facebook的React各领风骚。自己写的前端项目也越来越复杂, 单纯的面向过程开发和单文件已经不能满足现有的需求。虽然ES6早就引入模块化特性,但是目前的浏览器还没有实现。还需要借助其它前端工具进行编译。。。下面我就顺便介绍下Web开发的历史。。。

Server Rendered

以前开发Web项目,全部是服务器来渲染页面,从数据库读入数据,浏览器只负责解释页面,这种情况下不考虑交互性,其实完全不用写任何Js代码。但是缺点也很大,处理全部交给服务器,服务器压力大。而且每次都是刷新加载,所以用户体验也不是很好。

Server-Rendered + AJAX

后来使用AJAX无刷新技术,对于简单的登录交互我们没有必要刷新页面,而是直接使用Js发送认证数据进行交互和登录。一定程度上减少了服务器的压力。

Single Page App

后来出现了谷歌的AngularJS这样的富客户端Js框架,做到了真正的页面和数据分离。全部使用JavaScript进行页面的渲染和交互,只向服务器请求必要的数据,并使用JSON的数据格式来传输。这样不仅有利于减少服务器压力,而且还可以复用到各种平台。但是JavaScript代码变得越来越多和复杂,必须考虑合适的架构和模块化开发才有利于之后的维护。

Webpack模块化工具

webpack模块化工具可以把你写的符合模块化规范的代码进行打包和整合到一个单子文件,这样就可以模块化自己的项目。但是官方文档实在是很难学习,自己琢磨了一段时间才算稍微能上手。并且还遇到点小问题,在此记录下,我们可以使用命令$ npm install webpack -g 安装webpack.但是我们使用的时候经常还是遇到找不到webpack的问题,是因为缺少环境变量NODE_PATH 可以通过设置export NODE_PATH="/usr/lib/node_modules来解决。

相关学习资料

webpack指南

webpack-howto

tagged: JavaScript

WebGL渲染的知识介绍

Posted on

来到日本导师的研究方向是计算机图形学,由于我比较擅长Web开发,便选择了WebGL进行图形渲染的研究课题。学习了大半年,也该写点东西介绍下自己对WebGL的理解,如有错误,欢迎批评指正。

WebGL介绍

WebGL是基于OpenGL ES 2.0文档进行开发的,所以想了解相关API的话,直接查阅官方的OpenGL ES 2.0文档即可.WebGL的一大优势是可以使用GPU资源来进行图形渲染和相关的计算,但是使用GPU资源的话必须使用对于GPU友好的语言来进行编写,WebGL使用GLSL来编写用于GPU渲染的着色器。 JavaScript主要负责整个渲染流程的控制和GPU与CPU之间的数据交互,繁重的坐标变换计算全部交给GPU来处理。

WebGL渲染管道

获取WebGL

WebGL是基于canvas画布来进行渲染的,首先我们要使用getContext方法来获取gl对象。并判断浏览器是否支持WebGL.

    var gl = null;
    try{
        gl = canvas.getContext('experimental-webgl');
        if(gl == null){
            gl = canvas.getContext('webgl');
        }
    }
    catch(error){}

    if(gl != null){
        // WebGL is supported 
    }
    else{
        // WebGL is not supported
    }

顶点着色器

WebGL只支持绘制基本图元,复杂的图形也是有基本图元组合而成的,基本图元包括点、线段或三角形。三角形是最经常用来使用的,因为3D空间中的任何3个点都是一个三角形的顶点。顶点着色器一般用写在HTML中,并标上HTML无法识别的type,这样浏览器就不会解释执行。 顶点的X,Y,Z轴的取值范围全部是-1到1之间,所以要注意变换坐标。

    <script id="vs" type="x-shader/x-vertex">  
        attribute vec3 position;  
        uniform   mat4 mvpMatrix;

        void main(void){  
            gl_Position = mvpMatrix * vec4(position, 1.0);  
        }  
    </script>

前面的mvpMatrix矩阵是坐标变换用的,因为我们在实际渲染可能要应用到很多的矩阵变换以渲染到屏幕上。这部分知识可以看看基本的图形学相关书籍,我看的是《DIRECTX.9.0.3D游戏开发编程基础》,里面介绍了相关的矩阵概念和数学知识。顶点着色器负责绘制图元的顶点信息,接下来就要使用片段着色器绘制相关的颜色信息。

片段着色器

片段着色器接收到顶点着色器的顶点信息并在此位置绘制相应的颜色值。颜色的取值范围是0到1.

    <script id="fs" type="x-shader/x-fragment">  
        void main(void){  
            gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);  
        }  
    </script>

使用片段着色器可以给相应的顶点设置颜色值,在顶点与顶点之间的位置使用插值法设置颜色值。

渲染管道可以参考这张比较形象的流程图:

WebGl Pipeline

texture

GPU运算里面有个非常常用的数据结构,那就是texture,既可以存储图片数据,也可以存储frame数据。对于离屏渲染非常重要,不过在GPU运算里面非常重要的一点是一个变量不能既作为输入有作为输出。 在CPU运算里面我们经常写: a=a+1 但是在GPU运算里面是不能这样操作的,需要有一个临时变量存储输出的值,然后再传入到最终变量中。类似于:b=a+1;a=b这样。所以对于动画渲染,我们需要使用两个framebuffer 相互交换进行渲染。

离屏渲染

对于复杂的图形渲染,可能一次描绘无法完成最终效果,这时候我们可以把渲染的中间效果存储到framebuffer里面,再下一次渲染的时候继续使用,只需要简单的在渲染之前绑定framebuffer即可。

    gl.bindFramebuffer(gl.FRAMEBUFFER, Cube.FboHandle);

相关学习资料

WebGL MDN

WebGL 開発支援サイト

WebGL入门

tagged: WebGL

关于JavaScript跨域请求的相关知识

Posted on

2016年已经过了3个月了,还没来得及更新博客...实在是惭愧惭愧...最近确实也很忙,一边要打工,一边要写论文,一边还要找工作...

最近百度的导出插件出了点小问题,导致很多人无法正常和Aria2c通讯.经过仔细的分析,发现问题出在跨域请求上,接下来就要详细说说JavaScript的跨域请求.

JavaScript跨域请求

跨域请求指得是发起请求的资源所在域不同于该请求所指向资源所在的域的 HTTP 请求.最常见的就是跨域载入图片,我们可以看到很多网站主站和网站所使用的图片 是不同的域名的.这样做的好处是请求图片的时候不会发送主站的Cookies,因为不同域名嘛.而且还可以减少主站的服务器压力.但载入图片使用的是GET方法,比较简单.

跨域POST请求

我们经常使用POST请求发送各种指令数据,因为POST发送的数据没有长度限制.我们还可以在发送数据之前修改请求头,发送各种自定义的 HTTP Request Headers.但是 在跨域请求的时候POST的请求头被严格限制,被允许设置的请求头只有:

    - Accept
    - Accept-Language
    - Content-Language
    - Content-Type

并且允许的 Content-Type 只有一下三种:

    - application/x-www-form-urlencoded
    - multipart/form-data
    - text/plain

Preflighted requests

当我们发送的请求不满足上面的条件时,就必须发送预请求给目的站点,来查明这个跨站请求对于目的站点是不是安全可接受的。以下条件会触发预请求的发送:

    - 请求以 GET, HEAD 或者 POST 以外的方法发起请求。或者,使用 POST,但请求数据为 application/x-www-form-urlencoded, multipart/form-data 或者 text/plain 以外的数据类型。比如说,用 POST 发送数据类型为 application/xml 或者 text/xml 的 XML 数据的请求。
    - 使用自定义请求头(例如 Aria2c的验证使用的 Authorization)

结论

本次BUG的出现,是由于代码验证不规范,对于不需要用户名和密码验证的RPC地址.仍然发送自定义的 Authorization 验证头.导致触发OPTIONS请求,但是本身不支持验证的Aria2c客户端无法识别OPTIONS请求,只能返回500错误.导致通讯失败,至此 BUG分析完毕.

参考:

HTTP access control (CORS)

HTTP/1.1: Method Definitions

tagged: JavaScript

使用最小费用最大流模型解决酒店管理收益最大化问题

Posted on

最近老师布置个作业,很没有头绪,于是各种咨询,大家的解决方案是使用最小费用最大流模型来求解,于是我便花了3天时间来理解这个算法.

问题描述

问题很简单,你有一个只有5个房间的酒店,然后本周会有18个订单到来,怎样接受订单可以使本周的收益达到最大值.也就是把这18个订单填写到一个 5*7的表格里 怎么填收益最大,订单要么接受要么拒绝,也可以说是典型的01背包问题吧,但用动态规划不好抽象出来状态转移方程,所以我还是选择用最大流来做.每个订单包含3个信息:每晚付的房费,从周几开始入住,入住几天.

抽象数据模型

因为题目要求是球最大收益,而流模型是最小费用,所以我们把收益取相反数即可.关键是建立容量和权值,因为房间的数量是5,所以限制源点和汇点的容量是5,权值是0.然后中间设置8个点,产生7条边,每条边代表每天的流,权值为0,容量为无穷大.这里我们不能简单的连接点来设置订单的边,因为有相同的起始日期和结束日期的订单.这样会造成冲突,如何解决冲突呢? 我们可以在订单的边上再加一个顶点来进行区分.模型图如下所示: model 顶点11和顶点16是重复边的订单,对应的是第一个订单和第6个订单.源码和输入数据可以在文章末尾看到.

查找增广路径

这里我们使用Ford–Fulkerson算法来计算每次的增广路径.

路径的查找

每次增广路径的查找都需要得到最小权值的路径.查找路径使用的是SPFA算法.

第一次查找增广路径

model

第一次查找增广路径

model

以此类推求出最后的结果~

参考:

Source code

Ford–Fulkerson

tagged: algorithm

解决Dota2在Archlinux下的各种疑难杂症

Posted on

V社的steam平台在Linux下出了这么多年还是没解决输入法的问题.本身官方也就只支持Ubuntu,所以在其它发行版也就难免会有各种各样的问题.容我慢慢道来

Alt键无法发信号

Dota2只能通过Alt和鼠标左键的配合发信号.无法像Dota一样可以单纯使用鼠标发信号.但是有些用户可能发现自己无法发信号. 原因在于你使用的窗口管理器已经为 Alt和鼠标左键绑定了事件,所以你得取消窗口管理器的事件绑定,例如openbox需要修改配置文件rc.xml找到鼠标事件并注释掉事件绑定的内容.

双显卡启动游戏

对于很多双显卡的机器,平时运行使用集显但是在玩游戏的时候肯定要使用独显才能充分享受游戏的乐趣.所以启动游戏的时候就必须设置启动参数才行. 启动参数设置为vblank_mode=0 primusrun %command% 前提是你要装上 primusrun.

Dota2没声音

如果你只使用的ALSA没使用PulseAudio可能会遇到这个问题,原因是因为Steam自己的库文件和系统的冲突.只要把Steam的相关ALSA库删掉即可. 删掉下面的文件夹 和 libasound.so.*即可搞定.

    ~/.steam/steam/ubuntu12_32/steam-runtime/i386/usr/lib/i386-linux-gnu/
    ~/.steam/steam/ubuntu12_32/steam-runtime/amd64/usr/lib/x86_64-linux-gnu/

参考:

Steam

Dota 2

tagged: linux

Archlinux和Win 10 在 UEFI下 双启动

Posted on

实验室给配备了一台高性能的台式机,但是UEFI本身关不掉.而且有些操作还是离不开Windows,于是便折腾Win10和Archlinux的双启动. 安装双系统基本上都是先装Windows再装Linux的.切忌这一点,安装顺序反了会很麻烦.

Win10配置

要安装Archlinux,首先得对Windows进行相应的配置才行.Windows的安装就不用我介绍了,大家基本都会的.

UEFI Secure Boot

首先要进入BIOS把这个参数关掉,如果BIOS里面有快速启动之类的也关掉.

Fast Start-Up

把Win10的这个加速系统开机的配置禁用掉,应该在电源管理界面可以禁用的.因为切换系统的话这个功能会导致你丢失数据.

制作Arch的U盘安装盘

如果你简单的使用dd命令就可以成功安装那是最好不过的了,但是有时候事与愿违...

假设你用dd刻录好之后,然后用电脑U盘启动,出现启动菜单 选择 Arch Linux archiso x86_64 UEFI CD 之后遇到黑屏的问题.

你可以先试试禁用KMS

如果这个不行的话,那说明你得采用第二个办法了,使用 GRUB 进行引导启动盘.

手动制作启动盘

因为要使用GRUB进行引导,所以不能使用dd刻录,dd刻录的U盘有写保护,我们无法修改.我们得手动制作安装盘

    # mkdir -p /mnt/{iso,usb}
    # mount -o loop archlinux-2015.10.01-dual.iso /mnt/iso
    # mount /dev/sdXn /mnt/usb
    # cp -a /mnt/iso/* /mnt/usb
    # sync
    # umount /mnt/iso

光是拷贝还不行,我们还得有引导信息. 使用syslinux进行引导

    # cp -r /usr/lib/syslinux/bios/*.c32 /mnt/usb/arch/boot/syslinux/       ## copy ALL the *.c32 files from /usr/lib/syslinux/bios/, DO NOT SYMLINK
    # extlinux --install /mnt/usb/arch/boot/syslinux/

如果你的U盘是MBR格式的,记得使用 fdisk把当前分区设置为活动分区. fdisk /dev/sdX 然后输入 a 命令,选择要设置的活动分区.

还有 U盘寻找启动分区是根据U盘的label标签来寻找的,确保你的卷标是 ARCH_2015XX 格式. 如果你不想用卷标的话,还可以用UUID. 查找你U盘的UUID的命令是 sudo blkid.

制作GRUB standalone

这才只是手动制作好了启动U盘.你还要切换成 GRUB引导.通过下面的命令制作出一个带有所有模块的GRUB引导文件.

    grub-mkstandalone -d /usr/lib/grub/x86_64-efi/ -O x86_64-efi --modules="part_gpt part_msdos" --fonts="unicode" --locales="en@quot" --themes="" -o "/tmp/grubx64_standalone.efi"

生成的grubx64_standalone.efi就是我们需要的文件.

然后回到我们的那个启动U盘.备份EFI/boot/loader.efi 成 EFI/boot/gummiboot.efi.

拷贝两份grubx64_standalone.efi. 分别命名为EFI/boot/loader.efi和EFI/boot/bootx64.efi.

然后新建一个 EFI/boot/grub.cfg 把 wiki上的内容拷贝过去.记得替换掉ARCH_YYYYMM的值.

然后这样就应该用 grub启动了. 如果你这样还是启动失败了, 得到了类似 invalid magic number的错误.那只能说你跟我一样倒霉.

用GRUB挂载ISO进行引导

既然我们的GRUB是好的,那我们就用GRUB直接引导一个完整的ISO文件. 参考我之前发布的文章使用GRUB2引导ISO镜像.

在原有的grub.cfg上添加一个menuentry.并把完整的ISO镜像拷贝到U盘里面.记得替换 UUID和文件名.

    menuentry "Archlinux-x86_64.iso" --class iso {
      set isofile="/archlinux-2013.04.01-dual.iso"
      search -s -f -n /archlinux-2013.04.01-dual.iso
      probe --set=DB7B-2C3D -u $root
      loopback loop /archlinux-2013.04.01-dual.iso
      linux (loop)/arch/boot/x86_64/vmlinuz archisolabel=ARCH_201304 img_dev=/dev/disk/by-uuid/DB7B-2C3D  img_loop=$isofile earlymodules=loop
      initrd (loop)/arch/boot/x86_64/archiso.img
    }

这次总算可以引导出arch的安装环境了.接下来就是按照正常的步骤进行安装了. 在Win下直接使用Rufus刻录就没那么折腾了.

安装Archlinux之后配置双系统的GRUB

到最后我们要安装GRUB到UEFI上.如果你提前安装过Windows,那么你肯定有EFI分区的.

    # pacman -S dosfstools grub efibootmgr
    # mkdir /boot/efi
    # mount /dev/sda1 /boot/efi

把EFI分区挂载到系统上,然后安装GRUB.

    # grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=grub --recheck
    # grub-mkconfig -o /boot/grub/grub.cfg

生成的grub.cfg里面并没有Windows的启动菜单,我们需要手动写.

    menuentry "Microsoft Windows 10" {
        insmod part_gpt
        insmod fat
        insmod search_fs_uuid
        insmod chain
        search --fs-uuid --set=root $hints_string $fs_uuid
        chainloader /EFI/Microsoft/Boot/bootmgfw.efi
    }

$hints_string的值是命令# grub-probe --target=hints_string /boot/efi/EFI/Microsoft/Boot/bootmgfw.efi的输出.

$fs_uuid的值是命令# grub-probe --target=fs_uuid /boot/efi/EFI/Microsoft/Boot/bootmgfw.efi的输出.

替换这俩值,保存并退出即可.到此双系统配置完毕.最后别忘了进BIOS把第一启动项目设置为GRUB.

参考

Preinstalled Windows 8.1 and Arch Linux dual boot

Unified Extensible Firmware Interface

USB flash installation media

Syslinux

GRUB

tagged: linux

使用Python实现Histogram_equalization算法

Posted on

最近刚来日本忙着生活上的事情,对技术上的研究也有点松懈了.在日本的研究课题是计算机图形学.所以就选修了相关的课程,最基本 的当然是图形处理,老师的第一个任务就是实现Histogram equalization算法.中文名字就是直方图均衡化.

概念介绍

这里首先讨论的是灰阶的图像,也就是平常大家所有的黑白照片.然后每个颜色都有具体的数值.纯黑色是0,纯白色是255. 当然这是对于8 Bit的图来说的. 色差的范围只有0-255.就拿这个作为例子来讲解吧~ 然后就要讲解如何从数值上来分析一张图片是拍的好呢还是不好呢,如果是一张好照片,那这张照片必然是 对比度鲜明,该亮的地方亮,该暗的地方暗.也就是大家所说的曝光合适.然后我们从照片的二进制数据中去读取每个像素的数值,然后画出直方图.发现每个数值的 像素分布比较均匀,就说明这是一张好照片.但是现实生活中,我们可能会拍到一些曝光不足导致整体照片很暗或者曝光过了导致照片过亮的情况.在不能重拍的情况 下只能使用算法来进行优化了,本文就是来说明这个算法的实现的.

算法大致概念

算法的本质就是提取每个像素的数值,然后经过计算把每个像素进行替换.每个像素都有经过计算之后的新的数值.新的数值可以使照片整体上的数值分布更均匀. 给人的整体感觉是照片对比度变得很好了.具体的算法解释看wiki 我解释的比较烂. Histogram_equalization

算法实现

根据wiki的例子,我用Py实现了这个算法,效果还算不错吧.尽可能的少引用类库,不过opencv已经有现成实现好的API了.但是只支持Py2,而我用的Py3. 库的引用只使用了PILnumpy.前者用来读取图像的二进制数据, 后来用来进行数值的累加计算.源码发布在 Github

计算机图形学对我来说完全是未知的领域,希望有经验的同学可以不吝指正.

tagged: python

Chrome扩展和Web Page 进行通讯

Posted on

由于WEB页面本身受到浏览器的安全策略限制,而且 Content Scripts和页面本身并不共享环境变量.我们很多时候都是 通过DOM去注入JS代码到当前页面上.因为同源策略的原因不得不把有些操作发回到Background进行处理,因为Background 没有任何限制,可以拥有完整的Chrome的API权限.

通过Content Scripts中转

第一种通讯方式是通过Web API postMessage 发送消息,然后在 Content Scripts 监听 message 事件捕捉消息发送回Background 这种方式适合单向通讯,只从 web page发送数据到Background 或者 Background 发送给 web page 如果双向通讯的话就很不方便了 因为 Content Scripts和 web page 共享window会造成事件重复绑定的.

使用Chrome API

首先在manifest.json里声明权限

    "externally_connectable": {
      "matches": ["*://*.example.com/*"]
    }

声明要通讯的网页的网址,然后和 Content Script 一样建立通讯

    var port = chrome.runtime.connect(chrome_id,{name: "abc"});
    port.postMessage({
        method:"rpc_data",
        data: parameter
    });
    port.onMessage.addListener(function(response) {
        SetMessage(response[0],response[1]);
    });

关键是Background监听消息的接收,必须使用 chrome.runtime.onConnectExternal.addListener 而不是 chrome.runtime.onConnect.addListener 后者是接收 Content Script消息的,前者是 接收 Web Page消息的

官方文档

这样便实现了Web Page 和 Background之间的双向通讯, Web Page不能完成的事情交给Background来处理即可.

tagged: chrome

解压和打包安卓DAT文件

Posted on

安卓5.0的卡刷包系统基本上都是在一个 dat文件里面的。但是对于很多国内ROM,要不就是植入了很多流氓软件, 要不就是有很多无用软件。我们可以通过解压DAT文件并进行修改然后再封装回去就完美啦~

首先把DAT解压成IMG镜像

我们要使用sdat2img工具把 DAT文件转换成IMG镜像

    ./sdat2img <transfer_list> <system_new_file> <system_ext4>
    - <transfer_list> = input, system.transfer.list from rom zip
    - <system_new_file> = input, system.new.dat from rom zip
    - <system_ext4> = output ext4 raw image file

一个很简单的例子:

    ./sdat2img system.transfer.list system.new.dat system.img

新生成的IMG镜像便是我们下一步需要的.

挂载IMG镜像

    sudo mount -t ext4 -o loop system.img /mnt/android

这时候你可以在系统的 /mnt/android目录下看到文件的具体内容然后进行修改了.

打包回IMG镜像

把修改好的内容打包回去需要使用make_ext4工具

    ./make_ext4fs -T 0 -S file_contexts -l 1073741824 -a system system_new.img /mnt/android

file_contexts文件是ROM本身提供的.后面的一串数字指的是打包的IMG文件的大小 1G

封装成DAT文件

封装仍需要使用工具 使用 rimg2sdat

    ./rimg2sdat <system_img>

这样就打包好了新的DAT文件,放入刷机ROM.刷机即可~ 以上操作最好在Linux环境下,Win下不保证成功.

参考 XDA

tagged: notes

Chromebook Pixel (2013) 安装Linux的方法

Posted on

同学最近淘了一台二手Chromebook Pixel (2013),这款笔记本最大的诱惑就是 12.85寸但是配备了一块2560 x 1700 分辨率的触摸屏。上网的体验不要太爽啊,在体验了一小时的Chrome OS之后,便感叹就只有一个浏览器而已啊!于是开始折腾如何安装Linux,毕竟这款笔记本 本来就对Linux支持很好。下面开始介绍安装过程。

由于这款笔记本是x86架构还自带SeaBIOS,所以在安装Linux上省了不少功夫,很多非SeaBIOS的Chromebook都是需要刷固件才能引导Linux的。

启用开发者模式

  • 启动电脑
  • 按住Esc和F3(刷新)按钮,这时候按电源键,即可进入Recovery 模式
  • 按Ctrl+ D,会提示你是否要进入开发者模式,确认之后等一会系统就会转变成启用开发者模式

这时候每次开机屏幕都有警告提示,要么按Ctrl + D,或者等待30s系统响一声便进入系统。

进入超级用户shell

  • 按Ctrl+Alt+F2(→),之后你会看到一个登录提示
  • 输入 chronos 作为用户名,没有密码
  • 然后输入 sudo bash,以超级用户使用shell

启用SeaBIOS

在终端里面输入下面的指令:

    # crossystem dev_boot_usb=1 dev_boot_legacy=1

之后重启电脑即可。之后每次进入SeaBIOS都要在开机的时候按Ctrl + L

将SeaBIOS设置成默认启动

首先你需要破解硬件写保护,按照wiki上的说法需要拆机和跳线,具体的操作可以查看这个wiki,拆机操作非常危险,请自己慎重考虑。

如果你已经成功破解硬件写保护,接下来就是解除软件写保护。同样是进入超级用户shell然后输入指令禁用软件写保护:

    # flashrom --wp-disable

检查软件写保护:

    # flashrom --wp-status

运行set_gbb_flags.sh :

    # set_gbb_flags.sh

注意:新版本的Chrome OS已经把这个脚本移动到 /usr/share/vboot/bin/set_gbb_flags.sh位置了,然而并不在$PATH环境变量中。

确认有下面的输出:

    GBB_FLAG_DEV_SCREEN_SHORT_DELAY 0x00000001
    GBB_FLAG_FORCE_DEV_SWITCH_ON 0x00000008
    GBB_FLAG_FORCE_DEV_BOOT_LEGACY 0x00000080
    GBB_FLAG_DEFAULT_DEV_BOOT_LEGACY 0x00000400

现在可以设置SeaBIOS为默认了:

    # set_gbb_flags.sh 0x489

最后再重新启用写保护:

    # flashrom --wp-enable

现在可以使用Linux安装U盘进行安装了。

写在最后

插上U盘,开机然后按Ctrl+ L 发现启动光盘正常启动,然后选择安装之后突然提示。。。

    not enough memory to load specified image

这时候不要慌张,在安装选择菜单点击Tab键,可以编辑启动命令,在双横线前面输入 mem=4G 注意是双横线前面!

恢复Chrome OS,请参阅Recover your Chromebook

参考:Chrome OS devices Chromebook Installing Linux on the Chromebook Pixel

tagged: chrome

突破同源策略限制的方法

Posted on

同源策略基本介绍

同源策略(Same Origin Policy)是一种约定,它是浏览器最核心也是最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能会受到影响。可以说Web是构建在同源策略的基础之上的,浏览器只是针对同源策略的一种实现。

浏览器的同源策略,限制了来自不同源的“document”或脚本,对当前“document”读取或设置某些属性。

但是随着互联网的发展跨域的需求也越来越迫切,有些时候我们需要突破同源策略。下面开始介绍突破同源策略的几种方法:

使用HTTP头

因为JavaScript无法控制返回的HTTP头,所以可以在服务器端这个HTTP Header即可:

    self.set_header("Access-Control-Allow-Origin", "*")

使用JSONP

Jsonp(JSON with Padding)是 json 的一种“使用模式”,可以让网页从别的网域获取资料。jsonp是采用的js的回调机制来实现的。 本质上是因为GET方法不受同源策略限制,然后获取到的数据不是json,而是一段JavaScript并解释执行。

使用iframe

上面两种都是比较常见和常用的突破浏览器的方法,这种不常见的才是重点。虽然使用iframe算不上优雅,但也算开了眼界。在开发115网盘的时候,发现115 的主站网页和获取数据的网址是不同源的,也顺便研究了一下工作原理。
主站是:http://115.com/?mode=wangpan,
但是获取数据的API是:http://web.api.115.com/files/download?pickcode=?
这就造成了非同源,但是115巧妙的使用了一个桥接网页完成了跨域操作!
桥接网址:http://web.api.115.com/bridge_2.0.html?namespace=DownBridge&api=jQuery
关键代码:

    if(parent){
        var execObj = parent.window;
        if(params["namespace"]){
            var tmp = params["namespace"].split("."),f;
            while(f = tmp.shift()){
                execObj = execObj[f];
            }
        }
        var method = params["api"]? params["api"] : "DataAPI";
        execObj[method] = $;
    }

可以看到,桥接是首先判断父Window是否存在,获取namespace的值设置为 execObj 最后再获取api的值然后设置为 execObj的一个方法,不过这个方法对应的值肯定是 $,也就是整个JQuery,这样我们就可以使用页面 web.api.115.com 的JQuery发起数据请求从而绕过跨越的限制。

下面是115.com 上面注入的JS实现方法:

    set_down_url:function(){
        var self=this;
        DownBridge={};
          $('<iframe>').attr('src', 'http://web.api.115.com/bridge_2.0.html?namespace=DownBridge&api=jQuery').css({
            width: 0,
            height: 0,
            border: 0,
            padding: 0,
            margin: 0,
            position: 'absolute',
            top: '-99999px'
          }).one('load',function(){
            window.DownBridge.getFileUrl=function(pickcode,callback){
            this.jQuery.get('http://web.api.115.com/files/download?pickcode=' + pickcode, function (data) {
                     callback(data);
                    }, 'json');                        
            };
            window.DownBridge.getFileList=function(cate_id,callback){
            this.jQuery.get('http://web.api.115.com/files?aid=1&limit=1000&show_dir=1&cid=' + cate_id, function (data) {
                     callback(data);
                    }, 'json');                        
            };
          }).appendTo('html');
    }

115网站的这种实现方法很另类,不知道是否是有什么特殊需求导致的,但是很有意思~ 不过我还是不建议在网页里嵌入这么多的iframe,严重影响用户体验,非常不推荐!

tagged: JavaScript

CSS使用box-shadow和border-radius的妙用

Posted on

使用CSS画出一个圆角矩形很简单,但是要画出一个和圆角接近平行的月牙就是需要一点技巧了。 例如下面这个图片:

“首页”左边的那个白色和圆角平行的弧形就是我们要用CSS设计实现的。

先使用box-shadow制造一个长条:

    box-shadow: 10px 0 0 0 rgba(245,245,245,0.7);

效果如下图:

再使用border-radius使这个长条变成弧形:

    border-radius: 0 0 100px 0;

效果如下图:

最后再嵌套一个DIV截取我们所需要的部分

    overflow: hidden;

这样就达到我们一开始的图片效果了~

示例代码:点我点我

tagged: CSS

Windows下如何进行进程注入

Posted on

上学期学了有关Windows程序底层方面的知识,学习到病毒的基本工作原理。好记性不如烂笔头,学完之余还是写点东西出来吧~ 不过我也只是抛砖引玉,详细知识还是得自己多多钻研~

首先打开进程

首先使用Win系统的OpenProcessAPI打开所要注入的进程。 使用参数是进程的PID,可以使用任务管理器查看进程的PID,打开成功之后返回程序的句柄。

开辟内存空间

Win系统也提供了开辟内存空间的VirtualAllocExAPI。 传入的参数包括进程句柄,开辟起始地址,开辟空间的大小,分配的数据类型,分配空间的权限。例如: pRemoteCode = (PBYTE) VirtualAllocEx(hProcess, 0, dwSizeOfCode, MEM_COMMIT, PAGE_EXECUTE_READWRITE);

写入恶意代码

使用Win系统的WriteProcessMemoryAPI向新开辟的内存空间写入数据。注意这里写入的是二进制数据,要考虑到各种API函数的寻址问题。

执行注入的代码

微软照样还是提供了相关的CreateRemoteThreadAPI,我们注意最后一个参数是输出而不是输入。

获取执行的结果

如果要获取执行的结果,首先我们得等待线程的执行完成,使用WaitForSingleObjectAPI.例: WaitForSingleObject(hThread, INFINITE); 然后再使用GetExitCodeThreadAPI,得到返回的结果。

释放开辟的空间

干了坏事得不留痕迹才行,回收自己开辟的空间,使用VirtualFreeExAPI.

关闭打开的进程

打开的进程也得关闭,使用CloseHandleAPI.

代码示例:

        hProcess = OpenProcess(PROCESS_CREATE_THREAD 
            | PROCESS_QUERY_INFORMATION
            | PROCESS_VM_OPERATION 
            | PROCESS_VM_WRITE 
            | PROCESS_VM_READ,
            FALSE, PID);

        if (hProcess == NULL) {
            printf("failed.\n"); 
            return -1;
        }   
        printf("ok.\n");

        printf("[I]: Allocating remote memory with size of 0x%08x ......", 
            dwSizeOfCode);

        pCodeRemote = (PBYTE) VirtualAllocEx(hProcess, 
                0, 
                dwSizeOfCode, 
                MEM_COMMIT, 
                PAGE_EXECUTE_READWRITE);        
        if (pCodeRemote == NULL) {
            printf("failed.\n");
            CloseHandle(hProcess);
            return -1;
        }
        printf("ok at 0x%08x.\n", pCodeRemote);

        do_link_before_inj(pCodeRemote);

        printf("[I]: Writing code ......");
        if (WriteProcessMemory(hProcess, 
                pCodeRemote, 
                pCode, 
                dwSizeOfCode, 
                &dwNumBytesXferred) == 0) {
            printf("failed.\n");
            VirtualFreeEx(hProcess, pCodeRemote,
                    dwSizeOfCode, MEM_RELEASE);
            CloseHandle(hProcess);
            return -1;
        };
        printf("ok (%d bytes were written).\n", dwNumBytesXferred);

        printf("[I]: Creating a remote thread ......");
        hThread = CreateRemoteThread(hProcess, NULL, 0, 
                (LPTHREAD_START_ROUTINE) pCodeRemote,
                pCodeRemote, 0 , &dwThreadId);
        if (hThread == 0) {
            printf("failed.\n");
            if ( pCodeRemote != 0 ) 
                VirtualFreeEx(hProcess, pCodeRemote, 0, MEM_RELEASE);
            if ( hThread != 0 )         
                CloseHandle(hThread);
            return -1;
        }
        printf("ok.\n");

        printf("[I]: Waiting the remote thread ......");
        WaitForSingleObject(hThread, INFINITE);
        GetExitCodeThread(hThread, (PDWORD) &exitcode);
        printf("exited with 0x%08X\n", exitcode);

        VirtualFreeEx(hProcess, pCodeRemote, 0, MEM_RELEASE);
        CloseHandle(hProcess);
tagged: notes

使用Aria2下载百度网盘和115的资源

Posted on

虽然我已经开发了两款专门为Aria2用的下载插件,但是我发现还是有同学不会用Aria2c这么好的东西呢~ 于是还是写一篇文章来好好的介绍下吧~

安装Aria2

Aria2官网,通过官网应该能找到各个系统的下载包啦~ Linux用户可以直接通过包管理器进行下载安装,OSX我就没有经验了...Win用户直接下载对应的ZIP包就可以了

配置Aria2

Aria2有两种下载模式,一种是命令行下载模式,一种是RPC Server模式.前者不建议使用,后者的使用方式很方便. RPC模式就是启动之后什么也不做,等着通过RPC接口接受下载请求.下载完也不会退出,一直等待. 使用命令行加参数的方式配置Aria2非常不推荐,建议使用配置文件的方式,下面贴出我的配置文件.

    #用户名
    #rpc-user=user
    #密码
    #rpc-passwd=passwd
    #上面的认证方式不建议使用,建议使用下面的token方式
    #设置加密的密钥
    #rpc-secret=token
    #允许rpc
    enable-rpc=true
    #允许所有来源, web界面跨域权限需要
    rpc-allow-origin-all=true
    #允许外部访问,false的话只监听本地端口
    rpc-listen-all=true
    #RPC端口, 仅当默认端口被占用时修改
    #rpc-listen-port=6800
    #最大同时下载数(任务数), 路由建议值: 3
    max-concurrent-downloads=5
    #断点续传
    continue=true
    #同服务器连接数
    max-connection-per-server=5
    #最小文件分片大小, 下载线程数上限取决于能分出多少片, 对于小文件重要
    min-split-size=10M
    #单文件最大线程数, 路由建议值: 5
    split=10
    #下载速度限制
    max-overall-download-limit=0
    #单文件速度限制
    max-download-limit=0
    #上传速度限制
    max-overall-upload-limit=0
    #单文件速度限制
    max-upload-limit=0
    #断开速度过慢的连接
    #lowest-speed-limit=0
    #验证用,需要1.16.1之后的release版本
    #referer=*
    #文件保存路径, 默认为当前启动位置
    dir=/home/acgotaku/Downloads
    #文件缓存, 使用内置的文件缓存, 如果你不相信Linux内核文件缓存和磁盘内置缓存时使用, 需要1.16及以上版本
    #disk-cache=0
    #另一种Linux文件缓存方式, 使用前确保您使用的内核支持此选项, 需要1.15及以上版本(?)
    #enable-mmap=true
    #文件预分配, 能有效降低文件碎片, 提高磁盘性能. 缺点是预分配时间较长
    #所需时间 none < falloc ? trunc << prealloc, falloc和trunc需要文件系统和内核支持
    file-allocation=prealloc

小白用户可以直接copy我的配置文件保存成aria2.conf进行使用.
然后在终端里面输入 aria2c --conf-path=<PATH> 注意PATH必须是绝对路径.
例如: D:\aria2\aria2.conf 可以使用 -D 参数使Aria2在后台运行,即使关闭终端也不会停止运行. Win下可以把这个命令行保存成bat文件进行运行.注意路径不需要使用引号括起来.

接下来是如何管理Aria2的下载任务了,推荐使用binux菊苣的YAAW,超级好用,下载打开即用.
懒得下载的话可以使用在线版,只需在设置里面修改下RPC PATH为 http://localhost:6800/jsonrpc

百度网盘插件

我开发的百度网盘插件已经发布到Web Store了,无法翻墙的同学可以去Github下载安装包进行安装.安装之后打开百度网盘会发现在我的设备按钮的 右侧多了一个导出下载按钮,如果你使用的是默认配置的话那么选中要下载的文件之后点击ARIA2 RPC即可导出到Aria2进行下载,前提是你已经开启了Aria2的RPC模式.

115网盘插件

115网盘插件刚刚发布功能还不完善,可能还有未知的BUG,我后续会进行优化和开发的.安装之后打开115网盘, 会发现多了一个 设置导出按钮 的按钮.点击这个按钮之后会提示设置成功的,然后把鼠标移动到要下载的文件上会出现 导出下载 的按钮, 点击即可导出到Aria2下载.

参考: Aria2下载示例

如果有不懂的可以在GithubGoogle+上联系我~

tagged: notes

使用Postfix,Dovecot和Mysql配置邮件服务器

Posted on

由于学习的需要,我要配置个本地邮件服务器.虽然找到了一篇很详细的资料,但是在配置过程中还是遇到了这样那样的问题. 写篇文章记下来自己的学习过程,毕竟好记性不如烂笔头嘛.

详细教程

Google一下便找到了一个十分详细的教程,分享给大家:How To Configure a Mail Server Using Postfix, Dovecot, MySQL, and SpamAssasin 教程虽然很详细,但难免有不理解和差错的地方.下面就讲下我遇到的问题:

Dovecot证书问题

教程上设置的证书位置是:

    ssl_cert = </etc/ssl/certs/dovecot.pem
    ssl_key = </etc/ssl/private/dovecot.pem

但实际上是空的 需要手动copy过去,默认存储的位置是:

    ssl_cert = /etc/dovecot/dovecot.pem
    ssl_key = /etc/dovecot/private/dovecot.pem

查看日志发现读取配置出错

出错信息如下:

    dovecot: lmtp(11504): Fatal: Error reading configuration: Invalid settings:
    postmaster_address setting not given

查到相关资料Invalid settings: postmaster_address setting not given 简单来说就是在/etc/dovecot/dovecot.conf文件内添加一行 postmaster_address=postmaster at DOMAIN 即可

重启Dovecot

当我们执行 service dovecot restart 时,发现根本没有这个service.
其实dovecot已经在运行了,我们只需执行 dovecot reload 即可

邮件无法发送

日志输出如下:

    B0E4F26077E: to=<root@example.jp>, orig_to=<root>, relay=none, delay=6780, delays=6780/0/0.09/0, dsn=5.4.4, status=bounced (Host or domain name not found. Name service error for name=example.jp type=AAAA: Host not found)

这个问题是因为Postfix默认启用了IPv6并且优先级比IPv4要高,所以需要在配置文件 /etc/postfix/main.cf里面设置 inet_protocols = ipv4 这样就关闭IPv6了.即可解决问题.

大概就是遇到这么多问题了,最后祝愿大家圣诞节快乐~

tagged: notes

JavaScript处理二进制数据

Posted on

最近在开发扩展的时候遇到需要获取MP3数据并且直接交给其它接口处理的问题,但是使用JQuery的AJAX进行 获取数据的时候已经把数据进行了编码,毕竟默认都是处理文本数据.
但是我需要的是二进制数据并且进行Base64编码方便进行传输,因为Base64编码的值全是可打印字符. Base64常用于在通常处理文本数据的场合,表示、传输、存储一些二进制数据.

二进制数据转换成Base64编码

于是便找到了一个非常有用的MDN的文档Sending and Receiving Binary Data
不过稍作修改:

    var oReq = new XMLHttpRequest();
    oReq.open("GET", request.data, true);
    oReq.responseType = "blob";
    oReq.onload = function (oEvent) {
        var blob = oReq.response;
        var reader = new FileReader();
        reader.onload = function(readerEvt) {
            var binaryString = readerEvt.target.result;
            // console.log(binaryString);
            var testdata=btoa(binaryString);
            var data={
                "method":"getAudio",
                "data":'data:audio/mp3;base64,'+btoa(binaryString)
            };
            port.postMessage(data);
        };
        reader.readAsBinaryString(blob);
    };

    oReq.send(null);                                          
}

设置responseType为Blob 然后使用FileReader作为二进制数据读取,再使用btoa转码成Base64进行传输.

还有一种是读取需要上传的文件进行处理的方式和通过AJAX获取的方式差不多,代码在这里.使用Base64编码的好处是在需要二进制数据的地方,例如图片和音频,我们可以使用Base64编码进行替换获取这些资源的URL 可以减少HTTP请求,也可以做转发,我之所以有这个处理二进制数据的需求就是因为开发的一个插件无法在HTTPS页面上获取HTTP资源, 便通过 background js 进行获取数据然后Base64编码传输给content js进行使用.

Base64数据转换为ArrayBuffer

有时候需要把传入的二进制数据转换成ArrayBuffer进行处理就需要转换了

    var data = atob(base64String);
    var dataView = new Uint8Array(data.length);
    for (var i=0; i<data.length; ++i) {
      dataView[i] = data.charCodeAt(i);
    }
    var arrayBuffer=dataView.buffer;

今天暂时就写这么多吧,期末考试看书好累,头脑都不太清醒了. QAQ

tagged: JavaScript

扇贝单词助手发布

Posted on

最近受菊苣binux的要求,希望能开发一款扇贝网的单词助手,可以标记任意页面上已经背过的单词.项目源码在Github. 然后我就趁着国庆就开坑了,binux也给了我很多参考资料.因为之前也开发过Chrome的扩展,所以对Chrome提供的API比较熟悉.这里就介绍下使用的相关的JavaScript的知识吧~

Promise

Promise是用来处理大量的异步操作,因为异步操作不能直接return返回值,必须进行回调.所以就会导致很多异步操作在一起 不断的进行嵌套,这样实在是很不优雅的代码书写方式.代码首先是用来跟人读的,其次才是可以运行.使用Promise就可以把多个 异步操作的返回值以数组的形式返回来,然后再进行回调.语言描述不好,还是上例子吧.

    function get_cookie(site,name){
        return new Promise(function(resolve, reject) {
            chrome.cookies.get({"url": site, "name": name}, function(cookies) {
                if (cookies) {
                    var data = cookies.name + "=" + cookies.value;
                    resolve(data);
                }else{
                    reject();
                }
            });
        });
    }

Chrome的API全部是异步操作,所以我一开始使用return的时候并没有得到值.把回调函数封装到一个Promise对象里面,然后传入两个参数 resolve,reject 分别代表异步操作成功执行或失败需要调用的函数.

    var site1 = "http://pan.baidu.com/";
    var name1 = "BDUSS";
    var site2 = "http://pcs.baidu.com/";
    var name2 = "pcsett";
    Promise.all([get_cookie(site1,name1), get_cookie(site2,name2)]).then(function(value){
        console.log(value);
    },function(){});

这里我们需要调用两次异步操作,一般情况的话需要函数嵌套,但是使用Promise对象的话,只需要在then里面需要写上 resolve 和 reject 需要的执行函数, 传入的value就是两次异步操作的返回值数组.然后就可以进行相关的处理.代码就变得清晰有条理.

Promise文档

Promise.all

tagged: notes

计算机中的字节序问题

Posted on

计算机字节序这个问题,最近被老师反复提到.我也在本科阶段折腾交叉编译的时候遇到过. 并且这个问题貌似也是面试的时候一个常见问题,所以还是写下来比较好.

字节序顾名思义就是计算机中字节的存储顺序.目前最常见的就是大端字节序和小端字节序. 貌似百科里面又有一个中端字节序...(不过确实很少见)

大端字节序

按照最高位字节(包含最高位,即MSB,的字节)至最低位字节(包含最低位,即LSB,的字节)的顺序,存放在连续的地址中.

采用这种机制的处理器有IBM3700系列,和绝大多数的RISC处理(我的路由器就是)

小端字节序

按照最低位字节(包含LSB的字节)至最高位字节(包含MSB的字节)的顺序,存放在连续的地址中.

采用这种机制的处理器最常见的就是Intel系列处理器

判断字节序

当然字节序不能完全靠处理器来判断,因为有些处理器可能既支持大端字节序又支持小端字节序 最好用的方式还是用个程序跑一跑,实践是检验整理的唯一标准.

    #include <stdio.h>
    int main()
    {
        unsigned int x = 0x12345678;
        char *p = (char *)&x;
        int i;
        for (i = 0; i < sizeof(x); i++){
            printf("%.2x ", *p++);
        }
        printf("\n");
        return 0;
    }

上面的一段C程序就可以检验字节序,
如果输出是:12 34 56 78,那么CPU是大端字节序.
如果输出时:78 56 34 12,那么CPU是小端字节序.
输出证明,大端字节序更适合人类阅读.

涉及字节序的问题

谈到字节序问题,是因为网络编程的时候如果双方的字节序不一样 肯定会出问题的.本人在交叉编译学校的校园网程序的时候就是 遇到了这个问题,因为字节序的不同,导致发送的数据包是相反的. 后来在老师的点拨下才得以解决.

tagged: endian

chrome扩展开发经验记录

Posted on

最近写了两个chrome扩展.看了不少chrome扩展开发的文档.觉得还是写点东西比较好.

Background Pages

一个扩展肯定需要长时间运行的脚本对扩展进行管理.这个脚本就属于Background Pages的一部分. 不过由于Background Pages是一直运行的,对资源占用比较多.
现在已经用Event Pages代替.Event Pages是按需加载,不需要的时候不会激活运行.
Event Pages的JS可以使用chrome的所有API.
官方文档

Content Scripts

Content Scripts是运行在网页上的.manifest上进行网址匹配.当是目标网址的时候就加载这个JS. Content Scripts可以获取到匹配网址的DOM,并进行修改.Content Scripts也可以使用chrome的 API,但是只能使用比较有限的API.
官方文档

Message Passing

这个是chrome的通讯机制,是非常重要的一个知识点.开发扩展的content scripts几乎都是需要和 background pages进行通讯的.因为content scripts可以直接操作DOM,background pages可以 使用所有的chromeAPI.所以这俩JS肯定要互通有无.接下来重点讲解chrome的通讯机制.
官方文档

Simple one-time requests

官方文档有例子.但是我要说的是这个Simple requests真的太Simple content js使用sendMessage,就立即回调获取response. 如果background page稍微进行处理下占用点时间就会导致response的函数根本无法执行的情况. 推测可能是没接收到数据就直接pass了.所以稍微复杂点需要处理数据的通讯操作千万不能用这个.

Long-lived connections

这个通讯方式也是我比较推荐的通讯方式,生命周期长.并且通讯效果好,可以多次通讯一次监听. 官网给出的content js例子虽然是先postMessage之后再addListener.但是强烈推荐先 监听之后再发送数据.因为当你发送数据之后,background可能会立马给你返回数据.这时候可能会造成 没有监听到的情况... 之后的通讯方式我还没用到就不说了.

网页JS和 content js通讯

网页JS也有自己的局限性,例如无法获取到 http only的cookies.这时候可以通过content js的帮助. content js获取到数据之后怎么发给网页JS呢? content js使用window.postMessage进行发送数据:

    window.postMessage(msg, "*");

网页JS通过监听事件进行捕捉数据:

    window.addEventListener("message", receiveMessage, false);

        function receiveMessage(event)
        {
            if (event.origin == window.location.origin){
                console.log(event.data);
            }
        }

注意content js和网页JS不共享变量,所以不能通过全局变量的方式进行通讯.不过可以通过DOM进行通讯.

content js使用网页JS的变量.

如果网页本身加载了很多组件例如JQuery,自己想使用但是因为不共享变量导致无法使用.可以使用append的 方式把自己content js里面写的函数直接注入到网页的DOM中,因为DOM是共享的.这样content js写的函数 就变成网页的JS进行运行了~

content js注入

    var script = document.createElement('script');
    script.id = "baidu_script";
    script.appendChild(document.createTextNode('(' + baidu + ')();'));
    (document.body || document.head || document.documentElement).appendChild(script);

这里注入的是一个立即执行函数,append到DOM上的时候就会立即执行,并且可以使用网页JS的变量.

css 注入

有时候修改DOM的话肯定需要更改样式啊,这时候用内联样式必然太没效率,不能重用.就需要添加CSS.

    var css = function() {/*
     input{
     border: 1px solid #C6C6C6;
     box-shadow: 0 0 3px #C6C6C6;
     -webkit-box-shadow: 0 0 3px #C6C6C6;
     }
     */
    }.toString().slice(15, -4);
    var style = document.createElement('style');
    style.setAttribute('type', 'text/css');
    style.textContent = css;
    document.head.appendChild(style);

首先定义一个匿名函数赋值给css变量,然后里面写了css内容,由于是注释掉的,其实并不会被JS执行,最后 转换成字符串的时候是能读到CSS文本的,slice是去掉前后的注释符.

tagged: chrome

Linux下恢复误删除的文件

Posted on

经常有时候喜欢删除文件...但是之后又后悔删除... Linux下rm之后是没有回收站的.
于是我便寻找恢复文件的相关资料.

TestDisk

TestDisk是用来帮助恢复丢失的分区和使无法启动的启动盘再次启动.
使用TestDisk之前别忘了备份分区表.

备份分区表

# sfdisk -d /dev/sda > /tmp/sda.bak

恢复分区表

# sfdisk /dev/sda < /tmp/sda.bak

官方教程:TestDisk Step By Step

PhotoRec

PhotoRec是用来恢复丢失的文件和照片.即使重新格式化或严重损坏的文件系统都可以恢复. 但是PhotoRec只有全盘扫描和空闲分区扫描.耗时很长.对于自己无意中删除的小文件来说就是 杀鸡焉用牛刀.没有这个必要.

Extundelete

Extundelete设计的就是用来从ext3或ext4的分区恢复最近被删除的文件.它可以从一个相对路径恢复 被删除的文件,非常实用.但是只有当分区被卸载才可以使用.

恢复被删除的文件

# extundelete --restore-file tux/cv.tex /dev/sda4

恢复被删除的文件目录

# extundelete --restore-directory tux/Documents/tex/ /dev/sda4

Arch wiki:File recovery

百度网盘新版API

Posted on

突然发现百度网盘又更新API了....明明UI一点都没有更新.我严重怀疑这个前端工程师是有多寂寞... 没事就更新JS...这已经是我观察发现的第三次更新了...每次都导致我的aria2c导出脚本失效...

于是我又苦苦寻找新版的JS到底是怎么实现下载的,虽说可以直接操纵DOM获取选中的文件的fs_id信息 然后POST数据获取dlink(下载地址),但是这样怎么也不够优雅啊...因为百度自己的JS肯定实现了这部分内容 于是我便花了更多的时间找API,但是百度那既压缩又混淆的JS代码真是读起来特带劲啊(我绝对不是抖M)

于是说一下百度的新版API吧:

Toast模块

新版API把每个功能全部模块化,声明某个模块的方法是使用require方法.

    var SetMessage = function(msg, type) {
        var Toast = require("common:widget/toast/toast.js");
        Toast.obtain.useToast({
            toastMode: Toast.obtain[type],
            msg: msg,
            sticky: false
        });
    }

于是我便封装了一个函数用来实现消息提示.

data-center模块

    var File = require("common:widget/data-center/data-center.js");
    return File.get("selectedList");

data-center模块可以获取选择的文件的fs_id,通过传入fs_id可以获取dlink. File.get("selectedItemList")可以获取选择的文件的DOM结构.

commonService模块

    var Service = require("common:widget/commonService/commonService.js");
    Service.getDlink(JSON.stringify(File.get("selectedList")), "dlink", self.aria2_rpc.bind(self));

commonService模块有很多实用的功能,但是我们目前只需要这么一个就足够了,有需要的可以自己研究下.getDlink接受三个参数.第一个是 每个要下载的文件的fs_id,多个文件是数组的字符串方式传入,第二个是获取dlink的类型,总共有三个类型batsh,dlink,nolimit,第一个类型 是把传入的多个文件合并成一个压缩包进行下载,返回一个dlink,第二个是单独获取每个文件的dlink,第三个还没有尝试过.第三个参数是回调函数 会传入POST之后获取的json数据.

设置UA

因为我现在还没找到怎么获取选择的文件名的列表,所以在aria2c的时候无法手动指定文件名.貌似不写UA的情况下会被限速. 设置chrome的UA文件名会被encode,所以aria2c的时候设置UA为百度云管家的UA

    var UA="netdisk;4.4.0.6;PC;PC-Windows;6.2.9200;WindowsBaiduYunGuanJia";

设置cookie

百度云盘升级之后仅仅只有dlink还不行,还需要一个name为BDUSS的cookie. 但是这个cookie被设置为http only,所以JS是无论如何也获取不到的.目前只能采用手动设置的方法. 还好这个cookie每个用户基本都是唯一的,设置一次永久使用,否则真是麻烦.

完整版实现GitHub

书签:baidu_aria2c

Archlinux下声卡的设置与折腾

Posted on

最近又有瞎折腾装Arch....发现显卡驱动的安装和设置都不是问题... 声卡却老是出现奇怪的问题,便把最近折腾的经验写下来吧~

切换默认声卡

发现最近的新本子基本上声卡设备都不止一个,个人猜测应该是HDMI音频输出和 本机主板上的音频输出吧.可以使用 aplay -l 命令显示所有声卡设备. 我机器的输出是:

    [acgotaku@Archlinux ~]$ aplay -l
    **** List of PLAYBACK Hardware Devices ****
    card 0: HDMI [HDA Intel HDMI], device 3: HDMI 0 [HDMI 0]
      Subdevices: 1/1
      Subdevice #0: subdevice #0
    card 1: PCH [HDA Intel PCH], device 0: ALC3220 Analog [ALC3220 Analog]
      Subdevices: 1/1
      Subdevice #0: subdevice #0

第一个一般为默认的声卡输出设备,显然HDMI输出是不对的,导致声音无法正常播放,我们应该把默认声卡切换为 第二个.最简单易用的方法是在用户根目录下新建一个 .asoundrc 文件.输入以下内容:

    defaults.pcm.card 1
    defaults.pcm.device 0
    defaults.ctl.card 1

上面的数据可以从声卡设备输出中找到,这样就切换到 card 1的 device 0 设备了.不过切换之后可能发现还是没有 声音,因为alsa默认是静音的需要手动解除才行. 这就需要去官方源安装 alsamixer 工具. alsa-utils 我自己安装的alsamixer使用界面是:

默认Master竖条下方显示的不是00而是MM,表示是静音的,按下 m 键解除静音.

耳机有持续的杂音的解决方案

有些情况下即使安装好alsa解除静音了,但仍然发现戴耳机的时候没有任何软件发声的时候会有持续的杂音,但是音乐播放器一旦放音乐的时候杂音又消失了. 这个问题的原因是alsa 默认设置Mic声道是静音的,我们只需要解除Mic声道的静音并调节至合适的值即可.

恢复默认的alsa设置

有些时候自己胡乱设置导致alsa无法正常工作的时候可以通过使用 alsactl restore 命令使alsa恢复到默认的配置一般都能正常工作了.

tagged: linux

JavaScript中一个经典的正则例子

Posted on

学习知识的最好方法就是看例子.(个人观点) 因为书上总是会把概念和公式讲的晦涩难懂,才显得作者高大上.所以我总是喜欢 通过例子学习知识.每次写正则的时候总是喜欢查看例子学习如何使用,因为正则实在太耗脑细胞了,记不住,也没必要记住.学会 如何使用并在项目中熟练运用就好了,经常使用的话也就自然记住了.

JavaScript语言精粹上面有个非常棒的学习例子,每次写正则的时候都会想起它.所以还是记一下分享给大家也方面我查询啦~

    var parse_url=/^(?:([A-Za-z]+):)?(\/{0,3})([0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/;
    var url="http://www.ora.com:80/goodparts?q#fragment";
    var result=parse_url.exec(url);
    var names=['url','scheme','slash','host','port','path','query','hash'];
    var blanks='     ';
    var i;
    for(i=0;i<names.length;i+=1){
        console.log(names[i]+ ':' +blanks.substring(names[i].length),result[i]);
    }

让我们分解parse_url的各个部分,看看它是如何工作的:

^

^字符表示此字符串的开始.它是一个锚,指引exec不要跳过那些不像URL(non-URL-like)的前缀,只匹配那些从开头就像URL一样的字符串:

(?:([A-Za-z]+):)?

这个因子匹配一个协议名,但仅当它后面跟随一个:(冒号)的时候才匹配.(?:...)表示一个非捕获型分组(noncapturing group). 后缀 ? 表示这个分组是可选的. 问号表示重复0次或一次.(...)表示一个捕获型分组(capturing group).一个捕获型分组会复制它 所匹配的文本,并把其放到result数组里.每个捕获型分组都会被指定一个编号.第一个捕获型分组的编号是1,所以该分组所匹配的文本副本 会出现在result[1]中.[...]表示一个字符类,A-Za-z这个字符类包含26个大写字母和26个小写字母.连字符( - )表示范围从 A 到 Z. 后缀 + 表示这个字符类会被匹配一次或者多次.这个组后面跟着字符:,它会按字面进行匹配:

(\/{0,3})

下一个因子是捕获型分组2.\/表示该匹配/(斜扛).它是\(反斜杠)来进行转义,这样它就不会被错误地解释为这个正则表达式的结束符.后缀{0,3}表示 / 会被匹配0次,或者或者1~3次:

([0-9.\-A-Za-z]+)

下一个因子是捕获型分组3.它会匹配一个主机名,由一个或多个数字、字母,以及 . 或 - 字符组成. - 会被转义为 \- 以防止与表示范围的连字符想混淆:

(?::(\d+))?

下一个可选的因子匹配端口号,它是由一个前置 : 加上一个或多个数字而组成的序列. \d 表示一个数字字符.一个或多个数字组成的数字串会被捕获型 分组4捕获:

(?:\/([^?#]*))?

接下来是另一个可选的分组.该分组以一个 / 开始.之后的字符类[^?#]以一个 ^ 开始,它表示这个类包含除了?和#之外的所有字符.*表示这个字符类 会被匹配0次或多次.

注意这里的处理是不严谨的.这个类匹配除?和#之外的所有字符,其中包括了行结束符、控制字符,以及其他大量不应在此被匹配的字符.大多数情况下,它会按照 我们的预期去做,但某些恶意文本可能会有渗透进来的风险.不严谨的正则表达式是一个常见的安全漏洞发源地.写不严谨的正则表达式比写严谨的 正则表达式要容易得多.

(?:\?([^#]*))?

接下来,还有一个以一个 ? 开始的可选分组.它包含捕获型分组6,这个分组包含0个或多个非#字符.

(?:#(.*))?

最后一个可选分组是以 # 开始的 .会匹配除行结束符以外的所有字符.

$

$表示这个字符串的结束.它保证在这个URL的尾部没有其他更多内容了.

JavaScript函数中的闭包

Posted on

JavaScript中的闭包通常被视作 JavaScript 的高级特性.但是我看了好几次文档也没有明白过来.也没有实际应用过.最近重新学习JavaScript 打算好好学习下.

什么是闭包

闭包用通俗的说法来理解就是子函数可以使用父函数中的局部变量,这种行为叫做闭包.这个变量不是在这个代码块内或者全局上下文中定义的,而是 在定义代码块的环境中定义的(局部变量).一个经典的例子就是:

    function a(){
        var i=0;
        function b(){
        alert(++i);
        }
        return b;
    }
    var c = a();
    c();

函数b嵌套在函数a内部,函数a返回函数b.这样在执行了 var c = a() 之后,变量c变指向了函数a.再指向c()就会弹窗显示i的值.这段代码就创建了一个 闭包.因为函数a外的变量c引用了函数a内的函数b.就是说,当函数a的内部函数b被函数a外的一个变量引用的时候,就创建了一个闭包.

闭包的用途

  • 可以读取函数内部的变量
  • 这些变量的值始终保持在内存中
  • 模拟私有变量

上面的例子中,i的值只有b函数可以读取.当你不断执行c()的时候,发现i值不断增大,说明i没有被内存回收.

使用闭包构造模块

模块是一个提供接口却隐藏状态与实现的函数或对象.通过使用函数产生模块,我们几乎可以完全摈弃全局变量的使用. 举例来说,假定我们想要给String增加一个deentityify方法.它的任务是寻找字符串中的HTML字符实体并把它们替换为对应的字符. 这就需要在一个对象中保存字符实体的名字和它们对应的字符.但我们该在哪里保存这个对象呢?我们可以把它放到一个全局变量中, 但全局变量是魔鬼.我们可以把它定义在该函数的内部,但是那会带来运行时的损耗,因为每次执行该函数的时候该字面量都会被求值一次. 理想的方式就是使用闭包实现.

    Function.prototype.method=function(name,func){
        this.prototype[name]=func;
        return this;
    };
    String.method('deentityify',function(){
        //字符实体表,它映射字符实体打名字到对应的字符.
        var entity={
            quot:'"',
            lt:'<',
            gt:'>'
        };
        return function(){
            //查找以'&'开头和';'结束打子字符串.如果找到就替换.
            return this.replace(/&([^&;]+);/g,
                function(a,b){
                    var r=entity[b];
                    return typeof r === 'string' ? r : a;
                }
                );
        };
    }());
    cosole.log('&lt;&quot;&gt;'.deentityify());

通过给Function.prototype增加一个method方法,我们下次给对象增加方法的时候就不必键入prototype这几个字符,省掉了麻烦. 请注意最后一行,我们用()运算法立刻调用我们刚刚构造出来的函数.这个调用所创建并返回的函数才是deentityify方法.
关于这个的用法请参见wiki:IIFE

闭包的缺点

闭包会使局部变量始终存在,内存消耗很大.在IE浏览器中它会很容易导致泄露内存. 还有就是不要在循环中引用闭包.

参考资料

JavaScript函数调用的四种方式

Posted on

JavaScript函数调用除了声明时定义的形式参数,每个函数还接收两个附加的参数:this和arguments.参数this在面向的对象 编程非常重要,它的值取决于调用的模式.典型的两种函数定义方式有:

    var add=function(a,b){
        return a+b;
    };
    function add(a,b){
        return a+b; 
    };

这两种函数声明的区别是什么,是我上次百度面试的一个问题.当时没回答出来,很是尴尬.现在问了前端菊苣之后得出的答案是: 一个是声明了一个变量add,并给它赋初始值(一个匿名函数)另一个是声明了一个名为add的具名函数区别有:
1. add.name不同,trace的时候显示也不同(第一个add.name是"",第二个是"add")
2. 由于声明会被提升,但赋值语句不会,所以在语句之前访问add的时候结果不同(这点老IE还有点区别)

在JavaScript中四种调用模式分别是:方法调用模式,函数调用模式,构造器调用模式和apply调用模式.这些模式在如何初始化关键 参数this上存在差异.

方法调用模式

当一个函数被保存为对象的一个属性时,我们称它为一个方法.当一个方法被调用时,this被绑定到该对象.如果调用表达式包含一个提取属性 的动作(即包含一个 . 点表达式或[subscript]下标表达式),那么它就是被当做一个方法来调用.

    var myObject = {
        value:0,
        increment:function(inc){
            this.value+= typeof inc === 'number' ? inc : 1;
            console.log(inc);
        }
    };
    myObject.increment();  // 1
    myObject.increment(2); // 3

方法可以使用this访问自己所属的对象,所以它能从对象中取值或对对象进行修改.this到对象的绑定发生在调用的时候.这个"超级"延迟绑定(very late binding) 使得函数可以对this高度复用.通过this可取得他们所属对象的上下文的方法成为公共方法(public method).

函数调用模式

当一个函数并非一个对象的属性时,那么它就是被当做一个函数来调用的:

    var sum = add(3,4);

以此模式调用函数时,this被绑定到全局对象(浏览器中就是window对象).这是语言设计上的一个错误.倘若语言设计正确,那么当内部函数被调用时,this应该仍然绑定到 外部函数的this变量.这个设计错误的后果就是方法不能利用内部函数来帮助它工作,因为内部函数的this被绑定了错误的值,所以不能共享该方法对对象的访问权.幸运的是, 有一个很容易的解决方案:如果该方法定义一个变量并给它赋值为this,那么内部函数就可以通过那个变量访问到this.按照约定,我们把那个变量命名为that:

    myObject.double=function(){ //给myObject增加一个 double 方法
        var that=this;
        var helper=function(){
            that.value=add(that.value,that.value);
            console.log(this);
        };
        helper();  //以函数的形式调用helper
    };
    myObject.double(); //以方法的形式调用double

构造器调用模式

如果在一个函数前面带上 new 来调用,那么背地里将会创建一个连接到该函数的prototype成员的新对象,同时this会被绑定到那个新对象上.

    //创建一个名为Quo的构造器函数,它构造一个带有status属性的对象.
    var Quo = function(string){
        this.status=string;
    };
    //给 Quo的所以实例提供一个名为 get_status 的公共方法.
    Quo.prototype.get_status = function() {
        return this.status;
    };
    //构造一个 Quo 实例
    var myQuo = new Quo("confused");

一个函数,如果创建的目的就是希望结合 new 前缀来调用,那它就被称为构造器函数.这种构造器函数不推荐使用,详细内容请看《JavaScript语言精粹》后续内容.

Apply调用模式

因为JavaScript是一门函数式的面向对象编程语言,所以函数可以拥有方法.

apply方法让我们构建一个参数数组传递给调用函数.它允许我们选择this的值.apply方法接收两个参数,第一个是要绑定给this的值,第二个就是一个参数数组.

    var array=[3,4];
    var sum=add.apply(null,array);

    var statusObject={
        status:'A-OK'
    };
    //statusObject并没有继承自 Quo.prototype,但我们可以在 statusObject 上调用
    //get_status 方法,尽管 statusObject 并没有一个名为 get_status 的方法.
    var status=Quo.prototype.get_status.apply(statusObject);

以上内容摘录于《JavaScript语言精粹》.

Archlinux折腾笔记

Posted on

四月也算过去了一大半了,经过了一个月的奔波与折腾,总算安顿好自己了.在学校本地找了一个还算不错的实习. 阴差阳错的选择了Web开发工程师的职位,其实我最想做的是后端开发,前端也还是比较喜欢的.但是我有个很大的 缺点就是不会设计... 不会设计的前端开发工程师很鸡肋的,每次网站开发都是同学出设计图我来做的.这样不行的, 我必须能独当一面才算能在公司站得住脚.

近况就是这些,接下来就是记录下自己在Archlinux下折腾的东西.方便自己以后查询使用.

Linux下视频截图制作动态GIF

观看视频遇到经典的镜头当然得录下来做成GIF~ 首先需要安装 imagemagick 这个软件包才行

mplayer -vo png -ss 00:08:28 -frames 200 123.mp4 
mogrify -resize 848x480 *.png
convert -delay 4  *.png ry.gif

第一句话是利用mplayer输出png格式图片,从8分28秒开始,截图200帧,也可以换成

mplayer -vo png:z=1 -ss 00:08:28 -endpos 8  123.mp4

这样就是截取8秒,png后面的z=1是压缩比例从0到9 压缩程度递增,压缩速度也递增. 第二行便是修改尺寸,毕竟720p的GIF实在是吃不消啊.然后第三行变把这些png生成GIF了.

sudo设置环境变量

有时候程序需要使用root权限,但当我使用sudo的时候结果发现这时候程序又使用root用户的配置文件了.
现在的需求就是程序使用root权限运行但是执行时候读取HOME变量还是当前用户.
Run_X11_apps_using_sudo wiki上有清楚的讲解这样的解决办法.

面试笔记

Posted on

三月下旬,病还没有完全康复.我便赶往上海.只因为认识的一位菊苣帮我投递了一下简历.而且据说人家对我简历印象还不错. 现在想想,自己还真是幼稚...这句话就像你问店里员工我的菜怎么还没上,人家一定会回答:马上就好了,都已经下锅了. 刚刚抵达上海的时候已经是晚上,这是我人生第一次去上海,有种乡下人进城里的感觉.带着梦想与希望,来到了这个地方.

当天晚上便和HR联系面试时间,第二天早上10点就赶到约好的地方进行面试.说实话第一次面试,没有任何经验,炮灰的概率太大了. 虽然我有心理准备,但是面试的时候还是有点小紧张.不过面试的都很基础,例如问了我python如何进行文件读写,这不是太简单了么. 直接查看文档这不就几秒钟的事情么,简单到我根本不会去记住文件读写的具体写法.因为用的时候自然会去看文档,当然了熟练的话 就直接写出来了,我是一个对着电脑编程根本停不下来的人,但是给我纸笔的话反而写不好.这个文件读写放在项目里根本我都不当回事, 但是面试的时候尴尬的没回答好...后来菊苣也说了,这个时候你就如实回答查下文档不就行了么...

后来又去面试了一家公司,好吧.这回面试的公司主动反问我有什么想问的么...当时我就傻逼了,不是面试官考我么...我还问啥啊? 好吧,当时大脑空白不知道该说什么好就草草结束了这场面试,现在想想真是浪费了一次大好的机会.

第三次面试的是一家小公司,这家公司HR之前是知道我面试两家失败了的,然后她问了我一句:你有反思上两家面试的时候为什么失败么? 这可真是一针见血,虽然多多少少我知道自己的不足,但是还没有进行过深刻的反思,HR就对我说了,首先,你说话太快,导致面试官很难听清 你到底在说什么,妈呀...我父母都说我说话太快.这确实是个问题,因为面试官不可能因为你说话快就让你再重复的,也就一带而过了. 第二就是说话紧张,很难发挥自己平常真实的一面,其实我是一个很怕生的人,让我不紧张实在太难了.第三就是转移话题,面试官问的问题要 就事论事,别扯太多,这个,说实话因为上两次面试没有好好的表达自己的能力这回就想多说点证明自己的实力结果还起了反效果... 第三次面试还是失败了,但是HR确实给我很深刻的印象,能告诉面试者不足的HR才是好的HR,没有任何音讯的公司,以后我能力出众的时候你来 挖我我也不会去的,这体现的是一个公司的态度.

三次面试之后我便回学校了,因为上海的生活成本实在太高,我HOLD不住,在学校的时候把毕业论文搞定再说吧.不过在学校的时候也可以继续投递 简历,反思下自己哪方面做的不好有待进步.还是在本校接受电话面试,成功的话再去公司才是正确的选择.毕竟对于我这样的死宅来说长途旅行真是 要命,状态不知道要几天才能调节回来...如果有菊苣有好的职位推荐的话,顺便弱弱的球内推,实习职位即可.方向有:前端开发,Python开发和运维 职位.希望自己之后的面试能顺顺利利~

tagged: notes

漫无止境的三月

Posted on

三月...好漫长...整个2月不是过年走亲戚就是在家看PDF,鸟哥的Linux私房菜,虽然自己用了一年多Linux桌面了,但是感觉知识还是 不够系统,于是又去把鸟哥的Linux私房菜好好读一遍去了...

感觉鸟哥的Linux私房菜写的超小白,有时候想跳着读,但又怕错过了什么...又放弃了,感觉读的有点很浮躁,说实话很多功能如果不常用 的话,也会慢慢忘记,不过还是有印象的,但是就不知道在哪个位置,这就是PDF的缺点了,如果是纸质书的话很容易再去翻阅需要的知识点, 虽然 Ctrl+F 搜索很好用,但是得到的结果总是不尽如人意,我个人还是喜欢纸质书.

光看书真的很没劲,于是我又开坑了...为了练习python,我想当后端工程师啊!IE一天不死我写前端就不会痛快! 新项目就这么愉快的开坑了,前端懒的设计就直接用Bootstrap了,感觉Bootstrap也就只能用些组件的UI, 布局神马的还是不符合我的胃口.项目部署的网址是:http://netkuu.icehoney.me/ 目前还是绝赞填坑中~ 前端后端全部自己一个人写,希望能把自己锻炼成full stack developer~ 现在就差数据库练手了,好久没玩数据库了.

大家肯定会好奇为啥到现在还不去上学或工作,一直在家,其实由于种种原因,上了手术台,在家养病中.希望能尽快康复.我想出去挣钱啊 QAQ

每月一篇文章任务完成~

tagged: notes

程序员修炼之道

Posted on

每年寒假总是要读完一本书,去年寒假读的是白帽子讲Web安全.今年读的是程序员修炼之道:从小工到专家.去年那时候估计没有搭建好博客. 所以没有写点什么,今年还是写一下读完的感受吧.

这本书说实话我大一就买了,但是那时候没有一点项目经验.读起来宛如天书,没读一半就放弃了.因为那个看起来不知所云的目录让我顿时失去了兴趣. 大一的我确实无知,自负,浮躁,没有认真读下去.当时就沉迷于学习各种编程语言...没有审视到这本书的价值.如今我已大四,也算有了不少项目编程 经验,写过上万行代码,对于这本书的感受就是爱不释手.这本书用非常幽默的方式简述很多朴素而真挚,简单而有效的编程哲学.

全书基本是经验之谈,经验往往不能告诉我什么一定对,但是可以告诉我们什么一定不对.在提示36中讲述了需求之坑.告诉我们一个注重实效的程序员 应该需要挖掘需求而不是搜集它们.确实,客户往往不能很好的阐述需求,需要程序员进行深入挖掘才能使项目顺利进行.文中也举了很简单的例子进行阐述. 我一开始做项目的时候很顽固的要求需要明确的需求文档,现在想想这完全就是个笑话.因为客户自己都不清楚到底要做成什么样子.

本书的涉猎范围很广,也有很多专业名词.每个章节都可以延展出很多内容.我不认为这是一本只需读一遍的书.在每个编程阶段去读这本书都会有不同的感受. 这本书我肯定会珍藏,以后也会反复读的.这是一本程序员必须读的书,希望大家可以耐心的读一下这本书.

这本书的精彩之处只可意会不可言传,本人文采不好,但是极力推荐.最后摘抄下这本书阐述的注重实效的程序员的特征:

  • 早期的采纳者/快速的改编者. 你具有技术和技巧上的直觉,你喜欢试验各种事物.给你一样新东西,你很快能把握它, 并把它与你的知识的其余部分结合在一起.你的自信出自经验.
  • 好奇. 你喜欢提问.那很漂亮--你是怎么做的? 你用那个库时有问题吗?我听说的这个BeOS是什么? 符号链接是怎样实现的?你是收集小知识的林鼠(pack rat), 每一条小知识都可能会影响今后几年里的某项决策.
  • 批判的思考者.你不会不首先抓住事实而照搬别人的说法.当同事说"因为就该那么做"或者供应商允诺为你的全部问题提供解决方案时,你就会嗅到挑战的气息.
  • 有现实感.你会设法理解你面临的每个问题的内在本质.这样的现实主义给了你良好的感知能力:事情有多困难,需要多长时间?让你自己了解某个过程会有困难,或是 要用一点时间才能完成,能够给予你坚持不懈的毅力.
  • 多才多艺. 你尽力熟悉广泛的技术和环境,并且努力工作,以与各种新发展并肩前行.尽管你现在的工作也许只要求你成为某方面的专才,你却总是能够转向新的 领域和新的挑战.

最后,你是一个注重实效的程序员吗?

GUI和CLI的使用比较

Posted on

GUI和CLI也算是使用软件的两大流派吧,Win下几乎都是GUI程序,Linux下几乎都是CLI程序.因为两者面向的用户不同, 初衷也不同.今天看了<程序员修炼之道>这本书,可谓是有了更深入的了解.GUI侧重于易用,CLI则侧重于效率.

对于使用Linux的程序员,工作台就是命令shell.在shell提示下,你可以调用你的全套工具,并使用管道,以这些开发者从未想过的方式 把他们组合在一起.在shell下,你可以启动应用、调试器、浏览器、编辑器以及各种实用程序.你可以搜索文件、查询系统状态、过滤输出. 通过对shell进行编程,你可以构建复杂的宏命令,用来完成你经常进行的各种活动.在shell下面,可以高效率的处理事务,并且shell可以完成 在GUI下几乎所有的任务.GUI和IDE会让程序员变得更懒,更无知,各种辅助工具,例如自动补全可能让某些程序员 连asynchronous这个单词都无法完整的拼写出来.

对于在GUI界面和集成开发环境(IDE)上成长起来的程序员,这似乎显得很极端.毕竟,用鼠标指指点点,你不是也同样能把这些事情做好吗?

简单的回答:"不能".GUI界面很奇妙,对于某些简单操作,它们可能更快、更方便.移动文件、阅读MIME编码的电子邮件以及写信,这些都是你可能想要 在图形环境中完成的事情.但如果你使用GUI完成所有的工作,你就会错过你的环境的某些能力.你将无法使常见任务自动化,或是利用各种可用工具的全部 力量.同时,你也将无法组合你的各种工具,创建定制的宏工具.GUI的好处是WYSIWYG--所见即所得(what you see is what you get).缺点是WYSIAYG-- 所见即全部所得(what you see is all you get).

GUI环境通常受限于它们的设计者想要提供的能力.如果你需要超越设计者提供的模型,你大概不会那么走运--而且很多时候,你确实需要超越这些模型.注重实效的程序员 并非只是剪切代码、或是开发对象模型、或是撰写文档、或是构建过程自动化--所有这些事情我们全都要做.通常,任何一样工具的适用范围都局限于该工具预期要完成的任务. 例如,假定你需要把代码预处理器集成进你的IDE中(为了实现按合约设计、多处理编译指示,等等).除非IDE的设计者明确地为这种能力提供了挂钩,否则,你无法做到这一点.

你也许已经习惯于在命令提示下工作,这种情况下,本文就可以忽略啦~ 否则,你也许还需要我们向你证明,shell是你的朋友.

作为注重实效的程序员,你不断的想要执行特别的操作--GUI可能不支持的操作.当你想要快速地组合一些命令,以完成一次查询或某种其他的任务时,命令行要更为适宜. 例如:

在上周哪些Java 文件没有改动过?

  Shell  find . -name '*.java' -mtime +7 -print  
  GUI    点击并转到"查找文件",点击"文件名"字段,敲入"*.java",选择"修改日期"选项卡
           .然后选择"介于".点击"开始日期",敲入项目开始的日期.点击"结束日期",敲入1周以前
           的日期(确保手边有日历).点击"开始查找".

这样的例子很多.shell命令可能很晦涩,或是太简略,但却很强大,也很简练.同时,因为shell命令可被组合进脚本文件中,你可以构建命令序列,是你常做的事情自动化. 希望你看完这篇文章能逐渐接受CLI并熟悉shell.多用你的命令shell,你会惊讶他能使你的生产率得到怎样的提高.

tagged: notes

关于程序运行效率的一点见解

Posted on

最近在看计算机组成原理等4本考研书籍...发现其实回过头来看这些当年没认真学的书的话还是有不少收获的. 当年小白的我只知道编程看语言类的书籍,无法理解数据结构,计算机组成原理一类书对编程的影响. 刚开始学这本书的时候就感觉我一个搞软件编程的看硬件干嘛.又是指令又是寄存器啥的,还有那可怕的电路图... 看的真头大啊,不过其实想想,这门课要是真没用学校也不会开考研也不会考啊!当年真是图样图森破啊...

大一的时候一直以为搞软件的就要多学点编程语言...我现在倒是学了不少.到头来没一个拿的出手的,还真是可悲 广度是有了但是没深度,同样的语言人家编程风格和效率就是比你高.这个是在什么C#本质论 C++ primer里面学不到的 东西,说深了就是加编程素养.是不断积累的,不是看一本书就能迅速提升的东西.只上过培训班的程序员永远也不可能明白 学习计算机组成原理会对编程有更深入的了解.最终写的代码也只是知其然而不知其所以然.

编程素养是通过各方面计算机知识的综合培养才能得到提高的.接下来我就举个例子来谈谈学过组成原理对编程的理解和 提高.例子很简单,就是关于数组的遍历.一个二维数组,假定是10*10的吧.那么遍历输出的话,大家肯定都会的,很简单.

for(i=0;i<10;i++)
    for(j=0;j<10;j++)
       printf(a[i][j]);

这个是很简单的两层for循环,我写的是伪代码,变量定义输出什么的就不要吐槽了.学过数据结构的同学应该都明白虽然二维 数组都用矩阵来表示,但是实际储存的时候一般都是按照行优先的方式储存的.我的这个版本是先行遍历再列遍历的.

    for(j=0;j<10;j++)
            for(i=0;i<10;i++)
               printf(a[i][j]);

那么先列遍历再行遍历呢?大家一般都会觉得这个其实没什么差别吧.用数据结构的思想来说,空间复杂度和时间复 杂度都是一样的,运行起来没啥区别的.当然对于现在的高速CPU和这个元素比较少的数组来说其实也没多大差别,但是不能 因为这个就去忽略程序的效率问题.

为了讲述这个问题,首先的了解计算机的基本运行原理.首先程序是放在内存中的,但是执行程序需要用CPU来计算.因为CPU运行 速度极快,但是内存速度却没有显著的提高.所以引入了高速缓存也就是Cache,首先把内存中的程序调入高速缓存中.然后CPU再从 Cache中取出数据进行运算.但是因为Cache的容量极小,但是内存很大,根据程序访问的局部性原理.指令通常是顺序存放, 顺序执行的,数据一般也是聚集的存储在一起的.所以调入Cache中的数据基本上会是一段时间内CPU欲调用的数据.

当CPU发出度数据的请求时,如果访问的地址在Cache中即命中,那么直接从Cache中读取数据,和主存无关;如果Cache不命中, 则仍需访问主存,并把此地址所在的块一次性都调入Cache内.

显然,如果命中率高的话,那么程序直接的速度就更快.对比行遍历和列遍历.发现行遍历每次都是按顺序取数据的,因为是按照行优先方式 进行存储的嘛,所以命中率必然高.而列遍历的话每次取的都不是相邻的数据,会导致命中率大大降低,运行效率低.所以一会写程序的话 遇到数组遍历的话肯定要使用行遍历更好.

类似的问题还有GOTO语句,大一的时候老师明令禁止不准在程序中使用GOTO语句,但是就是不说为什么...学过计算机组成原理也就 明白了,因为GOTO语句违反了程序顺序执行的原则,使程序运行效率不高,而且程序读起来也很麻烦.

到此,对编程效率的见解就结束了,如果有什么纰漏的地方,还请大家指正,感谢您能读到最后~

Archlinux下有关无损音乐的折腾

Posted on

最近发现一些旧番的音乐只有无损的了...MP3的已经无法满足大众的需求了么....
但是在Linux下直接用MPD播放无损音乐不能分轨播放实在是不能接受. 于是就开始找寻切割的方法...Archwiki有相关的条目APE+CUE Splitting 但是,有个明显的缺陷是分轨之后的文件居然不是用歌曲名字命名的! 而且还是我不怎么喜欢的wav格式... 不过我还是在菊苣的帮助下找到了完美的解决方案~

无损音乐的分轨

Linux下有个shntool的工具非常实用,可以完美解决无损音乐分轨的问题. 根本不需要wiki里面说的cuebreakpoints
shntool split -f example.cue -t %n_%p_%t -o 'cust ext=mp3 lame --quiet - %f' example.ape
-t指的是文件的标题,%n 代表编号,%p 表示 Performer,%t 表示 Title.都是从cue里面读取的信息,shntool默认不支持 mp3格式的,所以需要指定用lame编码器进行编码.当然很多人只是想分轨,并不想转换成压缩的mp3格式.
shntool split -f example.cue -t %n_%p_%t -o flac example.ape
直接转换成flac格式就好了,因为flac格式是开源的无损音乐格式,播放器对它支持的肯定比较好. 如果出现 warning: failed to read data from input file using format: [ape]
这样的错误可能需要安装mac

给每个音乐文件打TAG

转换之后的文件只是单纯的音乐文件,每个音乐并没有包含想关的TAG信息,这时候需要用cuetag.sh给每个音乐文件打TAG
cuetag.sh脚本 最新版貌似有问题不能使用.
cuetag.sh file.cue *.mp3
命令很简单,在转换好的音乐文件夹下指定cue文件和mp3文件即可自动打TAG,但是需要注意的一点是,这个脚本打的是ID3v1的标签,如果是非英语语言的话, 会出现乱码问题.详情请戳Mp3标签乱码问题分析与解决方案 所以,之后我们还是要使用mp3tagiconv 这个工具来进行标签的转换使得所有的播放器都能够正确识别mp3音乐的标签.
for i in *.mp3; do echo "y"| mp3tagiconv "${i}" ;done
因为mp3tagiconv这个工具每次更新标签都会提示yes or no ,所以我就修改了下执行方式,使得批量自动化修改.flac格式的音乐不存在TAG编码问题.

flac转换为mp3

虽然在PC上听无损比较爽,但是放在手机上受制于存储空间和CPU性能,还是转换成Mp3比较好.
flac -d -c example.flac | lame -q 0 -b 320 - example.mp3
如果想保留flac的回放增益特性的话可以在转换的时候加上
--apply-replaygain-which-is-not-lossless 参数.

到此,折腾完毕...

Archlinux下使用airbase和dhcpd建立虚拟AP

Posted on

Linux下的wicd本身自带一个创建Ad-Hoc network功能的,但是这个功能太不实用了,
首先,Ad-Hoc只能把笔记本的无线通过有线进行共享,而且Ad-Hoc网络无法被安卓原生系统
识别,CM倒是可以用.hostapd也是一样.只能把有线网络通过无线进行共享.
但是我经常是电脑连接WIFI,并且想把WIFI共享给手机进行使用,于是我又开始折腾了 >_>

首先安装所需要的软件

    net-tools
    iptables
    aircrack-ng
    dhcpd

然后启动虚拟AP

    1.airmon-ng start wlp5s0 #启动无线网卡的monitor模式 wlp5s0是我网卡的设备名  
    这时候会看到输出 monitor mode enabled on mon0 mon0便是虚拟出来的一个网卡设备
    2.airbase-ng -e FreeWifi  -v mon0 & 在mon0设备上创建Fake AP  
    -e选项是设置SSID名字 -v是启动DEBUG模式 最后是设备名字,想进行后台运行请按Ctrl+D

激活tap insterface并添加路由表

执行上面的名字之后会输出Created tap interface at0 然后执行下面的命令

    ifconfig at0 up
    ifconfig at0 10.0.0.254 netmask 255.255.255.0
    route add -net 10.0.0.0 netmask 255.255.255.0 gw 10.0.0.254

设置iptables

    iptables --flush
    iptables --table nat --flush
    iptables --delete-chain
    iptables --table nat --delete-chain
    iptables -P FORWARD ACCEPT
    iptables -t nat -A POSTROUTING -o wlp5s0 -j MASQUERADE
    //这里wlp5s0是我额外要连接到互联网的网卡设备名

配置DNS服务器

没有DNS服务器即使能搜到AP也无法连接,因为获取不到IP地址. 首先列出 dhcpd.conf的内容

    ddns-update-style none;
    default-lease-time 600;
    max-lease-time 7200;
    authoritative;
    subnet 10.0.0.0 netmask 255.255.255.0 {
    option subnet-mask 255.255.255.0;
    option broadcast-address 10.0.0.255;
    option routers 10.0.0.254;
    option domain-name-servers 114.114.114.114;
    range 10.0.0.1 10.0.0.140;
    }

然后执行下面的命令

    echo > '/var/lib/dhcp/dhcpd.leases'
    dhcpd -d -f -cf dhcpd.conf at0 &

最后启用IP forwarding

echo "1" > /proc/sys/net/ipv4/ip_forward

写在最后

虽然能创建虚拟AP,但是安卓原生系统连接获取到IP一段时间后 又会自动掉线,原因不明,希望知道的菊苣能告诉咱一声~

Archlinux下Galaxy Nexus刷机并root

Posted on

安卓本来就是Linux衍生的,所以必然可以用Linux系统进行刷机,而且效果比Win还要好.不需要安装驱动,只需要安装adb和fastboot即可.

安装相关依赖

首先通过AUR源安装[android-sdk](https://aur.archlinux.org/packages/android-sdk/和android-sdk-platform-tools相关的wiki在 这里通过yaourt -Ql android-sdk-platform-tools 可以发现adb和fastboot都安装在
/opt/android-sdk/platform-tools 目录下,但是PATH环境变量没有这个值,所以无法在终端里直接调用必须先设置PATH变量 export PATH="${PATH}:/opt/android-sdk/platform-tools"在 .xinitrc里面添加这句就可以使X下的终端PATH环境变量被添加.也可以直接执行,当终端关闭时PATH变量的设置也失效.

配置ADB

前面提供的wiki已经很详细的讲述如何进行ADB的配置了

测试FASTBOOT

fastboot模式(上下音量键和开机键一起按),可以看到一个大大的Start画面,这就是进入了fastboot.或者使用命令adb reboot bootloader。此时把手机用USB连入电脑. 在终端里面输入fastboot devices 如果提示 no permission即是普通用户没有权限。这时候就需要切换到root用户进行执行.如果输入一串数字.则说明系统识别到了你的手机.

刷机开始

首先要下载固件下载下来的tgz文件解压即可看到有个flash-all.sh的脚本。首先 chmod +x flash-all.sh 赋予脚本执行权限,然后执行这个脚本即可,如果有大量输出则说明刷机正常进行中.

后续ROOT

ROOT的话首先需要刷recovery,我们先下载recovery找到自己型号的recovery进行下载,我的是recovery-clockwork-touch-6.0.3.3-maguro.img,touch代表的是是否支持触摸.否则只能用音量键和开机键进行选择和确定.然后下载superSU的ZIP压缩包,目前版本是1.41 ,ZIP格式不要解压.放入SD卡中.adb push UPDATE-SuperSU-v1.41.zip /sdcard/将手机关机进入fastboot模式,应该先解锁, fastboot oem unlock然后刷入recovery fastboot flash recovery recovery-clockwork-touch-6.0.3.3-maguro.img按下音量键调至recovery,按开机键进入,手机会自动重启进入recovery。choose zip from SDcard,然后依次进入,最终选择UPDATE-SuperSU-v1.41.zip,确认安装.

注:如果提示是否清除recovery flash,请选No,否则下次推送时无法进行OTA更新.安装完成后依次返回.这个步骤不会影响OTA更新,并且OTA更新之后会自动解决ROOT的问题,无需再次ROOT.

ADB小技巧

刷机之后需要一大堆APK软件安装怎么办 adb 本身只有单个APK安装的功能,通过 ls -1 *.apk | xargs -l adb install 这个命令可以批量的把一个文件夹下的所以APK安装上去,而且还是静默安装. 不过首先要打开ADB调试才行.

手机MTP连接Linux电脑

MTP是微软发明的东西....当然不好用 目前有两种解决方案一种是GMTP,速度较慢,因为第一次和之后所有操作之后都会全部索引一遍文件列表.第二种是gvfs-mtp结合thunar使用,可以自动挂载,如果无法挂载可以使用 lsusb 命令.

Bus 001 Device 117: ID 04e8:6860 Samsung Electronics Co., Ltd GT-I9100 Phone [Galaxy S II], GT-I9300 Phone [Galaxy S III], GT-P7500 [Galaxy Tab 10.1]

可以看到我的设备号,然后在地址栏里面输入 mtp://[usb:001,117]/。第一个数字是Bus 号,第二个数字是设备号.

Archlinux下终端进行代理的解决方案

Posted on

Linux下软件安装基本都是在终端下进行的....
虽说安装软件基本上选对源就没有无法安装的,但是Archlinux下的yaourt安装就是社区打包的软件了.
软件来源各种各样,有的是Google code上的,有的是GitHub上的,有的就是sourceforge上的了.
但是在中国的大环境下不能保证上面网站的正常访问 QAQ Google的话有hosts的情况下基本上是 把http修改成https就可以下载了,其余两个就只能代理进行下载软件并安装了.

代理

首先科学上网的话就需要代理,SSH这样进行端口监听的比较不错.VPN是全局代理就不用讲述了. 不过VPN也可以走路由表进行部分代理(不在本文讨论范围之内)
SSH的话在Linux下就是简单的一句 ssh -NfD 127.0.0.1:7070 user@server
因为SSH不是HTTP代理,所以需要转发,如果是纯HTTP代理的话直接配置环境变量即可.
前面写的是本机代理的端口,后面是服务器的用户名和服务器IP地址.

转发

SSH是SOCKET5代理,但是下载的话基本上就是HTTP代理 怎么转发呢? 这里就需要一个软件叫做Privoxy Privoxy可以把SOCKET5转HTTP代理,
首先安装它 sudo pacman -S privoxy
然后进行配置 sudo vim /etc/privoxy/config
在配置文件里面添加 forward-socks5 / 127.0.0.1:7070 . 注意后面有个点,容易忽略.
这个是转入的SOCKET5代理,指的是SSH的代理监听的端口号.
配置文件里面有句话是 listen-address 127.0.0.1:8118 这个是转发出去的HTTP代理的端口号
这样写好之后, 启动服务 sudo systemctl start privoxy.service

配置环境变量

转发实现之后就可以应用在终端里面了, 在终端里面输入

export https_proxy=127.0.0.1:8118
export http_proxy=127.0.0.1:8118

这样就完成代理的设置了.

Archlinux下安装过wps之后字体发虚的问题

Posted on

Office是Linux永远的痛...微软的Office市场霸主地位是根本无法撼动的...
但是微软那家伙很讨厌Linux和开源... Office产品目前也只有Windows和Mac 据说微软要出Linux版本的Office...但是不知道猴年马月啊 (望天

之所以字体发虚是因为安装WPS之后的方正字体使得Sans和Sans-Serif字体的中文部分默认指向了方正宋体.所以导致个这个问题的发生...效果如下图:

左边是微米黑,右边是装过WPS之后映射的宋体字.... 明显右边太难看了有木有!!!
解决方案是自己编写 fonts.conf设置字体的优先级...
文泉驿提供了一个在线配置生成工具 这个网页有部分JS资源被墙了,所以需要科学上网才能正常使用,需要注意.
配置完成之后点上面的“生成[create]”按钮,将结果复制保存到~/.fonts.conf就能覆盖系统的字体选择顺序. 注意上面的方法在Archlinux下会出现警告错误提示是过时的使用方法.
正确的解决办法是把配置文件写在 /etc/fonts/conf.avail/50-user.conf 这样就可以完美解决字体发虚的问题了.

下面贴一下我的配置方案:

<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
<!-- created by WenQuanYi FcDesigner v0.5 -->
<match>
    <test name="family"><string>Arial</string></test>
    <edit name="family" mode="prepend" binding="strong">
        <string>文泉驿微米黑</string>
            <!-- Please install 文泉驿微米黑 first -->
        <string>文泉驿等宽微米黑</string>
        <string>DejaVu Sans</string>
        <string>DejaVu Sans</string>
        <string>WenQuanYi Micro Hei</string>
            <!-- Please install WenQuanYi Micro Hei first -->
        <string>WenQuanYi Zen Hei</string>
        <string>Liberation Sans</string>
        <string>Droid Sans</string>
        <string>WenQuanYi Bitmap Song</string>
            <!-- Please install WenQuanYi Bitmap Song first -->
        <string>Cantarell</string>
            <!-- Please install Cantarell first -->
        <string>DejaVu Sans Condensed</string>
        <string>DejaVu Sans Light</string>
        <string>DejaVu Sans Mono</string>
        <string>DejaVu Serif</string>
        <string>DejaVu Serif Condensed</string>
        <string>方正书宋_GBK</string>
        <string>方正仿宋_GBK</string>
        <string>方正姚体_GBK</string>
        <string>方正宋体S-超大字符集</string>
        <string>方正宋体S-超大字符集(SIP)</string>
            <!-- Please install 方正宋体S-超大字符集(SIP) first -->
        <string>方正小标宋_GBK</string>
        <string>方正楷体_GBK</string>
        <string>方正细黑一_GBK</string>
        <string>方正行楷_GBK</string>
        <string>方正超粗黑_GBK</string>
        <string>方正隶书_GBK</string>
        <string>方正魏碑_GBK</string>
        <string>方正黑体_GBK</string>
    </edit>
</match>    
<match>
    <test name="family"><string>sans-serif</string></test>
    <edit name="family" mode="prepend" binding="strong">
        <string>文泉驿微米黑</string>
            <!-- Please install 文泉驿微米黑 first -->
        <string>文泉驿等宽微米黑</string>
        <string>DejaVu Sans</string>
        <string>DejaVu Sans</string>
        <string>WenQuanYi Micro Hei</string>
            <!-- Please install WenQuanYi Micro Hei first -->
        <string>WenQuanYi Zen Hei</string>
        <string>Liberation Sans</string>
        <string>Droid Sans</string>
        <string>WenQuanYi Bitmap Song</string>
            <!-- Please install WenQuanYi Bitmap Song first -->
        <string>Cantarell</string>
            <!-- Please install Cantarell first -->
        <string>DejaVu Sans Condensed</string>
        <string>DejaVu Sans Light</string>
        <string>DejaVu Sans Mono</string>
        <string>DejaVu Serif</string>
        <string>DejaVu Serif Condensed</string>
        <string>方正书宋_GBK</string>
        <string>方正仿宋_GBK</string>
        <string>方正姚体_GBK</string>
        <string>方正宋体S-超大字符集</string>
        <string>方正宋体S-超大字符集(SIP)</string>
            <!-- Please install 方正宋体S-超大字符集(SIP) first -->
        <string>方正小标宋_GBK</string>
        <string>方正楷体_GBK</string>
        <string>方正细黑一_GBK</string>
        <string>方正行楷_GBK</string>
        <string>方正超粗黑_GBK</string>
        <string>方正隶书_GBK</string>
        <string>方正魏碑_GBK</string>
        <string>方正黑体_GBK</string>
    </edit>
</match>
<match>
    <test name="family"><string>serif</string></test>
    <edit name="family" mode="prepend" binding="strong">
        <string>文泉驿微米黑</string>
            <!-- Please install 文泉驿微米黑 first -->
        <string>文泉驿等宽微米黑</string>
        <string>DejaVu Sans</string>
        <string>DejaVu Serif</string>
        <string>WenQuanYi Bitmap Song</string>
            <!-- Please install WenQuanYi Bitmap Song first -->
        <string>AR PL UMing CN</string>
            <!-- Please install AR PL UMing CN first -->
        <string>AR PL SungtiL GB</string>
            <!-- Please install AR PL SungtiL GB first -->
        <string>WenQuanYi Zen Hei Sharp</string>
            <!-- Please install WenQuanYi Zen Hei Sharp first -->
        <string>AR PL UMing TW</string>
            <!-- Please install AR PL UMing TW first -->
        <string>Liberation Serif</string>
            <!-- Please install Liberation Serif first -->
        <string>Bitstream Charter</string>
            <!-- Please install Bitstream Charter first -->
        <string>Droid Serif</string>
            <!-- Please install Droid Serif first -->
        <string>Cantarell</string>
            <!-- Please install Cantarell first -->
        <string>DejaVu Sans Condensed</string>
        <string>DejaVu Sans Light</string>
        <string>DejaVu Sans Mono</string>
        <string>DejaVu Serif</string>
        <string>DejaVu Serif Condensed</string>
        <string>方正书宋_GBK</string>
        <string>方正仿宋_GBK</string>
        <string>方正姚体_GBK</string>
        <string>方正宋体S-超大字符集</string>
        <string>方正宋体S-超大字符集(SIP)</string>
            <!-- Please install 方正宋体S-超大字符集(SIP) first -->
        <string>方正小标宋_GBK</string>
        <string>方正楷体_GBK</string>
        <string>方正细黑一_GBK</string>
        <string>方正行楷_GBK</string>
        <string>方正超粗黑_GBK</string>
        <string>方正隶书_GBK</string>
        <string>方正魏碑_GBK</string>
        <string>方正黑体_GBK</string>
    </edit>
</match>
<match>
    <test name="family"><string>monospace</string></test>
    <edit name="family" mode="prepend" binding="strong">
        <string>文泉驿微米黑</string>
            <!-- Please install 文泉驿微米黑 first -->
        <string>文泉驿等宽微米黑</string>
        <string>DejaVu Sans</string>
        <string>WenQuanYi Zen Hei Mono</string>
            <!-- Please install WenQuanYi Zen Hei Mono first -->
        <string>WenQuanYi Micro Hei Mono</string>
        <string>DejaVu Sans Mono</string>
        <string>Droid Sans Mono</string>
            <!-- Please install Droid Sans Mono first -->
        <string>WenQuanYi Zen Hei Sharp</string>
            <!-- Please install WenQuanYi Zen Hei Sharp first -->
        <string>Liberation Sans Mono</string>
            <!-- Please install Liberation Sans Mono first -->
        <string>AR PL UMing TW</string>
            <!-- Please install AR PL UMing TW first -->
        <string>Cantarell</string>
            <!-- Please install Cantarell first -->
        <string>DejaVu Sans Condensed</string>
        <string>DejaVu Sans Light</string>
        <string>DejaVu Sans Mono</string>
        <string>DejaVu Serif</string>
        <string>DejaVu Serif Condensed</string>
        <string>方正书宋_GBK</string>
        <string>方正仿宋_GBK</string>
        <string>方正姚体_GBK</string>
        <string>方正宋体S-超大字符集</string>
        <string>方正宋体S-超大字符集(SIP)</string>
            <!-- Please install 方正宋体S-超大字符集(SIP) first -->
        <string>方正小标宋_GBK</string>
        <string>方正楷体_GBK</string>
        <string>方正细黑一_GBK</string>
        <string>方正行楷_GBK</string>
        <string>方正超粗黑_GBK</string>
        <string>方正隶书_GBK</string>
        <string>方正魏碑_GBK</string>
        <string>方正黑体_GBK</string>
    </edit>
</match>
</fontconfig>

Sublime使用笔记以及插件推荐

Posted on

自从换了Linux就摆脱了笨重的VS这个强大的开发工具,自动提示,自动纠错.... VS为我提供了太多功能,已经把我宠坏了...以至于普通的编辑器我都无法习惯... 这样可不是什么好习惯,不过Sublime Text 2 是个很好的编辑器,特别是它的插件特别 丰富提供了很多实用的功能.于是在Linux下前端开发基本就是实用它了. 虽说是收费软件,但是个人用户可以无限期的免费试用,只不过经常提示购买罢了...

基础配置

个人喜欢亮色系的输入环境于是换成Dawn主题,并且修改了字体和大小

    {
        "color_scheme": "Packages/Color Scheme - Default/Dawn.tmTheme",
        "font_face": "Deja Vu Sans Mono",
        "font_size": 11.5,
        "ignored_packages":
        [
            "Vintage"
        ]
    }

输入法问题解决

如果大家和我一样使用的输入法是fcitx的话可以使用fcitx官方提供的解决办法Hall_of_Shame_for_Linux_IME_Support 按照那个帖子三个步骤便可以成功调出输入法.

常用插件推荐

Package Control

Package Control 是用来管理 Sublime Text 2 的插件的插件. 也是装完后第一个要安装的插件. 首先打开 console, 并在打开的 Sublime console 中输入:

import urllib2,os; pf='Package Control.sublime-package'; ipp=sublime.installed_packages_path(); os.makedirs(ipp) if not os.path.exists(ipp) else None; urllib2.install_opener(urllib2.build_opener(urllib2.ProxyHandler())); open(os.path.join(ipp,pf),'wb').write(urllib2.urlopen('http://sublime.wbond.net/'+pf.replace(' ','%20')).read()); print 'Please restart Sublime Text to finish installation'

ZenCoding

Zen Coding是为了高速HTML编程和编辑开发的插件。这个插件的核心是一个超级强大的缩略词引擎,允许你扩展表达式到HTML代码中,类似于CSS的选择器。 通过 Ctrl+, 进行代码展开

Zen Coding现在已经被Emmet替代(Zen Coding已经从Sublime Text 2的插件源中移除),直接在编辑器内输入表达式,按下Tab即可扩展。

jQuery Package for Sublime Text

这个毫无疑问,没有jQuery我们还能干什么?绝对不错的jQuery开发插件,个人来说非常喜欢.

JS Format

JS format是Sublime Text 2的一个javascript的格式化插件,使用来自JS beautifier的命令行/python模块的javascript格式来格式化选择的文本,或者整个文件. 不过貌似格式化部分文本会出现问题

Sublime CodeIntel

是一个想法来自于Komodo编辑器的智能插件. 帮助你实时展示存在模块的自动补齐信息,同时也展示目前方法的信息到状态栏中. 自动补全超赞的!

Tag

当你需要处理标签时,Tag是一个伟大的插件.

解决中文编码问题

ConvertToUTF8 Codecs26 同时装这两个插件就可以正常打开GBK文件并且保存.

一些编码规范

有时候会设置保存文件时删除多余的空白字符并且在文件结尾添加空白行

打开Preferences->Settings-Default设置

    "trim_trailing_white_space_on_save": true,
    "ensure_newline_at_eof_on_save": true,
tagged: sublime

Ubuntu下apache2的配置记录

Posted on

自从2012年四月买VPS...到现在已经算是一年多了吧.在此感谢琴对我的帮助和指导.没有琴的指导我也不会折腾和配置VPS. 期间一点一点的成长都离不开大家的帮助.不过对琴的过度依赖,使我一度成为伸手党...真是十分抱歉 QAQ 现在写一篇日志记录下apache2的配置方法也可以留着自己以后进行参考.

安装apache套装

首先安装lamp sudo apt-get install lamp-server^
PHP开发和服务器运行环境首选LAMP组合,即Linux+Apache+Mysql+Php/Perl/Python,能最优化服务器性能

开启相关模块

启用 mod_rewrite 模块
sudo a2enmod rewrite
启用 mod_proxy 模块
sudo a2enmod proxy
sudo a2enmod proxy_http
启用 SSL 模块
sudo a2enmod ssl
a2xx系列的命令共有a2dismod、a2enmod、a2dissite、a2ensite,作用分别是禁用模块、启用模块、停用站点、启用站点这些命令简单得连--help选项都没有,只能运行看提示。其作用也很简单,就是在/etc/apache2/mods-enabled和/etc/apache2/sites-enabled里面建立或删除相对应的x-available目录里面的模块的链接。

具体相关配置

服务器资源一般很高贵,大家都会挂很多独立站点的.apache2默认存放网站的目录是 /var/www 配置文件储存在 /etc/apache2 主配置文件在 apache2.conf 其中这个配置文件包含了 sites-enabled 目录 这个目录是主要储存配置文件的地方 配置文件以 000-default 000-default-ssl 这种命名方式进行命名. 这两个文件是对http 和 https 进行默认配置.下面我们来看看这两个默认配置 vim 000-default

<VirtualHost *:80>
ServerAdmin acgotaku311@email.me
    ServerName _default_
DocumentRoot /var/www/default
<Directory />
    Options FollowSymLinks
    AllowOverride None
</Directory>
<Directory /var/www/default>
    Options Indexes FollowSymLinks MultiViews
    AllowOverride None
    Order allow,deny
    allow from all
</Directory>

ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
<Directory "/usr/lib/cgi-bin">
    AllowOverride None
    Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
    Order allow,deny
    Allow from all
</Directory>

ErrorLog ${APACHE_LOG_DIR}/error.log

# Possible values include: debug, info, notice, warn, error, crit,
# alert, emerg.
LogLevel warn

CustomLog ${APACHE_LOG_DIR}/access.log combined

Alias /doc/ "/usr/share/doc/"
<Directory "/usr/share/doc/">
    Options Indexes MultiViews FollowSymLinks
    AllowOverride None
    Order deny,allow
    Deny from all
    Allow from 127.0.0.0/255.0.0.0 ::1/128
</Directory>

</VirtualHost>

第一行指定的是服务使用的端口 第二行 ServerAdmin 写的是服务器管理员的邮箱 第三行 ServerName 写的是服务器的名字 default 代表默认使用这个即服务器名字找不到对应的配置文件时时候此配置文件 第四行 DocumentRoot 写的是网站的根目录,即输入网址映射到服务器具体的哪个站点 Directory子节点是对具体目录进行配置 我们主要是对网站根目录进行权限的配置 其中主要说明的是 AllowOverride 选项 AllowOverride控制那些被放置在.htaccess文件中的指令 其余的配置基本不需要关心.

vim 000-default-ssl

<IfModule mod_ssl.c>
<VirtualHost *:443>
ServerAdmin acgotaku311@email.me
    ServerName  _default_
DocumentRoot /var/www/default
<Directory />
    Options FollowSymLinks
    AllowOverride None
</Directory>
<Directory /var/www/default>
    Options Indexes FollowSymLinks MultiViews
    AllowOverride All
    Order allow,deny
    allow from all
</Directory>

ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
<Directory "/usr/lib/cgi-bin">
    AllowOverride None
    Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
    Order allow,deny
    Allow from all
</Directory>

ErrorLog ${APACHE_LOG_DIR}/error.log

# Possible values include: debug, info, notice, warn, error, crit,
# alert, emerg.
LogLevel warn

CustomLog ${APACHE_LOG_DIR}/ssl_access.log combined

Alias /doc/ "/usr/share/doc/"
<Directory "/usr/share/doc/">
    Options Indexes MultiViews FollowSymLinks
    AllowOverride None
    Order deny,allow
    Deny from all
    Allow from 127.0.0.0/255.0.0.0 ::1/128
</Directory>

#   SSL Engine Switch:
#   Enable/Disable SSL for this virtual host.
SSLEngine on

SSLCertificateFile    /etc/apache2/ssl/ssl.crt
SSLCertificateKeyFile /etc/apache2/ssl/ssl.key


<FilesMatch "\.(cgi|shtml|phtml|php)$">
    SSLOptions +StdEnvVars
</FilesMatch>
<Directory /usr/lib/cgi-bin>
    SSLOptions +StdEnvVars
</Directory>

BrowserMatch "MSIE [17-9]" ssl-unclean-shutdown

</VirtualHost>
</IfModule>

SSL模块就是多了一个证书的设置 其余的基本不变.

其它站点基本就是拷贝默认的配置文件修改 ServerName 和 DocumentRoot 即可.

关于SSL证书

生成 SSl证书命令

openssl genrsa -des3 -out ssl.key 1024

然后他会要求你输入这个key文件的密码。不推荐输入。 但是生成时候必须输入密码。你可以输入后 再删掉。

mv ssl.key xxx.key  
openssl rsa -in xxx.key -out ssl.key  
rm xxx.key

然后根据这个key文件生成证书请求文件
openssl req -new -key ssl.key -out ssl.csr
以上命令生成时候要填很多东西 一个个看着写吧(可以随便,毕竟这是自己生成的证书) 最后根据这2个文件生成crt证书文件

openssl x509 -req -days 365 -in ssl.csr -signkey ssl.key -out ssl.crt

这里365是证书有效期 推荐3650哈哈。这个大家随意。最后使用到的文件是key和crt文件。

这里生成的证书是不受信任的...我们可以在[StartSSL(http://www.startssl.com/)上 申请免费的证书.不过免费时间是一年,并且只能对根域名和一个子域名生效.不过一年后可以重新 注册,所以可以永远对根域名和一个子域名设置受信任的SSl证书. 相关的申请教程在此点我

关于微云下载的那点事

Posted on

貌似腾讯公司出品了一个叫微云的东东,说实话,要不是基家上某人告诉我我还真不知道== 然后就说说这个东西的事情了,貌似下载的时候只能给出下载页面的链接...故意把真实的下载地址隐藏了,而且分析到下载地址还不行 还得带cookies才行....咳咳,我就不说啥腾讯的坏话了,大家都懂的.然后就是下载页面上还有个举报按钮!我勒个去...分享河蟹的东西 的时候就经常被爆菊了...

于是需求就来了,分析下载页面得出真正的下载地址和cookies,让用户点击下载链接即可下载,既方便用户又免得被爆菊... 然后看了下页面的JS... qqdisk_web_outlink.js qqdisk_web_wy.js qqviplib_2.0.0.js 三个主要的JS都压缩了 看着就想吐... 于是换了一个思路,F12开启chromium的调试工具,在Network面板下查看网页到底和服务器发生了哪些交互 chromium的开发工具非常明确的红色的标注了发生POST请求的链接.一眼就看到了.测试用下载链接 在这个页面载入的时候可以很清楚的看到 POST请求 网页对http://web.cgi.weiyun.com/wy_web.fcg这个网址进行POST请求,提交的表单是个json对象

    {
        "req_header": {
            "proto_ver": 10006,
            "main_v": 12,
            "sub_v": 1,
            "encrypt": 0,
            "msg_seq": 1,
            "source": 30006,
            "token": "4d3754f563ad04a56fece81bbcc83302",
            "client_ip": "127.0.0.1",
            "cmd": "decode_url"
        },
        "req_body": {
            "url": "Q69+qVhF33RXX+jAvhRil4kmLW5GIZRW8Zd13MWsUyvzpcDLunyFPxcqaLdUNtq5FfLp9Oj65Xr2XxTn298qsOWHWHqVdGuP7q1xMT4Do/y34dP00q4H7gpr94udJr/d2H23l0QGIKteptOEY1bTYLwkc+BRRdYEFvNb36XnUMBrS8pMf8RqoLJiAPrbXgYeBFvAwJu13QPHiQOe2lxS2i+V7/UDRpJr2qz8FqnOTHSQhaNXD+8s7uZIyaWH8INMc1Ls9Ay1XOc="
        }
    }

上面的JSON对象里面只有token和url是可变的,其它都是固定值,仔细分析页面上的那三个压缩过的JS便可以得到答案. url明显就是网址上问号后面的参数,我就不赘述了.关于token,仔细分析发现 var _token=QQVIP.security.getAntiCSRFToken(); 了这段代码,也就是说腾讯还专门防止CSRF(跨站点请求伪造),可是... 这个token值我随便填写也能成功返回结果 == 看来腾讯的程序员偷工减料了(又是临时工的错!) 只能说这里预留了一个接口,但是没做,仔细查了一下腾讯的那个函数并自己修改了一下

    var CONST_SALT = 5381;
    var CONST_MD5_KEY = 'tencentQQVIP123443safde&!%^%1282';
    function getAntiCSRFToken(objConfig) {
        objConfig = objConfig || {};
        var salt = objConfig.salt || CONST_SALT;
        var skey = objConfig.skey || "";
        //QZFL.cookie.get("skey")
        var md5key = objConfig.md5key || CONST_MD5_KEY;
        var hash = [],
            ASCIICode;
        hash.push((salt << 5));
        for (var i = 0, len = skey.length; i < len; ++i) {
            ASCIICode = skey.charAt(i).charCodeAt();
            hash.push((salt << 5) + ASCIICode);
            salt = ASCIICode;
        }
        var md5str = $.md5(hash.join('') + md5key);
        return md5str;
    }

看来做的还是不错的,用cookies生成token,但是你的"skey"的cookies是空的要闹哪样 == 基本上token就是个定值 "4d3754f563ad04a56fece81bbcc83302" 接下来看返回回来的json对象 POST返回的JSON对象

{
    "rsp_body": {
        "dk": "3540da0c9b7db5fb4f26f2baea50ef60",
        "dl_cookie_name": "FTN5K",
        "dl_cookie_value": "8eb5b2ee",
        "dl_encrypt_url": "da42a15644a68baa30e174d7ed580165176d66c912d9eb235bb4d01363a32e739fc68cab7402869010e99ab64d899c2dd537b2ffc39e3464732acc7ac1413ec4",
        "dl_remain": 99997,
        "dl_svr_host": "hz.yun.ftn.qq.com",
        "dl_svr_port": 80,
        "eptm": "1398678922",
        "fd": "6385d50b-1be5-436a-8430-43f53b583922",
        "nickname": "─╄OvЁ1颗心只为ㄚòu",
        "nm": "CC女王.ssf",
        "realnm": "CC女王.ssf",
        "sha": "f9800d2bd0fa71c2506c5c305c2b50988eb5b2ee",
        "shorturl": "http://url.cn/A8Clnw",
        "sz": "145195",
        "uin": 215629877
    },
    "rsp_header": {
        "cmd": "decode_url",
        "msg_seq": 1,
        "ret": 0,
        "uin": 215629877
    }
}

这里我只关心下载有关的参数,其它参数大家可以自行猜测,dl_cookie_name和dl_cookie_value 是所需要的cookies的键值对.
"http://"+json.rsp_body.dl_svr_host+":"+json.rsp_body.dl_svr_port+ "/ftn_handler/"+json.rsp_body.dl_encrypt_url+"/"+json.rsp_body.realnm;
这就是根据JSON拼接出来的下载链接 接下来的问题就是cookies跨越问题了,点击页面上的下载按钮可以发现,页面与服务器又发生了交互

页面向http://web.weiyun.qq.com/php/downloadCheck.php提交了downloadn=FTN5K&downloadv=8eb5b2ee这个正是cookies的键值对,后面的callback是JQUery回调的参数 ,无视就可以了,关键是服务器返回的
Respose Headers
Set-Cookie:FTN5K=8eb5b2ee; expires=Mon, 29-Apr-2013 11:51:48 GMT; path=/; domain=qq.com
这样就写入了cookies,因为是GET方法提交的请求,这样就可以使用跨域请求.完成cookies的跨越注入.具体使用方法是

    var downloadcookie = "http://web.weiyun.qq.com/php/downloadCheck.php?downloadn=" + json.rsp_body.dl_cookie_name + "&downloadv=" + json.rsp_body.dl_cookie_value;
    var d = document;
    var s = d.createElement('script');
    s.src = downloadcookie;
    d.body.appendChild(s)

通过src可以向http://web.weiyun.qq.com发送GET请求,就完成了跨越的cookies注入.注意跨越请求只能支持GET方法,POST是绝对不可能实现跨越请求的. 但是调试的时候需要POST跨越请求怎么办,最简单的办法是在启动浏览器的时候添加启动方法 chromium --disable-web-security 这样浏览器就不会阻止 跨越的POST请求,否则就会console出现下面的红色错误提示.

XMLHttpRequest cannot load http://web.cgi.weiyun.com/wy_web.fcg. Origin null is not allowed by Access-Control-Allow-Origin.

这样就下载页面分析下载链接就完成了,然后基友说微云的下载链接有两种还有一中是这个 不是outlink而是sharekey了,其实就是格式变了而已....腾讯也不想在写一套解析办法吧 == 提交的JSON对象变成

    {
        "req_header": {
            "net_type": 3,
            "build_v": 123,
            "proto_ver": 10006,
            "main_v": 12,
            "sub_v": 1,
            "encrypt": 0,
            "msg_seq": 1,
            "source": 30006,
            "token": "4d3754f563ad04a56fece81bbcc83302",
            "client_ip": "127.0.0.1",
            "cmd": "get_share_info",
            "uin": 0
        },
        "req_body": {
            "share_key": "a86487d6aa48e33429e0be477a87dc21"
        }
    }

然后提交的网址变成http://web.cgi.weiyun.com/wy_share.fcg
返回的JSON对象是:

{
    "rsp_body": {
        "createtime": "2013-04-25 13:50:09",
        "data": "xfjJXsNsySkhhwT5SvFC7j85IjXqTxmV9RBC+mcIk1MOmMi0G68fkDTQulEHnBwSPO3Zs/6oAR/k5WLMkpiezA2NFpoCI1LRe2vrko9mMVrP1PcrGnJY26n7Iogto20Wq6aIBm7VjNI9+D2TmXWw4LodHzIf4VhMMGRDacvH04yd4+W/fwG6BbpQKOma42CbA4d8OGken8hNFnnxjE5QIO5GKCPjgsKpxQtPsM0nNbgvKfnLdcPrvcnT+Dm5ZQbZnkdj022QE2ZzXGNfX0c1IA==",
        "dir_list": [],
        "dl_cookie_name": "FTN5K",
        "dl_cookie_value": "2bc024b4",
        "dl_encrypt_url": "a85801ad13f6c92de0de07f0b0c1002d11b438dcfe7a63de37dce08e9a0db51b0789677db4a256a57085b66b52ebe680ab796e9f5c844e620884a443b397339e",
        "dl_svr_host": "tj.yun.ftn.qq.com",
        "dl_svr_port": 80,
        "downcnt": 19,
        "file_list": [{
            "file_id": "2bf60c0a-8694-4d08-bcb3-90e267b32691",
            "file_name": "逆転電池.rar",
            "file_size": "7163756"
        }],
        "flag": 1,
        "pdir_key": "9c9bbd18f0a739ae12b58dcd423dce4a",
        "ppdir_key": "9c9bbd18f338fdcb41c5dfa52b9ed888",
        "res_type": 0,
        "sharename": "逆転電池.rar",
        "storecnt": 0,
        "uin": 415079324,
        "url": "http://url.cn/BKueJM",
        "viewcnt": 83
    },
    "rsp_header": {
        "cmd": "get_share_info",
        "ret": 0
    }
}

这样就是真的完工了~

使用GRUB2引导ISO镜像

Posted on

首先来说下什么是GRUB,熟悉Linux的同学都应该知道的说.它是一个多重操作系统启动管理器。用来引导不同系统,如windows,linux。 GRUB2就是GRUB的下一个版本.首先说下GRUB的作用吧,当你按下电脑上的电源按钮的时候,这时候电脑会开始加载BIOS,BIOS首先检查计算机硬件 能否满足运行的基本条件,这叫做"硬件自检"(Power-On Self-Test).自检完成之后,BIOS就会把系统控制权转交给下一阶段的启动程序,一般情况下 都会是硬盘,由硬盘启动时,BIOS通常是转向第一块硬盘的第一个扇区,即主引导记录(MBR).

装载GRUB和操作系统的过程,包括以下几个操作步骤:

    1、装载记录
    基本引导装载程序所做的唯一的事情就是装载第二引导装载程序。
    2、装载Grub
    这第二引导装载程序实际上是引出更高级的功能,以允许用户装载一个特定的操作系统。
    3、装载系统
    如linux内核。GRUB把机器的控制权移交给操作系统。
    不同的是,微软操作系统都是使用一种称为链式装载的引导方法来启动的,
    主引导记录仅仅是简单地指向操作系统所在分区的第一个扇区。

这时候就发挥GRUB2的作用了,根据不同的选项可以加载不同的操作系统,甚至可以加载ISO镜像.我此次折腾GRUB2就是为了免去每次安装系统的时候都去使用DD 刻录镜像的麻烦.然后写下来记录自己的一点成果. 下面就来讲解下如何使用GRUB2去引导ISO镜像

安装GRUB2到U盘上

准备好一个U盘,在Linux的操作环境下进行安装,确保自己的系统上已经安装过GRUB2,如果没安装的话,请自行安装,Arch下安装 pacman -S grub-bios

1.在终端下输入lsblk查看你自己的U盘是哪个设备,例如我的是sdc1
2.在/mnt下新建一个文件夹 并挂载上U盘 mkdir /mnt/USB && mount /dev/sdc1 /mnt/USB
3.安装GRUB2到U盘上 grub-install --force --no-floppy --root-directory=/mnt/USB /dev/sdc
4.这是GRUB2应该安装到U盘上了,如果U盘里没有boot文件夹可以直接copy系统的boot文件夹放进里面即可

配置GRUB2

GRUB2的配置文件就是boot/grub/grub.cfg文件

从Archlinux-x86_64启动

menuentry "Archlinux-x86_64.iso" --class iso {
  set isofile="/archlinux-2013.04.01-dual.iso"
  loopback loop (hd0,1)$isofile
  linux (loop)/arch/boot/x86_64/vmlinuz archisolabel=ARCH_201304 img_dev=/dev/disk/by-uuid/DB7B-2C3D img_loop=$isofile earlymodules=loop
  initrd (loop)/arch/boot/x86_64/archiso.img
}

添加上面这段代码即可,首先要把Archlinux的安装镜像拷贝到U盘里面,第二行代码的意思就是设置isofile所在的位置,第二行是挂载iso镜像 hd(0,1) 的意思是IDE硬盘用hd表示,这里也可以表示U盘,0表示第一块磁盘,从0开始计数.1代表的是第一块分区,这里是从1开始计数.因为U盘引导的话第一块磁盘肯定是 U盘,所以前面的肯定是0,后面的1代表的是分区,如果你的分区是从其它数字开始的就做相应修改.第四行是加载内核,也就是ios里面的内核所在路径,archisolabel代表的 是虚拟挂载的时候的卷标,注意这个一定不能和你现有的硬盘或者U盘的卷标重复,否则会出现错误的.img_dev表示的是镜像所在的设备位置,这里使用uuid来表示,因为uuid是 U盘的固有属性,一般不会发生变化(除非你格式化U盘),所以保证一定能找到设备.
查看uuid的命令是 ls /dev/disk/by-uuid/ -al 查看卷标的命令是 ls /dev/disk/by-label -al initrd 是使用户能够指定一个在引导时可用的初始RAM盘。当内核为了完全引导而需要某些模块时,这是必需的。 还有另外一种启动方法

menuentry "Archlinux-x86_64.iso" --class iso {
  set isofile="/archlinux-2013.04.01-dual.iso"
  search -s -f -n /archlinux-2013.04.01-dual.iso
  probe --set=DB7B-2C3D -u $root
  loopback loop /archlinux-2013.04.01-dual.iso
  linux (loop)/arch/boot/x86_64/vmlinuz archisolabel=ARCH_201304 img_dev=/dev/disk/by-uuid/DB7B-2C3D  img_loop=$isofile earlymodules=loop
  initrd (loop)/arch/boot/x86_64/archiso.img
}

从Archlinux-i686启动

和上面同理可得

menuentry "Archlinux-i686.iso" --class iso {
  set isofile="/archlinux-2013.04.01-dual.iso"
  loopback loop (hd0,1)$isofile
  linux (loop)/arch/boot/i686/vmlinuz archisolabel=ARCH_201304 img_dev=/dev/disk/by-uuid/DB7B-2C3D img_loop=$isofile earlymodules=loop
  initrd (loop)/arch/boot/i686/archiso.img
}

用GRUB2启动其它ISO镜像

其它ISO启动链接
有关启动Win系列的镜像可以参考这个链接启动win镜像 上面链接说明了GRUB2使用ISO直接启动的原理,GRUB2只能读取ISO9660格式的镜像,只不过GRUB2是把ISO当作一个虚拟的CD罢了. 但是win7的安装镜像是ISO-13346 "UDF"格式的,也就是说GRUB2无法读取Win7镜像,所以使用ISO直接启动win7是不可能的事情了.

安徽师范大学校园视频下载器2.0版本发布

Posted on

安师大的校园视频网是个资源不错但软件坑爹的东西. 当然这个服务不是安师大自己研发的,是由沈阳光音网视科技有限公司开发的,应该是学校出钱购买的吧.不过那个坑爹的客户端只给在线观看不给下载, 网页也是完美的兼容IE6,Javascript 里面也调用了好多微软自己的ActiveX控件,所以只能用学校提供的自带的那个客户端浏览和观看视频.

对于这种反人类的做法我当然是很不爽了,客户端的网址栏虽然是不变的,不过是通过重定向的方式访问学校的服务器了吧,通过修改hosts我也可以直接在 IE浏览器中查看,然后查看源代码进行了分析工作.原理其实很简单,就是通过解析服务器上的XML文件进行网页的渲染,XML文件里面记录了服务器上所有的 视频资源和视频的ID,然后通过一步步的研究直接找到了资源的下载地址.详细的原理请看我github上公开的VideoSeach 里面的Function.cs记录了具体解析过程.

通过研究知道了原理便和同学商量这开发个下载器方便大家使用,Win版本是由同学基于.Net Framework 2.0开发的,由于我的特殊需求变移植了 web版本,1.0版本是在上学期开发并发布的.现在软件使用的用户大概有一万人左右,因为很多学校也用的这个视频服务提供商,我们变提供了配置按钮可以满足 各个学校的需求.不过寒假回来发现下载器不能再下载视频了,我便抽出一晚上时间研究了原因,发现是学校的服务器屏蔽了下载器的功能.手法也很简单使用 User-Agent: Novasoft NetPlayer/4.0 进行屏蔽非官方客户端,知道原因事情就很简单了,伪造UA嘛,之前下载器是调用迅雷下载的,现在在里面内置了下载功能.

软件官网: www.icehoney.me

百度云盘下载:Download

软件截图: 安师大校园视频下载器

tagged: notes

关于CSS解决高度自适应和宽度自适应的问题

Posted on

今天在做一个地图的web应用,这是个大坑...首先开始设计首页,于是开始山寨谷歌地图了 =.= 设计出来基本的div框架之后,想做到地图界面宽度和高度自动适应浏览器.之前做网站 的需求都是固定宽度和高度的.于是我变开始折腾了.

自适应宽度

自适应宽度的难处在于网页里有超过两个并列的div,如果两列都是变宽的话就直接按比例设置 就好了,关键在于1列需要固定宽度另外一列需要自适应.我遇到的就是后者,具体情况是第一列需 要固定宽度350px,第二列的宽度是100%-350px,最简单的方法是设置第二列的宽度是width: -webkit-calc(100% - 350px); 但是这个方法的适用性不太好,由于我用的Archlinux,也不好测试IE是否支持,估计是不可能支持的吧. 另外一个方法是设置第一个div的属性为float:left;这样第一列的div就脱离整个文档流,第二个div直接 设置margin-left: 350px;即可.

自适应高度

自适应高度遇到的问题也是上面有一个固定高度为142px的div,下面的div的高度为100%-142px. 同样可以设置高度为height: -webkit-calc(100% - 142px); 另外一种方法是把div设置成 position: absolute;脱离文档流,然后设置top:142px; bottom:0px;即可 另外附上截图:

tagged: CSS

个人博客的建立

Posted on

好吧,我建立个人博客的想法其实已经差不多有一年了吧.一直没去实现...主要原因还是自己太懒...一开始想用WordPress搭建一个....其实我也搭建了 但是由于一直找不到好看的主题就坑在那一年多了...后来还想用Octopress来搭建...又由于自己当时的系统是Win而放弃了,现在我把自己的系统换成 Arch并且在学习Python,于是又开坑了...

虽然是自己动手写出来的,但基本属于山寨,毕竟现在自己对Python的掌握还属于初级阶段...UI其实也是山寨的 >_>

嘛嘛,先搭建出来一个再说,之后自己再慢慢装饰吧,不过总算是把坑给填上了.万事开头难,迈出了第一步,之后就容易多了....

不过我这个文笔很差的人写的博客估计也没人看

tagged: notes