0%

Other

综述

PhpStorm可以使用File Watchers自动编译Less,有了这个IDE,妈妈再也不用担心我的Less编译了。下面说一下我的配置过程。 下面的例子以 Mac OS X为例。

配置

1.配置npm

1
brew install npm

更多平台安装方式 npm

2.安装lessc

1
npm install less -g

安装完毕后查看安装路径

1
which lessc

Mac OS X的结果是

1
/usr/local/bin/lessc

3.配置PhpStorm

打开PhpStorm,Preferences->Tools->File Watchers 点击加号新增 Less Template,然后点击编辑按钮编辑,页面如下 56926F1E-D626-4E66-925D-15971F993F30 其中需要配置两个地方

Program:

配置为lessc的路径,这边配置为 /usr/local/bin/lessc

Output paths to refresh:

1
$FileParentDir(less)$/css/$FileDirPathFromParent(less)$/$FileNameWithoutExtension$.css

在这里简单解释下这个路径的意思。

例如项目名为 project,less文件我们放置在 project/public/less/manage/style.less $FileParentDir(less)$ 是获取 less 目录的路径,也就是 project/public $FileDirPathFromParent(less)$ 是获取 less 文件到 less 目录的路径,也就是 manage $FileNameWithoutExtension$ 是获取 less 文件不带后缀的名字,也就是 style 经过如上拼接,生成的内容为 project/public/css/manage/style.css

所以,不论我们的 less 文件如何放置,都可以生成相对路径的 css 文件。 配置完成之后,我们新建 less 目录,任意编辑一个 less 文件,都会在 css 目录下生成相应的文件。

简单配置

当然,如果你的 less 文件就直接在 less 目录下,可以简单配置以上的 Output Path如下 ../css/$FileNameWithoutExtension$.css 这也是一种比较常用的配置方法。 如果目录结构简单,可以采取以上方式。

JavaScript

C/C++

C/C++

Python

关于

首先,在此附上项目的地址,以及官方文档 PySpider 官方文档

安装

1. pip

首先确保你已经安装了pip,若没有安装,请参照 pip安装

2. phantomjs

PhantomJS 是一个基于 WebKit 的服务器端 JavaScript API。它全面支持web而不需浏览器支持,其快速、原生支持各种Web标准:DOM 处理、CSS 选择器、JSON、Canvas 和 SVG。 PhantomJS 可以用于页面自动化、网络监测、网页截屏以及无界面测试等。 安装 以上附有官方安装方式,如果你是 Ubuntu 或 Mac OS X用户,可以直接用命令来安装 Ubuntu:

1
sudo apt-get install phantomjs

Mac OS X:

1
brew install phantomjs

3. pyspider

直接利用 pip 安装即可

1
pip install pyspider

如果你是 Ubuntu 用户,请提前安装好以下支持类库

1
sudo apt-get install python python-dev python-distribute python-pip libcurl4-openssl-dev libxml2-dev libxslt1-dev python-lxml

测试 如果安装过程没有提示任何错误,那就证明一些OK。 命令行输入

1
pyspider all

然后浏览器访问 http://localhost:5000 观察一下效果,如果可以正常出现 PySpider 的页面,那证明一切OK 在此附图一张,这是我写了几个爬虫之后的界面。 2016-02-11 20.55.36 好,接下来我会进一步介绍这个框架的使用。

常见错误

我曾遇到过的一个错误: PySpider HTTP 599: SSL certificate problem错误的解决方法 ,后来在作者那发了issue得到了答案,其他的暂时没什么问题。 不过发现有的小伙伴提了各种各样的问题啊,不过我确实都没遇到过,我再Win10,Linux Ubuntu,Linux CentOS,Mac OS X都成功运行。不过确实有些奇怪的问题,跑着跑着崩了,一点就崩了我也就比较纳闷了。 如果大家有问题,可以看看作者项目里面有没有类似的issue,另外也推荐大家直接到作者的GitHub上发issue。 毕竟,这个框架不是我写的。 在此附上Issue地址: PySpider Issue

Python

综述

爬虫入门之后,我们有两条路可以走。 一个是继续深入学习,以及关于设计模式的一些知识,强化Python相关知识,自己动手造轮子,继续为自己的爬虫增加分布式,多线程等功能扩展。另一条路便是学习一些优秀的框架,先把这些框架用熟,可以确保能够应付一些基本的爬虫任务,也就是所谓的解决温饱问题,然后再深入学习它的源码等知识,进一步强化。 就个人而言,前一种方法其实就是自己动手造轮子,前人其实已经有了一些比较好的框架,可以直接拿来用,但是为了自己能够研究得更加深入和对爬虫有更全面的了解,自己动手去多做。后一种方法就是直接拿来前人已经写好的比较优秀的框架,拿来用好,首先确保可以完成你想要完成的任务,然后自己再深入研究学习。第一种而言,自己探索的多,对爬虫的知识掌握会比较透彻。第二种,拿别人的来用,自己方便了,可是可能就会没有了深入研究框架的心情,还有可能思路被束缚。 不过个人而言,我自己偏向后者。造轮子是不错,但是就算你造轮子,你这不也是在基础类库上造轮子么?能拿来用的就拿来用,学了框架的作用是确保自己可以满足一些爬虫需求,这是最基本的温饱问题。倘若你一直在造轮子,到最后都没造出什么来,别人找你写个爬虫研究了这么长时间了都写不出来,岂不是有点得不偿失?所以,进阶爬虫我还是建议学习一下框架,作为自己的几把武器。至少,我们可以做到了,就像你拿了把枪上战场了,至少,你是可以打击敌人的,比你一直在磨刀好的多吧?

框架概述

博主接触了几个爬虫框架,其中比较好用的是 Scrapy 和PySpider。就个人而言,pyspider上手更简单,操作更加简便,因为它增加了 WEB 界面,写爬虫迅速,集成了phantomjs,可以用来抓取js渲染的页面。Scrapy自定义程度高,比 PySpider更底层一些,适合学习研究,需要学习的相关知识多,不过自己拿来研究分布式和多线程等等是非常合适的。 在这里博主会一一把自己的学习经验写出来与大家分享,希望大家可以喜欢,也希望可以给大家一些帮助。

PySpider

PySpiderbinux做的一个爬虫架构的开源化实现。主要的功能需求是:

  • 抓取、更新调度多站点的特定的页面
  • 需要对页面进行结构化信息提取
  • 灵活可扩展,稳定可监控

而这也是绝大多数python爬虫的需求 —— 定向抓取,结构化化解析。但是面对结构迥异的各种网站,单一的抓取模式并不一定能满足,灵活的抓取控制是必须的。为了达到这个目的,单纯的配置文件往往不够灵活,于是,通过脚本去控制抓取是最后的选择。 而去重调度,队列,抓取,异常处理,监控等功能作为框架,提供给抓取脚本,并保证灵活性。最后加上web的编辑调试环境,以及web任务监控,即成为了这套框架。 pyspider的设计基础是:以python脚本驱动的抓取环模型爬虫

  • 通过python脚本进行结构化信息的提取,follow链接调度抓取控制,实现最大的灵活性
  • 通过web化的脚本编写、调试环境。web展现调度状态
  • 抓取环模型成熟稳定,模块间相互独立,通过消息队列连接,从单进程到多机分布式灵活拓展

pyspider-arch

pyspider的架构主要分为 scheduler(调度器), fetcher(抓取器), processor(脚本执行):

  • 各个组件间使用消息队列连接,除了scheduler是单点的,fetcher 和 processor 都是可以多实例分布式部署的。 scheduler 负责整体的调度控制
  • 任务由 scheduler 发起调度,fetcher 抓取网页内容, processor 执行预先编写的python脚本,输出结果或产生新的提链任务(发往 scheduler),形成闭环。
  • 每个脚本可以灵活使用各种python库对页面进行解析,使用框架API控制下一步抓取动作,通过设置回调控制解析动作。

Scrapy

Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架。 可以应用在包括数据挖掘,信息处理或存储历史数据等一系列的程序中。 其最初是为了页面抓取 (更确切来说, 网络抓取 )所设计的, 也可以应用在获取API所返回的数据(例如 Amazon Associates Web Services ) 或者通用的网络爬虫。Scrapy用途广泛,可以用于数据挖掘、监测和自动化测试

Scrapy 使用了 Twisted 异步网络库来处理网络通讯。整体架构大致如下

Scrapy

Scrapy主要包括了以下组件:

  • 引擎(Scrapy): 用来处理整个系统的数据流处理, 触发事务(框架核心)
  • 调度器(Scheduler): 用来接受引擎发过来的请求, 压入队列中, 并在引擎再次请求的时候返回. 可以想像成一个URL(抓取网页的网址或者说是链接)的优先队列, 由它来决定下一个要抓取的网址是什么, 同时去除重复的网址
  • 下载器(Downloader): 用于下载网页内容, 并将网页内容返回给蜘蛛(Scrapy下载器是建立在twisted这个高效的异步模型上的)
  • 爬虫(Spiders): 爬虫是主要干活的, 用于从特定的网页中提取自己需要的信息, 即所谓的实体(Item)。用户也可以从中提取出链接,让Scrapy继续抓取下一个页面
  • 项目管道(Pipeline): 负责处理爬虫从网页中抽取的实体,主要的功能是持久化实体、验证实体的有效性、清除不需要的信息。当页面被爬虫解析后,将被发送到项目管道,并经过几个特定的次序处理数据。
  • 下载器中间件(Downloader Middlewares): 位于Scrapy引擎和下载器之间的框架,主要是处理Scrapy引擎与下载器之间的请求及响应。
  • 爬虫中间件(Spider Middlewares): 介于Scrapy引擎和爬虫之间的框架,主要工作是处理蜘蛛的响应输入和请求输出。
  • 调度中间件(Scheduler Middewares): 介于Scrapy引擎和调度之间的中间件,从Scrapy引擎发送到调度的请求和响应。

Scrapy运行流程大概如下:

  • 首先,引擎从调度器中取出一个链接(URL)用于接下来的抓取
  • 引擎把URL封装成一个请求(Request)传给下载器,下载器把资源下载下来,并封装成应答包(Response)
  • 然后,爬虫解析Response
  • 若是解析出实体(Item),则交给实体管道进行进一步的处理。
  • 若是解析出的是链接(URL),则把URL交给Scheduler等待抓取

结语

对这两个框架进行基本的介绍之后,接下来我会介绍这两个框架的安装以及框架的使用方法,希望对大家有帮助。

个人随笔

最近博客的多说评论总是抽风,先来吐槽一下。 刚在整理评论的时候吓我一跳,之前我的好多评论都没了,是的,是只有我自己的评论没有了,而其它人的评论还有。摸不着头脑的我打开多说后台管理,哦天,我之前所有的评论和回复全部自动转为垃圾评论了,八百多条啊,废了一会功夫好不容易批量还原了。结果刷新页面一看,咦,还是没有,重新刷一下后台,竟然再次把我的评论设成垃圾评论了,后来又退出重新绑定了其它平台的账号,总算还原回来了。 然后我就继续开始回复大家的评论呀,结果要发布评论的时候,点一下发布,按钮就卡在正在发布这里不动了。打开浏览器看一下 Ajax 出了什么问题,结果出现了一个 create_json 报了个500服务器错误,查看页面信息显示参数配置不正确还是怎么了,没错,是显示我的站点多说评论配置不正确。那,其它人怎么评论上来的? 另外,之前,多说崩溃了已经不知道多少次了。 真是不爽的多说。 另外,多说还有一个非常令人发指的行为,会自动同步用户文章,收集用户信息,同步我们社交账号,即使是修改用户信息也要进行备份,这尼玛发展一定程度,多说还真有可能有利用这些用户信息谋利,甚至可能利用各个用户的文章做一个个性化阅读推荐也说不定。当然最开始的时候我只是觉得多说比较火,当时用上了也感觉比较方便,加上最初网站没几个东西,心想同步就同步呗,然后就一直用着了。现在再想想,也是可怕。 果断!弃用!弃用!弃用! 搜索了网上比较热门的评论插件,发现了畅言。使用了 WordPress 插件。 于是用上了,不过还是有一些令我感到不是太友好的地方,简单在此提一下。 嗯,首先我是比较追求美感的,界面问题。首先我会关注有没有个性化主题定制这个功能,畅言还是有的,支持CSS自定义。不过这个功能比较蛋疼,如果你不选择已经提供的主题,而是选择自定义CSS样式的话,你需要把所有样式重写一遍,它缺省继承了默认主题。比如我如果想在浅色主题红色风格基础上修改几个样式的话,这是办不到的,除非重写所有的红色风格样式,这就鸡肋了。建议可以选择继承某个默认主题的功能,然后自定义的CSS是在这个基础上设置的功能。而且我看WAP版本并没有自定义CSS,非常建议增加这个功能。最后我还是选择了红色主题,不过自定义样式就写在了站点全局样式表里面了,以此解决。 其次,同步本地评论功能,由于换了插件,所以评论都留在了本地了。同步完成之后,我并不能在畅言后台管理看到我刚才同步的评论,但是新发的评论是可以看到的,页面也是可以正常显示的。只是不能在线管理早先的原始评论了。 另外,头像问题,其实我个人非常不能忍受一个账号没有头像的行为,简直是大逆不道。畅言有个QQ快速登录的功能,然而,登录之后竟然不能获取我的QQ头像!不知道是不是我这边的问题,如果大家正常希望可以反馈我一下。另外,QQ登录之后怎么会给我取了一个奇怪的用户名,叫什么cmcccc,有点醉。而微博的快速登录的昵称和头像都是正常的,然而每次评论的时候都会默认勾选那个同步到微博的按钮,这个可以默认取消么? 还有,希望可以增加更多的平台的支持,比如微信、GitHub、脸书、推特等平台啦。 最后,有没有发表文章自动分享到各个平台的功能?我暂时没有发现。这点多说还是做得比较好的。 以上。 嗯,总之换上畅言之后用起来还是比较开心的,嘿嘿主要是改好了样式,看起来一阵舒爽。 昂,没错,我就是颜控! 有时候,我会因为一个样式不合我意而执着地去修改,即使要花费几个小时。 有时候,我会因为一个应用的图标(没错,就是说的图标)太丑了而卸载掉,即使是它的功能再怎么好。 有时候,我会因为一个屏幕膜有一点点损伤而去重新买一个新的。额,其实是因为今天给电脑贴膜折角了,我又花了几十块重新买了一个新的。 有时候,我就是一个强迫症,在写上面三句话的时候,第一句原本是在第二行的,然而因为看起来长度参差不齐我就把它移动到了最上面。嗯,这第四句话要写得更长才行。 好啦,貌似跑题了,时候不早啦,大家晚安。 嗯,换上调教好的美美的畅言还是很开心的,文章前后呼应,拜~ 屏幕快照 2016-02-03 03.43.51 美美哒~

个人日记

Hello,时隔一个多月,终于再次回到博客啦,首先跟大家说声抱歉,许多评论没有及时回复感到非常抱歉,希望我现在给大家的回复为时不晚。 距离上次在博客上写日记过去了几个月了吧。那时的我刚刚结束大学三年级。而现在,大四上半学期已经过半啦。这半年的时间可以说忙也可以说不忙。不忙是说这半年以来的课程比较轻松,只有四门选修课,学业负担比较轻。忙是说半年以来各种错综复杂的事情,许多事情需要好好安排一下时间才可以好好把握各个“进程”的合理分配。 那么就从我上次日记开始总结一下吧。 当时更新是去年七月十二日,刚大三放假不久。那个暑假前期过得可谓是心惊胆战呀,当时为了保研北京航空航天大学一直在紧张地复习准备。包括复习备考三年的专业课,还有准备C语言的上机考试。七月二十八二十九那两天机考和面试,不负众望,我顺利拿到了北京航空航天大学计算机科学与技术学院的Offer,心里的石头也落了地,当时看到通过面试的消息时真的激动得说不出口,这也算是我人生中为数不多的重大十字路口做出的一个选择吧。 面试结束之后,我便留在了导师那边做项目,一些大数据处理和爬虫的项目。不过当时的项目个人感觉比较简单,所以就在整个八月份找了一份实习,PHP研发工程师的岗位,之前准备保研也一直没有找工作,这也算是我找的第一份工作吧,不求赚神马,只求充实一下我的假期,也学习一些新的东西。 在这里顺便安利一下,公司名称是佳信德润,主打品牌是灵析,北京三环。在那边工作真的感觉特别舒心,虽然是创业公司,但是CEO玛丽老板(女哦)还有负责技术的柱子哥真的超级热情,为人特别好,谈吐之间就有一种亲切的感觉,那边的小伙伴十几个,相处地也十分融洽。在那边工作你不会有一种被忽视和指使的感觉,你可以随意向老板们发表自己的看法,甚至你可以与他们探讨代码问题,甚至公司某处的装饰啦,哪里的餐馆不错啦等等。有次我眼睛发炎,玛丽老板和柱子哥还特别关心我,亲自送到我医院,在这里再次说声谢谢,不知道你们是不是可以看到。我还记得小伙伴们一起拍公司写真,真的超赞。当然最主要的还是一起商讨项目进度,代码问题,接触到我之前没有接触过的新知识。在这里你可以负责许多事情,最大限度地发挥你的光和热。不是我打广告,如果有小伙伴们有意向做PHP,非常推荐你们过去,可以去他们官网发应聘哦。相信我,不会坑你的。 实习时间真的过得很快,转眼一个月过去了,九月份来了,大四开学啦。 因为需要开学以后办一些保研的相关手续,另外重要的事当然是陪妹纸啦嘿嘿,你懂得。 开学之后就开始忙一些项目了。大四的课程不紧,趁着这大四的时光,多学点东西,以后这样的日子真不多了,好好珍惜。 所以大四上学期的基调是,专注陪妹子,业余撸代码,顺便做做外包。请叫我全职男朋友,兼职程序猿。 这半年以来做的项目不算多。首先,学习了Laravel框架,嗯,保证你学完之后就不想用其他的框架了。最开始是帮我的叔叔做一个拍电影的网站,规模还比较大,一直在完善中。另外在暑假实习的过程中发现了公司自己写了一款CMS框架,自己觉得还有许多功能可以添加,一些架构可以继续完善,所以决定自己写一个CMS,不仅算是练手,也算是造轮子吧。毕竟你写好了就是你自己的,用起来都是那么自然,假如来了个外包神马的,轻松应对分分钟的事,神马深度定制都是浮云。当然还有帮爸爸妈妈写的一个微信平台,在微信订餐售卖东西,嗯,这叫做自己动手实现O2O。另外就是一些零零散散的小外包,没事可以赚点外快养活一下自己。 最近刚刚入手了自己人生中的第一台MAC,当然还是要感谢父亲大人的支持,自己的手头还是比较紧的,你懂得。 嗯摒弃Windows,感觉撸代码的快感不止提升了几管气,也希望它可以陪我度过接下来许多年的学习时光。在这里也安利一下一个小店,微博上的小闷小闷,水果从她这里买,价格真的是十分公道啊,正品无误了,入手十几天了,感觉逼格提升了,心情也舒畅了,多年的Windows卡顿症也治好了~ 嗯,接下来的时间,爸爸妈妈年前工作辛苦,接下来几天我要去帮他们分担一下工作啦。 最后,恭祝大家小年快乐,工作顺心,别跟钱过不去,开心最重要~ 欢欢喜喜过大年啦~

HTML

问题来源

在写代码的时候遇到了如下问题: 使用了bootstrap框架来编写了一个页面,其中input元素两侧留有空白。然而用JS动态添加的同样的元素却不会出现这种情况。具体截图表现如下: 20151226212230 我们可以发现,第一行和而三行的代码是完全一样的,可是呈现的结果是截然不同的。 在线测试样例 大家是不是觉得很奇怪?没错,我也是。中间的那个缝隙是哪里来的呢?

刨根问底

在这里感谢 wonder 同学的大力相助,才得以找到问题的所在。 出现此问题的原因在于:

html中的内联元素在书写代码时,如果两元素代码之间有换行,浏览器会将其解释为空格。而这个空格是会被当作一个空白节点(nodeType等于3的节点,就是文字节点)

所以,因为代码中我使元素呈现 inline 的属性,然后两个代码之间具有换行,所以二者之间出现了空白。正常情况下,二者之间是不应该出现空白的。然而用 jQuery 生成元素的时候,因为是用的 + 连接符,所以换行符被忽略了。也就是代码是连接起来的,所以二者之间便不会出现空白。 解决方法: 1.将原代码中的input写到一行。如下:

1
2
3
<div class="form-group">
<input class="form-control inline span3" name="education[school][]" type="text" value="123"><input class="form-control inline span3" name="education[date][]" type="text" value="456">
</div>

2.或者在JS代码中加入换行符。如下:

1
2
3
4
5
6
$('button').on('click', function() {
$('<div class="form-group">'+
'<input class="form-control inline span3" name="education[school][]" type="text" value="">\n'+
'<input class="form-control inline span3" name="education[date][]" type="text" value="">'+
'</div>').appendTo($(".content"));
});

以上两种方式,解决方法都比较简单实用。其他的改变 padding 或者 margin 的方法就不推荐了。 好了,那么明白了之后,我们肯定要可以举一反三的,来探究一下如果是块级元素会不会这样呢?

举一反三

好的,让我们试一下块级元素如果设置为 inline的话会不会也这样。 把 input 标签改成 div,然后给它加上 display: inline 属性,加一下背景颜色区分,观察一下效果。 代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="//cdn.bootcss.com/bootstrap/3.3.5/css/bootstrap.min.css">
</head>
<body>
<h1>点击按钮添加元素</h1>
<div class="content">
<div class="form-group">
<div class="item">hello1</div>
<div class="item">hello2</div>
</div>
</div>
<button class="btn btn-primary">添加</button>
<script src="//cdn.bootcss.com/jquery/1.11.3/jquery.min.js"></script>
<script src="//cdn.bootcss.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
<style>
.item {
display: inline;
background: #555;
}
</style>
</body>
</html>

观察一下效果 20151226220135 嗯,果然,它的间距还是出现了。 那么改成 display: inline-block 呢?

1
2
3
4
5
.item {
display: inline-block;
width: 200px;
background: #555;
}

20151226220326 可见间距还是有的。 我们把 div 的换行去掉,看一下。 20151226220443 Perfect!它已经消失不见了! 以上,在chrome,edge,ie11测试通过。

综述

通过以上研究我们可以得出如下结论: 内联元素,代码中带有换行,会出现空白间距。块级元素,设置了内联样式,且代码中带有换行,也会出现空白间距。 解决方法是删除代码中的换行即可。 以上是在写程序过程中发现的现象,希望能对大家有帮助!

Other

1.Git 还在担心自己辛辛苦苦写的代码被误删了吗?还在担心自己改错了代码不能挽回吗?还在苦恼于多人开发合作找不到一个好的工具吗?那么用Git就对了,Git是一个开源的分布式版本控制系统,用以有效、高速的处理从很小到非常大的项目版本管理。有了它,代码托管不是问题,版本控制不再苦恼,多人开发变得简单易行。 链接:http://git-scm.com/ QQ截图20141124142208 2.GitHub 学会了Git之后,我们把代码托管到哪里呢?在此推荐给大家两个网站,GitHub,一个是国外免费的代码托管平台。许许多多的开源项目几乎都托管在上面,你也可以在上面搜一些你需要的源代码,丰富资源应有尽有,如果还不知道这个,是不是已经Out了呢? 链接:https://github.com/ QQ截图20141124142704 3.GitCafe 和GitHub一样,也是一个免费的代码托管平台。GitHub是国外的,GitCafe是国内的。两者功能几乎相同,不喜欢英文版的朋友可以来GitCafe尝尝鲜。 链接:http://gitcafe.com/ QQ截图20141124143049 4.七牛云存储 还在为自己项目的图片等资源没处存放而着急吗?还在担心图片加载耗费你主机大量流量吗?还在为你自己的网站加载速度过慢而揪心吗?云时代,就要用云存储。 比如我有一个虚拟主机是每月限流量的,而大量的图片加载占据了我网站访问流量的大部分,这时我们就可以考虑将图片移植到七牛云存储上,图片通过七牛来加载,节省宝贵的主机空间和流量。比如我的个人网站已经移植到了七牛,所有网页图片的加载都是从七牛上获取,速度快而且省流量。而且免费的七牛还提供了图片处理,比如压缩,水印等各种图片加工方式,想要什么有什么。 链接:http://www.qiniu.com/ QQ截图20141124143837 5.云适配 有时候我们做出的网站没有用到响应式布局,也可能我们的网站DIV的像素宽度已经规定成具体的多少像素了。所以,用手机访问的时候可能就会出现这样那样的问题,或者显示得很小,或者整个布局全都乱掉了。这时,我们需要把自己的网站适配一下,那么在此推荐一个网站,云适配。插入一行代码,通过在线修改和生成手机适配效果,得到手机访问的网页最佳适配效果,方便而又快捷。这时电脑和手机均能看到最佳适配效果了。 链接:http://www.yunshipei.com/ QQ截图20141124144434 6.聚合数据 大家在开发过程中,可能会用到各种各样的数据,想找一些接口来提供一些数据。比如天气预报查询,火车时刻表查询,彩票查询,身份证查询等等。有了这个接口,直接调用即可。各种各样的API接口满足你。 链接:http://www.juhe.cn/ QQ截图20141124150336 7.够快云库 够快云库,是基于云存储的团队协作性文件管理平台。 团队成员可以同步共享资料,即时沟通交流,便捷移动办公,从而实现团队的100%高效运作。比如团队开发过程中有什么要共享的资料,就可以放到里面。支持在线编辑和修改,小组讨论合作等方式。 链接:http://www.gokuai.com/QQ截图20141124150727 8.SAE SAE的强大不用多说了,强大的虚拟主机提供商。提供各种各样的编程语言在线运行,同时提供云存储Storage,各种数据库,应用十分广泛。不仅提供代码托管,还提供在线服务器运行,JAVA,PHP,Python等等的支持应有尽有,在这里你可以假设你自己的网站,你的应用接口,你的创意应用。好不好,用用就知道。另外还有JAE(京东),BAE(百度),功能类似,也推荐一下。 链接:http://sae.sina.com.cn/ QQ截图20141124151607 9.多备份 多备份提供了主机的文件备份及数据库的备份。可能对于文件的备份大家都已经有其他方法了,不过数据库的自动备份现在还没有多少有效的解决方法,人工备份又显得比较麻烦。如果数据库有数据丢失,那就不好找回了。在此推荐一个网站:多备份。提供主机的文件自动备份和数据库自动备份,方便快捷。 链接:http://www.dbfen.com/ QQ截图20141124152121 10.慕课网 学习IT知识哪家强?在此为大家推荐一个全免费的学习网站,资源丰富,还提供了比较创新的学习和练习相结合的方式。感觉非常不错,讲解得也很有条理,推荐一下。 链接:http://www.imooc.com/ QQ截图20141124152408 11.W3SCHOOL 这也是一个非常棒的学习网站,讲解简单易懂,我的PHP入门,HTML,JS,CSS入门都是从这里学到的,感觉讲解非常不错。 链接:http://www.w3school.com.cn/ QQ截图20141124152659 12.阿里云 感觉比较不错的主机提供商,提供免费备案,对于ECS的管理做得非常好,数据恢复和备份及监控也应有尽有,另外提供了SLB,RDS,CDN等等的支持,个人感觉很不错的主机提供商。就是价格略贵! 链接:http://www.aliyun.com/ QQ截图20141124153235 13.DNSPod 免费的域名解析提供商,管理方便,简洁高效。非常人性化的提示,还提供域名D监控等功能,方便实用。 链接:https://www.dnspod.cn/ QQ截图20141124153637 14.WordPress 一款开源的PHP框架,搭建个人博客网站最实用的选择之一。我的个人网站就是基于此搭建,甚至你都不需要懂PHP你就可以搭建自己的个人网站。提供强大的后台文章管理和插件及主题管理,几乎可以满足个人网站所有需求。 P.S.找个好的网站模板,你就相当于成功了一大半。 链接:http://cn.wordpress.org/ QQ截图20141124154227 15.BootStrap 一个强大的前端框架,有了它你再也不需要敲那么多行的样式表,再也不用一点点地调试难看的DIV了。直接调用各种CSS样式,分分钟写出好看的网页界面。按钮、表格、进度条、提示框、输入框、菜单,应有尽有。 链接:http://www.bootcss.com/ QQ截图20141124154554 16.芒果广告 如果你开发了自己的移动应用,想添加广告来赚钱,推荐芒果广告。它是一个综合的移动广告平台,聚合了百度广告、多盟广告、易传媒等等广大广告商,可以自己定制广告显示的内容及样式,可以选择各种广告的投放比例。广告点击量、展现量详细报告、收入分析详细数据应有尽有。推荐一下。 链接:http://www.adsmogo.com/ QQ截图20141124154953 17.极光推送 极光推送,使得开发者可以即时地向其应用程序的用户推送通知或者消息,与用户保持互动,从而有效地提高留存率,提升用户体验

平台提供整合了Android推送、iOS推送的统一推送服务。让用户可以更好地接收你推送的内容。 链接:https://www.jpush.cn/ QQ截图20141124155237 18.Bmob 提供强大的后台接口支持,移动开发过程中可能需要各种各样的后台接口,直接利用这个,你可以创建自己的后台接口。它还提供了强大的游戏后端支持数据,文档和教程也是非常的完备,为移动开发带来相当大的便利。 链接:http://www.bmob.cn/ QQ截图20141124155600

19.有道云笔记 大多数人都听说过吧?在学习或者开发过程中可能会有各种各样的知识点,好记性不如烂笔头。最好的方式就是记录下来。在此推荐一个笔记,有道云笔记。在使用了各种笔记工具之后,发现还是有道好用。它提供了个人云笔记,云协作等功能,做到PC、手机、平板等多处同步。占用小,界面美观,使用方便。 链接:http://note.youdao.com/ QQ截图20141124160008 20.Coding 集合了GitHub和SAE的功能,利用Git上传代码,并对代码进行托管,而且提供了一键部署和运行的功能。提供了代码监控功能,通过自动化静态代码分析等管理工具,发现代码问题,获取代码度量信息,及时了解代码质量状况。一个新兴网站,推荐一下! 链接:https://coding.net QQ截图20141124161059 21.SendCloud 开发过程中你可能会遇到邮件发送的功能,用了SendCloud,一切都方便多了,它提供免费的邮件代发功能。邮件发送的难题,就交给它来解决吧。 链接:http://sendcloud.sohu.com/ QQ截图20141124161409 22.蒲公英 有时候你发布Android市场,发布Appstore,会花费相当多的时间才能得到审核通过。那么在团队之间,想团队之间提供内测功能,那么就可以用到它啦,这里提供了一个发布应用的平台,发布之后,生成链接和二维码,即可实现下载测试,简单方便。 链接:http://www.pgyer.com/ QQ截图20141124161757 23.DCloud HTML5现在已经定稿,用HTML5开发APP的浪潮即将袭来,在此提供一个免费的开发网站。它提供了一个HBuilder的工具,以及H5APP开发文档及H5APP的开发框架MUI框架,有了它,H5APP的开发变得相当简单,而我也在蠢蠢欲动。 链接:http://www.dcloud.io/ QQ截图20141124162042 24.青云 提供免费的云服务器、云存储、云数据库的功能。同时提供多个开放API,功能实用,非常强大。 链接:https://www.qingcloud.com/ QQ截图20141124162338 25.云测 开发了应用之后到哪里去测试?选择云测没错。它提供应用云测试、手机游戏测试,提供崩溃分析。一切测试由它来搞定。 链接:http://www.testin.cn QQ截图20141124162721 26.Meteor Meteor 是一个构建在 Node.js 之上的平台,用来开发实时网页程序。Meteor 位于程序数据库和用户界面之间,保持二者之间的数据同步更新。 链接:https://www.meteor.com/ 屏幕快照 2016-02-21 14.04.17 暂时先分享给大家这么多!如果有好的网站或者应用推荐,欢迎与我交流,可以评论,可以留言!以上是一些比较实用的网站和工具的分享!希望能给大家的生活和开发带来一定的便利!Thanks!

Other

前言

有时候,一些文件是不想公开让被人看到的,或者只想某些特定的人看到。 比如说,你要上交一个实验报告啦,老师给了一个FTP上传目录,但是你只想让老师看到报告,而不想公开给同学们看。这时候,就需要加密一下你的文件啦。 那么有什么好的加密方式呢?PGP,你的不二之选。

PGP加密原理

用直白的话来讲就是 对方给你一个公钥,他自己也保存了一个私钥,你利用他的公钥来加密,只有对方自己用自己私钥才能解密。由于其他的人没有拿到私钥,所以解不开的,即使是你自己,也解不开的。所以,这样就保证了,只有你和对方可以查看内容。 引用详解原理:

PGP(Pretty Good Privacy),是一个基于RSA公钥加密体系的邮件加密软件。可以用它对邮件保密以防止非授权者阅读,它还能对邮件加上数字签名从而使收信人可以确认邮件的发送者,并能确信邮件没有被篡改。它可以提供一种安全的通讯方式,而事先并不需要任何保密的渠道用来传递密匙。它采用了一种RSA和传统加密的杂合算法,用于数字签名的邮件文摘算法,加密前压缩等,还有一个良好的人机工程设计。它的功能强大,有很快的速度。 PGP是一种供大众使用的加密软件。电子邮件通过开放的网络传输,网络上的其他人都可以监听或者截取邮件,来获得邮件的内容,因而邮件的安全问题就比较突出了。保护信息不被第三者获得,这就需要加密技术。还有一个问题就是信息认证,如何让收信人确信邮件没有被第三者篡改,这就需要数字签名技术。RSA公钥体系的特点使它非常适合用来满足上述两个要求:保密性(Privacy)和认证性(Authentication)。 RSA(Rivest-Shamir-Adleman)算法是一种基于大数不可能质因数分解假设的公匙体系。简单地说就是找两个很大的质数,一个公开即公钥,另一个不告诉任何人,即私钥。这两个密匙是互补的,就是说用公匙加密的密文可以用私匙解密,反过来也一样。 假设甲要寄信给乙,他们互相知道对方的公匙。甲就用乙的公匙加密邮件寄出,乙收到后就可以用自己的私匙解密出甲的原文。由于没别人知道乙的私匙,所以即使是甲本人也无法解密那封信,这就解决了信件保密的问题。另一方面由于每个人都知道乙的公匙,他们都可以给乙发信,那么乙就无法确信是不是甲的来信。这时候就需要用数字签名来认证。

加密流程

在 Windows 下,有一个非常强大的工具,叫做 PGP Desktop,可以利用这个来加密。 下载链接 这个是 64 位的安装包,安装过程我就不详细描述了,比较简单。如果你的系统是 32 位,那么请再寻找一下其他的安装包。 安装之后,搜索一下开始菜单或者安装目录,找到 PGP Desktop,打开它。 20151215003158 打开之后界面是这个样子的 20151215003312 恩,别人应该给了你公钥了对吧,比如,这次老师的公钥是

1
2
3
4
5
6
7
8
9
10
11
12
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: PGPfreeware 6.5.8 for non-commercial use <http://www.pgp.com>

mQCNA1NKJuwAAAEEAMvdBmw1TTTkLTL6w9C28MhtVnsWtNeHVJl98CFCzOveyKDH
KGPf/RA4moDUE1IkkUelRKJV1O2JlcqBZPOGI/FII/6yq/DbNHKOKhz6fgbbdgxh
UArjUSthA/BPnodTcEtGZHlGSvK7n3TcWmGY700YB9crWpbyHfVQ7KLGc3SlAAUR
tCF0ZXN0NGNyeXB0byA8dGVzdDRjcnlwdG9AMTYzLmNvbT6JAJUDBRBTSibs9VDs
osZzdKUBAQzxBACr3UpakH7SMtU9OgHHZCARE4xKVChTBCh9kquhhU5Kr9Er0AHy
UWO1BD+z58VTu9XPR2/NSrCOGNwfZVqZTY8eTUTuQVJKpAR9CNtRi0B6yQa8pQVm
7vfEqzBcsWcPkOzfRU75Ubdc+IxZkdMcPfPnzn1kU2cTKUIlM5+cy/+qvw==
=nNyG
-----END PGP PUBLIC KEY BLOCK-----

来,添加一下,有一个非常简单的方法就是直接右键粘贴,然后确定就好了。 20151215003737 20151215003953 然后,你可以把这个公钥加到你的 Master Keys 里。 点击,菜单中的 Tools -> PGP Options -> Master Keys -> Add -> add -> OK,按照图示箭头操作即可。 20151215011210 然后,确定,邮件点一下 添加到 Master Keys。 20151215011305 会提示成功添加。然后接下来就进行加密吧。 点左侧的 PGP ZIP,然后 New PGP Zip,然后选择文件,点击确定。 20151215004710 然后直接下一步 20151215004802 直接下一步 20151215011736 然后下一步,选择导出路径即可 20151215011755 下一步,导出成功,加密完成。 导出的文件格式就是原文件名加了 pgp 后缀,只有对方的私钥可以解开。 当然如果有不死心的小伙伴想破解,自己试试咯~

综述

以上,为了便于演示,我直接将秘钥添加到了 Master Key 中,其实这个 Master Key 当然最好是你自己专属的秘钥咯,不过无所谓啦,我们只是为了演示一下加密过程,利用这个公钥加密,不要在意这些细节。 小伙伴们体验一下吧!

Other

FreeNAS简介

FreeNAS 是什么? FreeNAS 是一款广受赞誉的开源免费 NAS 操作系统。它能把普通台式机瞬间变成一台多功能 NAS 服务器。不但适用于企业文件共享,同样适用于打造家庭媒体中心。 FreeNAS 支持多种共享协议,包括 SMB/CIFS、NFS、AFP、WebDAV、iSCSI、FTP/TFTP、RSync等。 官方网站

iSCSI

iSCSI技术是一种由IBM公司研究开发的,是一个供硬件设备使用的可以在IP协议的上层运行的SCSI指令集,这种指令集合可以实现在IP网络上运行SCSI协议,使其能够在诸如高速千兆以太网上进行路由选择。iSCSI技术是一种新储存技术,该技术是将现有SCSI接口与以太网络(Ethernet)技术结合,使服务器可与使用IP网络的储存装置互相交换资料。 iSCSI:Internet 小型计算机系统接口 (iSCSI:Internet Small Computer System Interface)。

本篇目标

那么本篇文章的目标就是记录一下怎样使用 FreeNAS 配置一个输入我们的网络存储服务。

下载安装

首先我们要下载 FreeNAS的镜像,由于FreeNAS 9 对系统的要求比较高,在这里我们用到的是 FreeNAS 8。 镜像下载 下载完成之后我们利用 VMware安装即可。 安装界面提供了四个选项:

  1. Install/Upgrade 安装/升级
  2. Shell 终端
  3. Reboot System 重启系统
  4. Shutdown System 关闭系统

20150129171248 使用键盘上的方向键切换菜单,选择第一项,按回车键确认。 接下来系统会提示选择 FreeNAS 系统盘,在如下图所示的界面中会显示出所有可以用作安装 FreeNAS 系统的设备。你需要根据自己界面上显示的实际内容进行选择,此处应该选择我们准备作为 FreeNAS 系统盘的另一块U盘,选择好以后按回车键确认。 20150129171601 此时,界面上显示了一些警告信息,大意为“你选择作为 FreeNAS 系统盘的设备上的所有数据都会被清空,而且该设备将完全被系统占用,不能用作数据存储。” 按回车键确认。 20150129171850 确认后,系统开始执行安装,如下图所示。 20150129172137 系统安装完成后会给出成功提示,提醒我们移除安装盘,如下图。不用理会,按回车键继续。 20150129172300 此时,系统又回到了最初的安装界面,如下图所示。用方向键切换选择第四项,按回车键关闭系统。 20150129172526 待系统完全关闭以后,再次启动电脑,并设置从 U盘系统盘引导,成功启动以后,看到如下所示的界面,代表系统已经安装完成。 20151213171035 接下来,你可以使用浏览器访问界面中给出的 IP 地址打开 FreeNAS 的 WebGUI 管理界面了。

网页配置

接下来,我们就可以通过浏览器来配置我们的 FreeNAS了。 通过系统的提示,我们访问以下链接即可: http://192.168.231.131/ 20151213171159 访问之后,可以看到如上内容。 点击 setting,先切换一下语言,简体中文。 20151213171352 接下来我们便开始iSCSI的配置啦。

iSCSI配置

1.首先开启iSCSI服务,然后点击右侧的设置图标进入设置界面。 20151213173030 2.添加iSCSI端口,选择默认设置即可。 20151213171558 3.添加网络授权信息 20151213171648 两个全部填写为ALL即可。 4.添加iSCSI用户信息 20151213171733 填入用户信息和秘钥即可,下方的输入框是加密可选的。 5.添加iSCSI属性信息 20151213171855 6.添加存储设备 在这里我们首先要添加一块磁盘,在VMware中的设置里添加一块磁盘,在这里分配为20G。 2015121317195120151213172007 在FreeNAS设置界面中,添加扩展设备。 20151213172115 7.关联目标 将用户信息和磁盘信息关联起来。 20151213172143 通过以上流程,我们便完成了一个网络硬盘的配置。

Windows使用

好,配置好了网络硬盘,那么我们就在Windows下使用一下吧,Windows 7 以上的系统自带了 iSCSI 服务。 我们可以直接在控制面板里面找到它。在控制面板中搜索。 20151213172329 然后,会出现一个属性设置窗口。点击发现,发现门户,输入刚才FreeNAS的地址即可。 20151213172518 接下来在目标的选项卡中我们会看到刚才配置的一个目标,点击下方的连接即可。 20151213172541 连接成功之后,我们便可以完成连接了。 打开磁盘设置,我们便可以看到那个20G的磁盘了,然后新建简单卷。 20151213172806 格式化为NTFS系统,刷新一下,打开计算机。 20151213172840 我们可以发现,一个新的网络磁盘便安装成功啦。 可以向其中放置任何文件,与其他的磁盘没有任何区别!

结语

FreeNAS是一个非常强大的工具,在这里我们只涉及到了 iSCSI 服务的使用,还有更多等待着我们的探索,小伙伴们快来试验一下吧。

C/C++

最近在研究信息安全,需要用到OpenSSL库,我用到的开发IDE是VS2012,所以,在这里也记录一下我配置VS2012的OpenSSL库的过程。

下载OpenSSL库

OpenSSL库大家可以自行下载源码然后用ruby进行编译,另外我们也可以选择直接下载编译好的类库。 这里我们利用的后者,在此提供一个下载链接。 OpenSSL-Win32 下载完成之后解压,比如我的放到了D盘。 20151207162627

新建项目

首先,我们找一段测试代码,在此利用的是 AES 算法的示例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <stdio.h>
#include <openssl/aes.h>
#include <stdlib.h>
#include <string.h>

int main()
{
unsigned char key[16+1] = "my-key-i-choosed"; // 128bits key (应该是真正的随机数才好)
char pt1[16+5+1] = "0123456789abcdef12345"; // 明文
char ct[16+5+1]; // 密文
char pt2[16+5+1]; // 解密后的明文

AES_KEY k;
unsigned char iv1[16+1] = {"1023456789abcdef"}; // 16+1,加密用
unsigned char iv2[16+1] = {"1023456789abcdef"}; // 16+1,解密用

{ // single blcok test
AES_set_encrypt_key(key, 16*8, &k);
AES_encrypt((unsigned char*)pt1, (unsigned char*)ct, &k);

AES_set_decrypt_key(key, 16*8, &k);
AES_decrypt((unsigned char*)ct, (unsigned char*)pt2, &k);

if (memcmp(pt1, pt2, 16)==0)
puts("AES block ok");
else
puts("AES block err");
}

{ // cfb mode (stream mode)
int num=0;
AES_set_encrypt_key(key, 16*8, &k);
AES_cfb128_encrypt((unsigned char*)pt1, (unsigned char*)ct,
16+5, &k, (unsigned char*)iv1, &num, AES_ENCRYPT);

num=0;
AES_set_encrypt_key(key, 16*8, &k); // MUST as enc
AES_cfb128_encrypt((unsigned char*)ct, (unsigned char*)pt2,
16+5, &k, (unsigned char*)iv2, &num, AES_DECRYPT);

if (memcmp(pt1, pt2, 16+5)==0)
puts("AES CFB mode ok");
else
puts("AES CFB mode err");
}
system("pause");
return 0;
}

接下来新建一个项目,win32控制台程序,空项目,完成。 20151207161749 20151207161824 新建源文件,我取名叫做 aes.cpp,将代码复制进去,可以看到代码最初是在报错的。 20151207162418 好,接下来我们进行环境配置。

环境配置

右键项目名称,弹出一个菜单,选择属性。 在VC++目录选项卡中,添加包含目录和库目录。 在这里,我的包含目录就是刚才解压的OpenSSL目录的include目录,库目录则是lib目录。 注意:分号要是英文分号,英文分号! 20151207162929 接下来选择连接器选项卡,输入libeay.lib和ssleay32.lib两个附加依赖项。 20151207163915 现在右击项目,重新生成。 我们可以看到,程序可以正常生成exe了。 20151207164034 但是直接运行的话会报错,是因为缺少dll文件。 20151207164304 之后,将项目中的libeay32.dll和ssleay32.dll文件放入项目的debug目录即可。 20151207164444 最后项目的debug目录如下 20151207164405 重新运行exe程序,发现已经正常运行。 20151207164736 至此,VS配置OpenSSL环境的过程已经全部完成。 其他项目类似,大家可以试着配一下。 如有问题,欢迎留言交流~

Linux

最近服务器要过期了,需要进行迁移,新服务器如果上面配置的是Apache服务器该怎么办呢? 系统:Ubuntu 14.04

环境配置

首先新主机上配置好apache环境,这个就不多说了,直接执行下面的命令即可。

1
2
3
4
5
6
7
sudo apt-get install apache2
sudo apt-get install php5 php5-cgi php5-mysql php5-curl php5-gd php5-idn php-pear php5-imagick php5-imap php5-mcrypt php5-memcache php5-mhash php5-ming php5-pspell php5-recode php5-snmp php5-tidy php5-xmlrpc php5-sqlite php5-xsl
sudo apt-get install mysql-server mysql-client
sudo apt-get install libapache2-mod-php5
sudo apt-get install libapache2-mod-auth-mysql
sudo apt-get install phpmyadmin
sudo ln -s /usr/share/phpmyadmin/ /var/www/html/phpmyadmin

通过以上配置,新主机便可以实现lamp环境的配置了。

代码迁移

首先旧主主机上打包一下代码,比如一个文件夹名字叫 wonder

1
tar -zcvf wonder.tar.gz wonder

然后,打包完成之后,便会出现一个名字叫做 wonder.tar.gz 的文件 可以利用wget方式直接下载。

1
wget http://xxx.xxx.xxx.xxx/wonder.tar.gz

下载完成之后,直接解压即可。 这样代码就取到了。

数据库迁移

数据库迁移无非就是在phpmyadmin之间导入导出,这个很简单。 但是重要的一点是,需要把 wp-options 表中的两个URL配置改掉,比如原来是一个域名链接,现在需要改为 IP+文件名。 20151128144223 否则,浏览器会提示重定向循环的问题。

服务器配置

首先我们需要将域名解析到这个主机。 配置示例域名:wonderlee.me 20151128144413 然后配置一下,vhost,在apache下配置是这样的 首先在 /etc/apache2/apache2.conf 中加入如下两行

1
2
# Include all the user configurations:
Include httpd.conf

然后我们需要在 httpd.conf 配置一下域名解析 新建一个 /etc/apach2/httpd.conf,加入如下内容

1
2
3
4
5
6
7
8
9
10
11
12
ServerName 115.28.24.44:80

<VirtualHost 115.28.24.44:80>
DocumentRoot /var/www/html
ServerName 115.28.24.44
</VirtualHost>

<VirtualHost 115.28.24.44:80>
DocumentRoot /var/www/html/wonder
ServerName wonderlee.me
ServerAlias wonderlee.me
</VirtualHost>

然后执行服务器重启操作。

1
sudo service apache2 restart

好,这样的话我们的域名配置解析就好了。 输入 wonderlee.me 即可解析到 wonder 文件夹啦。 可以输入你的域名试试看,已经可以了吧。 然后我们需要开启 rewrite模块。 输入命令

1
sudo a2enmod rewrite

然后修改 /etc/apache2/apache2.conf 文件

1
2
3
4
5
<Directory /var/www/>
Options Indexes FollowSymLinks
AllowOverride None
Require all granted
</Directory>

改为

1
2
3
4
5
<Directory /var/www/>
Options Indexes FollowSymLinks
AllowOverride All
Require all granted
</Directory>

即可,结束之后重启一下服务器。

1
sudo service apache2 restart

在项目目录下新建一个文件 .htaccess,来支持重写

1
2
3
4
5
6
7
8
9
10
11
# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /wonder/
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /wonder/index.php [L]
</IfModule>

# END WordPress

好,这样,重写过程就完成啦。

网站配置

最后,需要将网站的配置修改一下,比如固定链接 20151128145507 比如网站的基地址 20151128145620 至此,网站配置工作全部完成,欢乐地上网体验一下吧 如有问题,欢迎留言。

Linux

之前一直都用Apache服务器,由于网站访问量比较大,另外加上旧服务器快到期了,准备迁移到新的服务器上,所以决定采用Nginx服务器。 迁移过程比较心酸,之前一直用apache,对nginx服务器配置不熟悉,踩了很多坑。下面说一下我的网站从旧主机(配有apache服务器)迁移到新主机(配有nginx服务器)的过程。

代码迁移

这个过程其实也是比较心酸的,查看了一下目录结构占用空间已经足足快1个G了,可想而知里面占用的大部分空间是上传的图片素材。 不过要是迁移全部图片的话工程量实在是巨大。不过,好消息是我从开始就使用了七牛CDN加速,所以,上传的图片会自动存放到七牛,只不过也在主机本地留了备份而已,所以,我可以安心地删掉它们了。 那么对代码进行瘦身之后,这里就有两种方法来迁移了: 1.可以用git上传到github,然后用另一台主机把代码拉下来即可,在此不再赘述。 2.打包上传,然后直接在另一台主机上下载下来,由于我的两台主机在同一局域网内,所以我直接采用了这种方式,传输速度快。

打包

由于代码中含有 .git 目录,所以这部分我们不需要打包,那么压缩时我们就需要排除这个文件夹。 20151113155701 文件夹名叫cqc,那么我们就打包一下,排除.git目录,使用如下命令

1
tar -zcvf cqc.tar.gz --exclude=cqc/.git cqc

运行结束后会出现 cqc.tar.gz 文件,这就是目录压缩包。 然后我们只需要在另一台主机上输入

1
wget http://xxx.xxx.xxx.xxx/cqc.tar.gz

即可完成下载,速度可是嗖嗖的 然后解压即可,代码便完成了迁移。

数据库迁移

数据库用二者的phpmyadmin导出和上传即可。我导出 .sql 文件,大小为9M,而phpMyAdmin的上传限制大小是2M,怎么办?其实我们可以压缩 .sql 文件为 zip格式,压缩之后就有了1.4M了,分分钟完成上传。要知道 phpMyAdmin 可是支持 .sql.zip 文件的。 接下来是一个比较重要的部分,那就是配置一下站点信息。直接修改数据库的两个URL。 分别是 siteurl 和 home,一定要修改为 http://xxx.xxx.xxx.xxx/cqc 的形式,也就是把原来的域名改成IP加目录的形式,要不然网站是无法访问的,会出现多重循环定向的提示。 好,其他的没什么问题,连接数据库错误的话就修改一下目录的 wp-config.php 文件吧,连接数据库的信息修改正确就好了。

配置vhosts

和 apache 一样,我们多个域名肯定要可以解析到不同的目录吧,nginx 当然也是支持的。 接下来我们需要把新域名解析到 cqc 目录,在 nginx 下怎么做呢?其实还是比较简单的。 在 /etc/nginx 目录下可以新建一个 vhosts文件夹。在这里我们要解析 cqc 目录,那么我就新建一个 cqc.conf 文件。 现在例如我要把 blog.cuiqingcai.com 解析到 cqc 文件夹,配置如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
server {
listen 80;
server_name cuiqingcai.com blog.cuiqingcai.com;

index index.html index.htm index.php;
root /var/www/cqc;

location / {

if (!-e $request_filename) {
rewrite ^([_0-9a-zA-Z-]+)?(/wp-.*) $2 last;
rewrite ^([_0-9a-zA-Z-]+)?(/.*\.php)$ $2 last;
rewrite ^ /index.php last;
}
}

location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
# # With php5-fpm:
# fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /var/www/cqc$fastcgi_script_name;
include fastcgi_params;
}
}

其中

1
2
3
4
5
6
7
8
location / {

if (!-e $request_filename) {
rewrite ^([_0-9a-zA-Z-]+)?(/wp-.*) $2 last;
rewrite ^([_0-9a-zA-Z-]+)?(/.*\.php)$ $2 last;
rewrite ^ /index.php last;
}
}

这一部分是伪静态重写,因为我的博客用的是wordpress,所以伪静态重写是这样的。当然还有其他的重写方式可以尝试。 之后在 /etc/nginx/nginx.conf 中的 http{} 中添加一行

1
include /etc/nginx/vhosts/cqc.conf;

则代表引用了这个文件。 注意,还要把 域名设置一下,添加一条A记录到主机上。 好了,一切大功告成了。

后记

迁移和配置的过程坑实在是太多了,列列吧,警醒世人呐。 (1)代码迁移过程上传git,整个项目差不多1个G,由于数据量太大,导致内存不够无法正常上传。后来删除了图片,发现项目还是很大,结果发现是 .git 目录已经占用了上百兆,后来打包排除这个目录迁移的。 (2)数据库迁移的时候由于phpMyAdmin上传大小限制,修改了一番上传大小结果发现没生效,还倒腾了一下php-fpm,后来发现可以直接上传压缩包,那就分分钟完成了。 (3)配置完之后发现网站首页正常访问了,可是其他页面全部出现了404错误,后来配置了一番伪静态解析发现配置代码直接写在了 localhost server里面,后来发现可以直接新写一个 server,然后配置域名servername,然后配置伪静态重写才成功。 总之,坎坷是多,但是,自己慢慢摸索出来,也是一种不错的体验。 当你成功之后,会觉得世界又是那么美好。

Java

之前是在eclipse上写的,后面换成了android sudio。 2048游戏的UI整体可以采用线性布局,即LinearLayout,其中嵌套一个线性布局和一个GridLayout,内嵌的线性布局填充文本框,以显示分数,GridLayout中填充4x4的继承自FrameLayout的card类作为主要的游戏界面。由于大部分操作都在GridLayout中进行,可以自定义一个继承自GridLayout的类GameView,类中定义判定上下左右滑动的方法和每次滑动后自动添加一个随机数字的方法以及每次滑动后判断游戏是否可以继续进行的方法。 主布局activity_main.xml代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
//match_parent表示布局充满整个屏幕
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.administractor.game2048.MainActivity"
//里面的组件垂直放置
android:orientation="vertical"
tools:ignore="MergeRootFrame">

<LinearLayout
//宽度充满整个屏幕,高度自适应。
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
//显示当前分数的文本框
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Your Score:"/>
<TextView
android:id="@+id/tvScore"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
//使用自定义的GridLayout
<com.example.administractor.game2048.GameView
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:id="@+id/GameView" >
</com.example.administractor.game2048.GameView>
</LinearLayout>

GameView.java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
package com.example.administrator.game2048;

import java.util.ArrayList;
import java.util.List;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Point;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.GridLayout;

public class GameView extends GridLayout {
//调用类构造方法
public GameView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
//初始化游戏
InitGameView();
}

public GameView(Context context, AttributeSet attrs) {
super(context, attrs);
InitGameView();
}

public GameView(Context context) {
super(context);
InitGameView();
}
private void InitGameView(){
//设置为4x4个方格
setColumnCount(4);
//设置背景颜色
setBackgroundColor(0xffeee4da);

//判定滑动方向
setOnTouchListener(new OnTouchListener() {
private float startx,starty,offsetx,offsety;
@Override
public boolean onTouch(View v, MotionEvent event) {
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
startx=event.getX();
starty=event.getY();
break;
case MotionEvent.ACTION_UP:
offsetx=event.getX()-startx;
offsety=event.getY()-starty;
if(Math.abs(offsetx)>Math.abs(offsety)){
if(offsetx<-5){
swipeLeft();
}else if(offsetx>5){
swipeRight();
}
}else{
if(offsety<-5){
swipeUp();
}else if(offsetx>3){
swipeDown();
}
}
break;
}
return true;
}
});
}
//适应不同大小的屏幕
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
int cardWidth=(Math.min(h, w))/4;
addCards(cardWidth,cardWidth);
startGame();
}
//在4x4的方格上添加满卡片
public void addCards(int cardwidth,int cardheight){
Card c;
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 4; x++) {
c=new Card(getContext());
c.setNum(0);
addView(c, cardwidth, cardheight);
cardmap[x][y]=c;
}
}
}
//游戏开始时每个卡片默认值设为0,并随机添加两张带数字的卡片
private void startGame(){
MainActivity.getMainActivity().clearScore();
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 4; x++) {
cardmap[x][y].setNum(0);
}
}
addRandomNum();
addRandomNum();
}
private void addRandomNum() {
//使用emptypoints将数字为0的card提取出来,并随即选择一个空card赋值
emptyPoints.clear();
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 4; x++) {
if(cardmap[x][y].getNum()<=0){
emptyPoints.add(new Point(x,y));
}
}
}
Point p=emptyPoints.remove((int)(Math.random()*emptyPoints.size()));
//2和4出现的概率控制在1:9
cardmap[p.x][p.y].setNum(Math.random()>0.1?2:4);
}
//左滑方法
private void swipeLeft(){
//merge作为判断能否滑动的flag
boolean merge = false;
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 4; x++) {
for (int x1 = x+1; x1 <4; x1++) {
if(cardmap[x1][y].getNum()>0){
if(cardmap[x][y].getNum()<=0){
cardmap[x][y].setNum(cardmap[x1][y].getNum());
cardmap[x1][y].setNum(0);
merge=true;
x--;
}else if(cardmap[x][y].equal(cardmap[x1][y])){
cardmap[x][y].setNum(cardmap[x][y].getNum()*2);
cardmap[x1][y].setNum(0);
MainActivity.getMainActivity().addScore(cardmap[x][y].getNum());
merge=true;
}
break;
}
}
}
}
if(merge){
addRandomNum();
checkComplete();
}
}
//下滑
private void swipeDown(){

boolean merge = false;

for (int x = 0; x < 4; x++) {
for (int y = 3; y >=0; y--) {

for (int y1 = y-1; y1 >=0; y1--) {
if (cardmap[x][y1].getNum()>0) {

if (cardmap[x][y].getNum()<=0) {
cardmap[x][y].setNum(cardmap[x][y1].getNum());
cardmap[x][y1].setNum(0);

y++;
merge = true;
}else if (cardmap[x][y].equal(cardmap[x][y1])) {
cardmap[x][y].setNum(cardmap[x][y].getNum()*2);
cardmap[x][y1].setNum(0);
MainActivity.getMainActivity().addScore(cardmap[x][y].getNum());
merge = true;
}

break;
}
}
}
}

if (merge) {
addRandomNum();
checkComplete();
}
}
//上滑
private void swipeUp(){

boolean merge = false;

for (int x = 0; x < 4; x++) {
for (int y = 0; y < 4; y++) {

for (int y1 = y+1; y1 < 4; y1++) {
if (cardmap[x][y1].getNum()>0) {

if (cardmap[x][y].getNum()<=0) {
cardmap[x][y].setNum(cardmap[x][y1].getNum());
cardmap[x][y1].setNum(0);

y--;

merge = true;
}else if (cardmap[x][y].equal(cardmap[x][y1])) {
cardmap[x][y].setNum(cardmap[x][y].getNum()*2);
cardmap[x][y1].setNum(0);
MainActivity.getMainActivity().addScore(cardmap[x][y].getNum());
merge = true;
}

break;

}
}
}
}

if (merge) {
addRandomNum();
checkComplete();
}
}
//右滑
private void swipeRight(){
boolean merge = false;
for (int y = 0; y < 4; y++) {
for (int x = 3; x >=0; x--) {
for (int x1 = x-1; x1 >=0; x1--) {
if(cardmap[x1][y].getNum()>0){
if(cardmap[x][y].getNum()<=0){
cardmap[x][y].setNum(cardmap[x1][y].getNum());
cardmap[x1][y].setNum(0);
x++;
merge=true;
}else if(cardmap[x][y].equal(cardmap[x1][y])){
cardmap[x][y].setNum(cardmap[x][y].getNum()*2);
cardmap[x1][y].setNum(0);
MainActivity.getMainActivity().addScore(cardmap[x][y].getNum());
merge=true;
}
break;
}
}
}
}
if(merge){
addRandomNum();
checkComplete();
}
}
//如果有空卡片或者相邻的值相同卡片则游戏还能进行
public void checkComplete(){
boolean complete=true;
ALL:
for (int y = 0; y <4; y++) {
for (int x = 0; x <4; x++) {
if(cardmap[x][y].getNum()==0||
x>0&&cardmap[x][y].equal(cardmap[x-1][y])||
x<3&&cardmap[x][y].equal(cardmap[x+1][y])||
y>0&&cardmap[x][y].equal(cardmap[x][y-1])||
y<3&&cardmap[x][y].equal(cardmap[x][y+1])){
complete=false;
break ALL;
}
}
}
//游戏结束弹出alert提示窗口
if(complete){
new AlertDialog.Builder(getContext()).setTitle("大林哥温馨提示").setMessage("游戏结束").setPositiveButton("重来",new DialogInterface.OnClickListener() {

@Override
public void onClick(DialogInterface arg0, int arg1) {
startGame();
}
}).show();
}

}
private Card[][] cardmap=new Card[4][4];
private List<Point> emptyPoints=new ArrayList<Point>();
}

主类MainActivity.java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package com.example.administrator.game2048;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class MainActivity extends Activity {

public MainActivity(){
mainActivity=this;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvscore = (TextView) findViewById(R.id.tvScore);
}
public void clearScore(){
score=0;
showScore();
}
public void showScore(){
tvscore.setText(score+"");
}
public void addScore(int s){
score+=s;
showScore();
}
private TextView tvscore;
private int score=0;
public static MainActivity mainActivity=null;
public static MainActivity getMainActivity() {
return mainActivity;
}
}

Card.java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
package com.example.administrator.game2048;

import android.content.Context;
import android.view.Gravity;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.TextView;

public class Card extends FrameLayout {

public Card(Context context) {
super(context);
LayoutParams lp = null;

View background = new View(getContext());
//参数-1表示layoutparams填充满整个父容器
lp = new LayoutParams(-1, -1);
//设置卡片之间有10像素的间隔
lp.setMargins(10, 10, 0, 0);
background.setBackgroundColor(0x33ffffff);
addView(background, lp);

label = new TextView(getContext());
label.setTextSize(28);
label.setGravity(Gravity.CENTER);

lp = new LayoutParams(-1, -1);
lp.setMargins(10, 10, 0, 0);
addView(label, lp);

setNum(0);
}



private int n=0;
public int getNum(){
return n;
}
//设置数字及对应的背景颜色
public void setNum(int n){
this.n=n;
if(n<=0){
label.setText("");
}else{
label.setText(n+"");
}
switch (n) {
case 0:
label.setBackgroundColor(0x00000000);
break;
case 2:
label.setBackgroundColor(0xffeee4da);
break;
case 4:
label.setBackgroundColor(0xffede0c8);
break;
case 8:
label.setBackgroundColor(0xfff2b179);
break;
case 16:
label.setBackgroundColor(0xfff59563);
break;
case 32:
label.setBackgroundColor(0xfff67c5f);
break;
case 64:
label.setBackgroundColor(0xfff65e3b);
break;
case 128:
label.setBackgroundColor(0xffedcf72);
break;
case 256:
label.setBackgroundColor(0xffedcc61);
break;
case 512:
label.setBackgroundColor(0xffedc850);
break;
case 1024:
label.setBackgroundColor(0xffedc53f);
break;
case 2048:
label.setBackgroundColor(0xffedc22e);
break;
default:
label.setBackgroundColor(0xff3c3a32);
break;
}
}
//判断卡片是否相等
public boolean equal(Card o){
return getNum()==o.getNum();
}
private TextView label;
}

Net

openvpn原理

VPN直译就是虚拟专用通道,是提供给企业之间或者个人与公司之间安全数据传输的隧道,OpenVPN无疑是Linux下开源VPN的先锋,提供了良好的性能和友好的用户GUI。 它大量使用了OpenSSL加密库中的SSLv3/TLSv1协议函数库。 目前OpenVPN能在Solaris、Linux、OpenBSD、FreeBSD、NetBSD、Mac OS X与Microsoft Windows以及Android和iOS上运行,并包含了许多安全性的功能。它并不是一个基于Web的VPN软件,也不与IPsec及其他VPN软件包兼容。 openvpn通过使用公开密钥(非对称密钥,加密解密使用不同的key,一个称为Publice key,另外一个是Private key)对数据进行加密的。这种方式称为TLS加密。 openvpn使用TLS加密的工作过程是,首先VPN Sevrver端和VPN Client端要有相同的CA证书,双方通过交换证书验证双方的合法性,用于决定是否建立VPN连接。 然后使用对方的CA证书,把自己目前使用的数据加密方法加密后发送给对方,由于使用的是对方CA证书加密,所以只有对方CA证书对应的Private key才能解密该数据,这样就保证了此密钥的安全性,并且此密钥是定期改变的,对于窃听者来说,可能还没有破解出此密钥,VPN通信双方可能就已经更换密钥了。 扩展阅读: openvpn

安装openvpn

首先,你需要有一台长期运行的服务器,大家可以用自己的闲置的电脑或者买一台阿里云啦。 我的服务器是Ubuntu 14.04,下面就演示一下我的配置过程。 安装

1
sudo apt-get -y install openvpn libssl-dev openssl

查看下版本并记录下来

1
openvpn --version

20151030145358 在这里我们的版本是2.3.2

安装easy-rsa

easy-rsa是用来制作openvpn相关证书的,使用如下命令安装

1
sudo apt-get -y install easy-rsa

好,一切准备就绪后,我们就开始制作证书啦,我们需要制作的有三个证书 CA证书、Server端证书、Client端证书。行动起来。

制作CA证书

openvpn与easy-rsa安装完毕后,我们需要在/etc/openvpn/目录下创建easy-rsa文件夹,如下

1
sudo mkdir /etc/openvpn/easy-rsa/

然后把/usr/share/easy-rsa/目录下的所有文件全部复制到/etc/openvpn/easy-rsa/下

1
sudo cp -r /usr/share/easy-rsa/* /etc/openvpn/easy-rsa/

当然,我们也可以直接在/usr/share/easy-rsa/制作相关的证书,但是为了后续的管理证书的方便,我们还是把easy-rsa放在了openvpn的启动目录下。 注意:由于我们现在使用的是ubuntu系统,所以我们必须切换到root用户下才能制作相关证书,否则easy-rsa会报错。如果是centos系统,则不存在此问题。 切换到root用户下,使用如下命令:

1
sudo su

在开始制作CA证书之前,我们还需要编辑vars文件,修改如下相关选项内容即可

1
sudo vi /etc/openvpn/easy-rsa/vars
1
2
3
4
5
6
7
export KEY_COUNTRY="CN"
export KEY_PROVINCE="SD"
export KEY_CITY="JiNan"
export KEY_ORG="germy"
export KEY_EMAIL="cqc@cuiqingcai.com"
export KEY_OU="germy"
export KEY_NAME="germy"

如图所示 20151030150125 之后,我们需要利用这个文件来制作我们的证书,保存一下。 然后一个很重要的一步,赋予权限,否则在制作证书的时候,值还是初始化的值。

1
sudo chmod 777 /etc/openvpn/easy-rsa/vars

vars文件主要用于设置证书的相关组织信息,红色部分的内容可以根据自己的实际情况自行修改。 其中export KEY_NAME=”germy” 这个要记住下,我们下面在制作Server端证书时,会使用到。 注意:以上内容,我们也可以使用系统默认的,也就是说不进行修改也是可以使用的。 然后使用source vars命令使其生效,如下:

1
2
source vars
./clean-all

注意:执行clean-all命令会删除,当前目录下的keys文件夹。 现在开始正式制作CA证书,使用如下命令:

1
2
cd /etc/openvpn/easy-rsa/
./build-ca

一路回车即可。 制作完成后,我们可以查看keys目录里有什么东西。 如果你的目录下出现了ca.crt和ca.key两个文件,其中ca.crt就是我们所说的CA证书。如此,CA证书制作完毕。 现在把该CA证书的ca.crt文件复制到openvpn的启动目录/etc/openvpn下,如下:

1
cp keys/ca.crt /etc/openvpn/

制作Server端证书

CA证书制作完成后,我们现在开始制作Server端证书。如下:

1
./build-key-server germy

上述命令中germy,就是我们前面vars文件中设置的KEY_NAME 查看 keys 目录 20151030150704 如果可以发现出现了 germy.crt,germy.csr,germy.key 文件,就说明成功了。 现在再为服务器生成加密交换时的Diffie-Hellman文件,如下:

1
./build-dh

你会发现目录下多了一个 dh2048.pem 文件。 以上操作完毕后,把germy.crt,germy.key,dh2048.pem 复制到 /etc/openvpn/ 目录下,如下:

1
2
cd /etc/openvpn/easy-rsa/
cp keys/germy.crt keys/germy.key keys/dh2048.pem /etc/openvpn/

如此,Server端证书就制作完毕。

制作Client端证书

Server端证书制作完成后,我们现在开始制作Client端证书,如下:

1
./build-key cqc

其中上述命令的cqc就是客户端证书名称,可以自定义 如果发现keys目录已经生成了cqc.csr、cqc.crt和cqc.key这个三个文件。其中cqc.crt和cqc.key两个文件是我们要使用的。 如此,Client端证书就制作完毕。

配置Server端

所有证书制作完毕后,我们现在开始配置Server端。Server端的配置文件,我们可以从openvpn自带的模版中进行复制。如下:

1
2
cp /usr/share/doc/openvpn/examples/sample-config-files/server.conf.gz /etc/openvpn/
cd /etc/openvpn/

解压server.conf.gz 文件,使用如下命令:

1
gzip -d server.conf.gz

注意:上述命令的意思是解压server.conf.gz文件后,然后删除原文件。 现在我们来修改server.conf文件 20151030151728 一共要修改3处文件 (1)修改了openvpn运行时使用的协议,由原来的UDP协议修改为TCP协议。生成环境建议使用TCP协议。 (2)修改了openvpn服务器的相关证书,由原来的server.csr、server.key修改为germy.crt、germy.key。 (3)修改了Diffie-Hellman文件,由原来的dh1024.pem修改为dh2048.pem。 配置文件修改完毕后,我们现在来启动openvpn,使用如下命令:

1
/etc/init.d/openvpn start

至此,服务器端的VPN已经配置完毕了。

客户端的配置

服务器端配置好了,我们需要用另一台机器来连接,这里我们的客户端依然是Ubuntu 14.04 首先我们需要从服务器上取到刚才生成的证书文件,那么我们需要的有什么呢? 20151030152200 首先这三个,ca.crt,cqc.crt,cqc.key 另外是一个模板,它是 /usr/share/doc/openvpn/examples/sample-config-files/client.conf 把这四个文件下载下来,然后放到客户端里。 比如我们保存到客户机的 home/user 文件夹下 20151030152421 把 client.conf 文件重命名为 client.ovpn 然后修改下面4处

1
2
3
4
5
proto tcp
remote 121.42.14.158 1194
ca ca.crt
cert cqc.crt
key cqc.key

其中 remote 就是你的服务器地址 配置好了之后,我们运行

1
sudo openvpn --config client.ovpn

如果最后的结果是 Sequence Completed 那就证明连接成功啦。 输入

1
ifconfig

你会发现多了一个tun0适配器,这就是openvpn的适配器。 至此,openvpn的配置和连接就全部完成啦。

参考来源

参考文献 如有问题,欢迎留言交流。

HTML

HTML5中包含一个帮助检测device orientation的特性,使用这个特性可以在移动设备浏览器中判断用户设备的旋转重力方向。

基本知识

Alpha, Beta, Gamma角度旋转。 当用户旋转手机的时候,HTML5中定义了三个轴方向的旋转,如下: 上图可以看考,分别是z,x,y轴,对应分别是:Alpha,Beta,Gamma,下面图将更清楚的展示: 上图是Alpha旋转, 围绕Z轴旋转(绿线旋转方向,水平) 上图是Beta旋转, 围绕X轴旋转(绿线旋转方向,前后) 上图是Beta旋转, 围绕Y轴旋转(绿线旋转方向,左右)

属性

  • alpha: (float 类型 )以z方向为轴心的旋转角度 浮点数类型,只读属性,取值范围为0到360(不等于360)。
  • beta: (float 类型 )以x方向为轴心的旋转角度 浮点数类型,只读属性,取值范围为-180到180(不等于180)。
  • gamma: (float 类型 )以y方向为轴心的旋转角度 浮点数类型,只读属性,取值范围为-180到180(不等于180)。

参考

原文链接

Net

综述

在上一篇文章中,客户机可以借助路由机直接上网,并没有什么登录限制。接下来我们将加入上网登录验证,只有输入了正确的用户名和密码才可以通过验证,然后才可以访问互联网。 接下来,就跟随我用PHP来实现登录验证吧。

环境配置

在这之前,你需要配置一下LAMP环境,也就是Apache,MySQL,PHP开发环境,依次执行如下命令即可。

1
2
3
4
5
6
7
sudo apt-get install apache2
sudo apt-get install php5 php5-cgi php5-mysql php5-curl php5-gd php5-idn php-pear php5-imagick php5-imap php5-mcrypt php5-memcache php5-mhash php5-ming php5-pspell php5-recode php5-snmp php5-tidy php5-xmlrpc php5-sqlite php5-xsl
sudo apt-get install mysql-server mysql-client
sudo apt-get install libapache2-mod-php5
sudo apt-get install libapache2-mod-auth-mysql
sudo apt-get install phpmyadmin
sudo ln -s /usr/share/phpmyadmin/ /var/www/html/phpmyadmin

如果配置出现问题,请查阅相关资料。 apache默认的目录为 /var/www/html,我们这时访问 localhost 或者 192.168.122.4,都可以出现apache的欢迎界面,就证明我们配置成功了。

路由初始设置

为了在登录之前限制主机的上网,我们需要利用iptables规则来对数据包的转发加以限制。同时,将网页重定向到本机的登录界面。 初始路由设置如下

1
2
3
4
5
6
7
8
9
iptables -F
iptables -t nat -F
iptables -t mangle -F
iptables -P INPUT ACCEPT
iptables -P FORWARD ACCEPT
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
iptables -t filter -A FORWARD -s 192.168.122.0/24 -o eth0 -j REJECT
iptables -t filter -A FORWARD -s 192.168.122.0/24 -d 119.29.29.29/32 -j ACCEPT
iptables -t nat -A PREROUTING -s 192.168.122.0/24 -p tcp -j DNAT --to 192.168.122.4

首先清除所有的iptables规则,然后设置前一篇我们说的IP伪装,这时可以客户机可以通过主机上网。 接下来的一条规则则禁用了来自 192.168.122.0 网段的所有IP的数据包转发,然后设置可访问DNS服务器,最后一条则设置了所有的tcp连接自动跳转到 192.168.122.4,也就是我们刚才配置的服务器。 可以把以上规则保存为脚本,比如叫 init.sh 来运行,也可以添加到 /etc/rc.local 中,开机自动运行。

登录页面

访问到192.168.122.4时,我们需要给用户呈现的当然不是刚才显示的apache欢迎页面,而是登录的输入框以及登录按钮界面。 所以,登录界面代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Auth Login</title>

<!-- Bootstrap -->
<link rel="stylesheet" href="css/bootstrap.min.css">
</head>
<body>
<form id="auth" method="post">
<div class="input-group">
<span class="input-group-addon" id="basic-addon1">Username</span>
<input type="text" class="form-control" placeholder="Username" aria-describedby="basic-addon1" name="username">
</div>
<div class="input-group">
<span class="input-group-addon" id="basic-addon1">Password</span>
<input type="text" class="form-control" placeholder="Password" aria-describedby="basic-addon1" name="password">
</div>
<input type="button" id="login" class="btn btn-primary" value="Login">
<input type="button" id="logout" class="btn btn-primary" value="Logout">
</form>
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="js/jquery.min.js"></script>
<!-- Include all compiled plugins (below), or include individual files as needed -->
<script src="js/bootstrap.min.js"></script>
</body>
<style>
form {
max-width:400px;
margin:0 auto;
}
.input-group {
margin-bottom:20px;
}
</style>
<script>
$(function(){
$("#login").on("click", function() {
$("#auth").attr("action", "/login.php");
$("#auth").submit();
});
$("#logout").on("click", function() {
$("#auth").attr("action", "/logout.php");
$("#auth").submit();
});
});
</script>
</html>

其中的js,jquery文件请大家自行引入。 预览一下效果 20151008151728 在这里我们设置了两个按钮,一个是登录,一个是下线。

数据库查询验证

接下来我们新建一个数据库,例如我新建了一个数据库叫auth,然后数据表user,里面有三个字段。分别是id,username,password,我插入了一条数据。 20151008152059 接下来我们就尝试一下登录,提交到 login.php 文件验证一下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?php
$mysql_server_name = "localhost";
$mysql_username = "root";
$mysql_password = "123456";
$mysql_database = "auth";
$username = @$_POST['username'];
$password = @$_POST['password'];
$ip=$_SERVER["REMOTE_ADDR"];
$conn=mysql_connect($mysql_server_name, $mysql_username,
$mysql_password);
if ($conn) {
$sql = "select * from user where username = '".$username."'";
$result = mysql_fetch_array(mysql_db_query($mysql_database, $sql, $conn));
if ($result) {
if ($result['password'] == $password) {
$status = -1;
system("sudo ./bash/login.sh $ip", $status);
if ($status == 0) {
echo "Login Successfully";
} else {
echo "Login Failed";
}
} else {
echo "Wrong Password";
}
} else {
echo "Not";
}
} else {
die("Could Not Connect");
}
?>

其中,最重要的部分莫过于

1
system("sudo ./bash/login.sh $ip", $status);

这一行代码了,此处便是登录验证用户名和密码之后执行的一个 Linux 脚本命令。 在这里我把要执行的脚本写入了login.sh文件中,传入的参数便是ip地址。 那么login.sh里面发生了什么事情呢,我们来看一下。

1
2
3
4
5
6
iptables -t nat -D PREROUTING -s $1/32 -j ACCEPT
iptables -t nat -D PREROUTING -s $1/32 -p tcp -j ACCEPT
iptables -t filter -D FORWARD -s $1/32 -o eth0 -j ACCEPT
iptables -t nat -I PREROUTING -s $1/32 -j ACCEPT
iptables -t nat -I PREROUTING -s $1/32 -p tcp -j ACCEPT
iptables -t filter -I FORWARD -s $1/32 -o eth0 -j ACCEPT

$1的意思就是获取第一个参数,在这里就是IP地址,脚本主要做的事情就是放行来自这个IP地址的数据包,让其正常访问互联网。 保存脚本后,记得给脚本赋予权限

1
sudo chmod 777 login.sh

-D的意思就是删除,因为iptables是可以添加多次相同的规则的,在添加之前删除一下,以防止多次添加。 在这里

1
sudo ./bash/login.sh $ip

执行命令脚本前,我们加了sudo,意思就是管理员身份运行,但是仍然可能导致权限问题,因为命令的执行者是PHP(其实是www-data),而并不是root用户,所以我们需要修改一下执行权限。 首先通过PHP文件获取执行该命令的用户是叫什么,比如新建一个 info.php 文件,输入如下内容:

1
2
3
<?php 
echo shell_exec("id -a");
?>

看一下运行结果 20151008153424 嗯,果然,执行用户是www-data,这样我们只需要给www-data添加一个执行权限就好了。 修改 /etc/sudoers 文件 添加一行

1
www-data ALL=(ALL) NOPASSWD:ALL

意思是www-data以root身份运行并且不需要密码。 20151008153700 好,保存之后,我们尝试一下,就可以登录啦。

测试登录

在路由主机(Ubuntu Route)里面,初始化一下iptables规则,然后查看当前规则。 我们发现当前访问都是被阻止的,而且tcp连接会自动跳转到 192.168.122.4 20151008154216 现在我们登录客户机,随机打开一个网址,比如百度,就发现自动跳转到了登录界面 20151008154629 输入用户名密码,尝试登陆,比如之前插入数据库的是cqc,123456,输入之后登录。 20151008162712 提示登录成功之后,我们便可以欢乐地上网啦。 20151008162926 好,这样我们就完成了验证之后上网啦。

下线操作

同样的,下线操作我们同样写一个logout.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?php
$mysql_server_name = "localhost";
$mysql_username = "root";
$mysql_password = "123456";
$mysql_database = "auth";
$username = @$_POST['username'];
$password = @$_POST['password'];
$ip=$_SERVER["REMOTE_ADDR"];
echo $ip;
$conn=mysql_connect($mysql_server_name, $mysql_username,
$mysql_password);
if ($conn) {
$sql = "select * from user where username = '".$username."'";
$result = mysql_fetch_array(mysql_db_query($mysql_database, $sql, $conn));
if ($result) {
if ($result['password'] == $password) {
$status = -1;
system("sudo ./bash/logout.sh $ip", $status);
if ($status == 0) {
echo "Login Successfully";
} else {
echo "Login Failed";
}
} else {
echo "Wrong Password";
}
} else {
echo "Not";
}
} else {
die("Could Not Connect");
}
?>

登出的脚本如下,其实就是单纯去除了刚才添加的路由规则

1
2
3
iptables -t nat -D PREROUTING -s $1/32 -j ACCEPT
iptables -t nat -D PREROUTING -s $1/32 -p tcp -j ACCEPT
iptables -t filter -D FORWARD -s $1/32 -o eth0 -j ACCEPT

配置方式和登录一样,大家可以尝试下。

源代码

在这里提供大家源代码下载 源码下载 如有问题,欢迎交流。

Net

综述

大家好,这次我们需要实现的是实现双网卡主机共享上网,就是一台主机通过连接另一台可以访问外网的双网卡主机来正常上网。所以我们需要两台机器来进行测试,在这里我们用的是两台Ubuntu 14.04,其中一台是单网卡,一台是双网卡。废话不多说,行动起来吧。

配置系统

博主使用了Vmware来安装了两台Ubuntu主机,一台当路由机,名称是Ubuntu Route,另一台是客户机,名称是Ubuntu Desktop,具体的网络配置如下: Ubuntu Route: 一个网卡eth0通过NAT方式来与外部主机共享上网,这个网卡也就是VMnet8网卡,网段是192.168.231.0 20151007164043 另一个网卡eth1连接了一个自定义的仅主机模式的网卡VMnet2,网段是192.168.122.0 20151007164114 网络适配器设置如下,eth0开启了DHCP,ech1没有开启DHCP 20151007164343 Ubuntu Desktop: 一个网卡eth0连接刚才那个自定义的仅主机模式的网卡VMnet2,网段是192.168.122.0 20151007164821 好了,以上就是基本硬件的配置

设置IP

接下来我们设置一下Ubuntu Route的IP地址,修改 /etc/network/interfaces

1
2
3
4
5
6
7
8
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet dhcp
auto eth1
iface eth1 inet static
address 192.168.122.4
netmask 255.255.255.0

在这里,eth0因为我们在VMware里面设置了DHCP,所以这里我们设置dhcp即可,eth1需要手动配置一下,我们分配了 192.168.122.4 这个IP地址,当然你可以随意指定,子网掩码如上,不需要写网关,因为它本身作为一个路由。 可以通过执行如下命令来使之生效

1
sudo /etc/init.d/networking restart

如果上述方法不行,则可以尝试使用关闭网卡和开启网卡的命令。

1
2
sudo ifup eth0
sudo ifdown eth0

eth1的开启和关闭同上

开启路由转发

修改 /etc/sysctl.conf 文件,将

1
net.ipv4.ip_forward=1

这一行取消注释,代表开启了路由转发功能。 也可以通过执行

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

命令来实现

设置iptables规则

iptables是非常重要的一个环节,如果大家不熟悉,可以去搜相关资料了解一下。 执行如下命令,来设置一下iptables规则,可以直接在命令行逐条执行,也可以写成一个脚本来执行。

1
2
3
4
5
6
iptables -F
iptables -t nat -F
iptables -t mangle -F
iptables -P INPUT ACCEPT
iptables -P FORWARD ACCEPT
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

其中最后一条是最重要的,代表将数据包通过eth0网卡来转发,也是IP伪装的一个常用方法,有了这条指令,从eth1网卡流经的一些数据包可以通过eth0来转发,这样就相当于连通了两个网卡,这样与eth1网卡连接的主机便可以上网了。

客户主机设置

因为客户机的eth0连接了VMnet2网卡,而VMnet2网卡又与路由主机的eth1连接,我们只需要简单设置一下IP地址就好了。 修改 /etc/network/interfaces

1
2
3
4
5
6
auto eth0
iface eth0 inet static
address 192.168.122.5
netmask 255.255.255.0
gateway 192.168.122.4
dns-nameservers 119.29.29.29

这里很重要的一个设置就是网关,设置成路由主机的IP地址。 设置完了同样重启一下网卡使其生效。 还可以选择性设置下DNS服务器。 至此,所有配置都完成了,测试一下吧。

测试

我们在客户机里打开浏览器,输入随意一个网页测试一下。 20151007172000 嗯,客户机可以正常上网啦,一切都是那么轻松加愉快! 如有问题,欢迎留言交流~