Linux 发展到今天,可用的软件已经非常多了。这样自然会有一些软件的功能大致上相同。例如,同样是编辑器,就有
nvi、vim、emacs、nano,而且我说的这些还只是一部分。大多数情况下,这样的功能相似的软件都是同时安装在系统里的,可以用它们的名称来执
行。例如,要执行 vim,只要在终端下输入 vim
并按回车就可以了。不过,有些情况下我们需要用一个相对固定的命令调用这些程序中的一个。例如,当我们写一个脚本程序时,只要写下
editor,而不希望要为“编辑器是哪个”而操心。Debian 提供了一种机制来解决这个问题,而 update-alternatives
就是用来实现这种机制的。
在说明 update-alternatives 的详细内容之间,先让我们看看系统中已有的例子。打开终端,执行下面的命令:
herbert@natsu:~$ ls -l /usr/bin/editor
lrwxrwxrwx 1 root root 24 2004-09-26 08:48 /usr/bin/editor -> /etc/alternatives/editor
herbert@natsu:~$ ls -l /etc/alternatives/editor
lrwxrwxrwx 1 root root 12 2004-10-27 16:24 /etc/alternatives/editor -> /usr/bin/vim
herbert@natsu:~$
我
们看到,editor 这个可执行命令实际上是个符号链接,它指向 /etc/alternatives/editor;而
/etc/alternatives/editor 也是个符号链接,它指向 /usr/bin/vim。这样,当我输入 editor
并回车时,将执行 vim。之所以要在 /usr/bin 和 /etc/alternatives
中费心建立这样两个链接,就是要实现上面说到的特性:方便脚本
程序的编写和系统的管理。
下面我们就来看看 update-alternatives 的功能。当然,如果你觉得我说得不详细,可以看看这个命令的 manpage:UPDATE-ALTERNATIVES(8)。
首先要介绍的参数是 --display。它使我们可以看到一个命令的所有可选命令。执行
natsu:/home/herbert# update-alternatives --display editor
editor - status is auto.
link currently points to /usr/bin/vim
/bin/ed - priority -100
slave editor.1.gz: /usr/share/man/man1/ed.1.gz
/usr/bin/nvi - priority 19
slave editor.1.gz: /usr/share/man/man1/nvi.1.gz
/bin/nano - priority 40
slave editor.1.gz: /usr/share/man/man1/nano.1.gz
/usr/bin/vim - priority 120
slave editor.1.gz: /usr/share/man/man1/vim.1.gz
/usr/bin/emacs21 - priority 0
slave editor.1.gz: /usr/share/man/man1/emacs.1emacs21.gz
Current `best' version is /usr/bin/vim.
natsu:/home/herbert#
你可以看到我的机器上的所有可以用来被 editor 链接的命令。
下面说说 --config。这个选项使我们可以选择其中一个命令:
natsu:/home/herbert# update-alternatives --config editor
There are 5 alternatives which provide `editor'.
Selection Alternative
-----------------------------------------------
1 /bin/ed
2 /usr/bin/nvi
3 /bin/nano
*+ 4 /usr/bin/vim
5 /usr/bin/emacs21
Press enter to keep the default[*], or type selection number: 4
Using `/usr/bin/vim' to provide `editor'.
natsu:/home/herbert#
我并没有修改它,因为我还是比较喜欢 vim 的。当然,你可以选择别的程序。
说
到这里我们就要介绍一些概念了。首先,update-alternatives 在一般情况下是由 postinst 和 prerm
这样的安装脚本自动调用的,所以一个 alternative 的状态有两种:自动和手动。每个 alternative
的初始状态都是自动。如果系统发现管理员手动修改了一个
alternative,它的状态就从自动变成了手动,这样安装脚本就不会更新它了。如果你希望将一个 alternative 变回自动,只要执行
update-alternatives --auto editor
就可以了。你注意到了吗?我们说到了“名字”。该怎样写名字呢?这就是我们要介绍的第二个概念:
general name -- 这是指一系列功能相似的程序的“公用”名字(包括绝对路径),比如 /usr/bin/editor。
link -- 这是指一个 alternative 在 /etc/alternative 中的名字,比如 editor。
alternative -- 顾名思义,这是指一个可选的程序所在的路径(包括绝对路径),比如 /usr/bin/vim。
--
auto,--display 和 --config 跟的都是
link。我们要说的第三个概念是优先级。这个比较简单,当然优先级越高的程序越好啦(在大多数情况下,我不想争论)最后一个概念是主和从的
alternative。想想看,你将 /usr/bin/editor 链接到了 vim,可是当你执行 man editor 时看到的却是
emacs 的 manpage,你会做何感想呢?这就引出了主和从 alternative 的概念了:当更新主的 alternative
时,从的 alternative 也会被更新。
说完这四个重要的概念后,我们介绍另外两个选项。至于其他的。。。。我相信你会去看手册页的,对吗?
第一个是 --install。它的格式是:
update-alternatives --install gen link alt pri [--slave sgen slink salt] ...
gen,
link,alt,pri 分别是我们上面说过的。如果需要从的 alternative,你可以用 --slave
加在后面。如果你在向一个已经存在的 alternative 组中添加新的 alternatives,该命令会把这些 alternatives
加入到这个已经存在的 alternative 组的
列表中,并用新的可选命令作为新的命令;否则,将会建立一个新的自动的 alternative 组。
呜呼!我加入了一个错误的 alternative。我不想要这个 alternative 了。在这种情况 下,可以执行下面的命令:
update-alternatives --remove name path
name
是一个在 /etc/alternatives 中的名字,也就是上面的 link,而 path
是希望删除的可选程序名的绝对路径名(放心,这样只是从列表中删除了这个程序,并不会真的从硬盘上删除程序的可执行文件)。如果从一个
alternative 组中删除了一个正在被链接的程序并且这个组仍然没有变成空的,update-alternatives
会自动用一个具有其他优先级的可选程序代替原来的程序。如果这个组变成空的了,那么连这个 alternative
组都会被移除。如果删除的程序没有被链接,则只有有关这个程序的信息会被移除。
说个例子吧。我下载了 Eclipse,并且安装了
gcj 和 gij。可是我发现 GNU 的 java 工具还不足以运行 Eclipse。我只好到 Sun 公司的网页上下载了它的 java
工具 jdk。因为是自己安装的,我将它们安装在 /usr/local 上,以便将来重新安装 Linux
系统时这些程序仍然可以使用。于是我要做的就是用这个 jdk 中的 java 和 javac 来代替系统原来的。执行
natsu:/home/herbert# update-alternatives --display java
java - status is auto.
link currently points to /usr/local/j2sdk1.4.2_06/bin/java
/usr/bin/gij-wrapper-3.3 - priority 33
slave java.1.gz: /usr/share/man/man1/gij-wrapper-3.3.1.gz
/usr/local/j2sdk1.4.2_06/bin/java - priority 100
slave java.1.gz: /usr/local/j2sdk1.4.2_06/man/man1/java.1
Current `best' version is /usr/local/j2sdk1.4.2_06/bin/java.
natsu:/home/herbert# update-alternatives --display javac
javac - status is auto.
link currently points to /usr/local/j2sdk1.4.2_06/bin/javac
/usr/bin/gcj-wrapper-3.3 - priority 33
slave javah: /usr/bin/gcjh-wrapper-3.3
slave javac.1.gz: /usr/share/man/man1/gcj-wrapper-3.3.1.gz
slave javah.1.gz: /usr/share/man/man1/gcjh-wrapper-3.3.1.gz
/usr/bin/gcj-wrapper-3.4 - priority 33
slave javah: /usr/bin/gcjh-wrapper-3.4
slave javac.1.gz: /usr/share/man/man1/gcj-wrapper-3.4.1.gz
slave javah.1.gz: /usr/share/man/man1/gcjh-wrapper-3.4.1.gz
/usr/local/j2sdk1.4.2_06/bin/javac - priority 100
slave javah: /usr/local/j2sdk1.4.2_06/bin/javah
slave javac.1.gz: /usr/local/j2sdk1.4.2_06/man/man1/javac.1
slave javah.1.gz: /usr/local/j2sdk1.4.2_06/man/man1/javah.1
Current `best' version is /usr/local/j2sdk1.4.2_06/bin/javac.
natsu:/home/herbert#
(你看到的是我更新以后的)就可以得到关于要更新哪些 alternatives 的信息。我是这么更新的:
update-alternatives
--install /usr/bin/javac javac /usr/local/j2sdk1.4.2_06/bin/javac 100
--slave /usr/bin/javah javah /usr/local/j2sdk1.4.2_06/bin/javah --slave
/usr/share/man/man1/javac.1.gz javac.1.gz
/usr/local/j2sdk1.4.2_06/man/man1/javac.1 --slave
/usr/share/man/man1/javah.1.gz javah.1.gz
/usr/local/j2sdk1.4.2_06/man/man1/javah.1
update-alternatives
--install /usr/bin/java java /usr/local/j2sdk1.4.2_06/bin/java 100
--slave /usr/share/man/man1/java.1.gz java.1.gz
/usr/local/j2sdk1.4.2_06/man/man1/java.1
posted @
2008-02-13 10:08 保尔任 阅读(998) |
评论 (0) |
编辑 收藏
远古时代计算机的操作系统都是文字命令行界面(CLI),随着技术的发展,苹果公司第一个制造了有桌面环境(DE)的计算机。
主流的操作系统:microsoft windows, mac os x, unix like中,前两者桌面环境不可变,但也有主题和第三方软件可以改变界面元素外观及界面本身。但unix like的ubuntu等操作系统是可以改变桌面环境的,例如:ubuntu使用Gnome桌面环境,kubuntu使用KDE桌面环境,Xubuntu 使用Xface桌面环境。
不同的桌面环境是由不同或相同的用户图形界面开发工具包(GUI toolkits)实现的,例如:
1、GTK+
GTK+基础
GTK+最初是GIMP的专用开发库,后来发展为Linux下开发图形界面的应用程序的主流开发工具之一。GTK+2.0是自由软件,并且是GNU工程的一部分。GTK+2.0的许可协议是LGPL。
GTK+使用C语言开发,但是其设计者使用面向对象技术。 在GNOME平台上提供了C++(gtkmm)、Perl、Ruby、Java和Python(PyGTK)绑定,其他的绑定有Ada、 D、 Haskell、PHP和所有的.NET编程语言。
与其他很多部件工具箱不同,GTK+并不基于Xt。这一决策优劣互见:优点是GTK+可以应用于其他系统,其灵活性也很强;而缺点就是它无法利用以传统方法为X11定制的X资源数据库。
Glade是一个界面设计工具,但是它也包含了一种描述GUI界面的XML语言,它和libglade一起工作就可以直接使用GTK和GNOME控件。
2、Qt
Qt简介
Qt是Trolltech公司的一个产品。
Qt是一个多平台的C++图形用户界面应用程序框架。它提供给应用程序开发者建立艺术级的图形用户界面所需的所用功能。Qt是完全面向对象的很容易扩展,并且 允许真正地组件编程。
自从1996年早些时候,Qt进入商业领域,它已经成为全世界范围内数千种成功的应用 程序的基础。Qt也是流行的Linux桌面环境KDE 的基础,KDE是所有主要的Linux发行版的一个标准组件。
Qt支持下述平台: MS/Windows - 95、98、NT 4.0、ME、和2000,
Unix/X11 - Linux、Sun Solaris、HP-UX、Compaq Tru64 UNIX、IBM AIX、SGI IRIX和其它很多X11平台
Macintosh - Mac OS X
Embedded - 有帧缓冲(framebuffer)支持的Linux平台。
3、wxWidgets
开源软件(LGPL),针对C++、Python以及Perl的跨平台工具集。wxWidget作为一个C++ GUI Library从1992发展到现在,已经在全球范围内产生了极大的影响力,利用这个类库进行GUI开发的软件更是不计其数。
wxWidgets原名wxWindows,是由Julian Smart于1992年还在英国爱丁堡大学人工智能应用研究所开始的一个项目。Julian Smart现在是Anthemion软件公司的技术总监。
简单地说,wxWidgets提供了用于可在多平台上开发GUI应用程序的一整套简单易学易用的API。wxWidgets支持的多平台包括 Windows 3.1/95/98/NT/2000/XP、带有Motif 1.2版本以上的Unix、带有GTK+的Unix/Linux/*BSD、Mac等等。开发者只需要用自己使用的系统平台的编译器编译源程序,并链接上相应的库文件,生成的程序代码即具有该平台的外观和界面。在这些GUI之上,wxWidgets还提供了在线帮助、网络编程、流处理、多种常用图形格式支持、数据库支持、HTML察看和打印,等等各种强大的功能。
4,MFC
只能用于windows平台图形界面开发。
wxWidgets和GTK+的一大吸引力是它的wxWidgets 2 licence类型是属于LGPL ( Lesser General Public License)的,也就是说,使用它开发的软件并不要求一定得公开源码。这样,无论是免费软件的开发者、GPL类型软件开发者,还是纯粹商业软件开发者都可以免费使用这个软件包。相比之下,TrollTech的Qt似乎正是因为此因素而影响了它的受接受程度,因为用Qt开发商用软件者所需交纳的费用对于个人
开发者来说还是比较昂贵的,而且分单平台、二平台、三平台(即Qt/X11、Qt/Windows和Qt/Mac都包含在内了)三个不同产品包,又分 Professional和Enterprise不同Edition亦即不同价位,而且开发人员数不同价格也不同。从这点说来,wxWidgets是绝对具有优势的。另外Qt起源于Linux上的开发,后来推展到Windows、Mac等其它平台;而wxWidgets恰好相反,始于Windows,其后移植到Unix等其它平台。
这些GUI toolkits除了作桌面环境这样的大东东外,还可以作应用程序的图形界面。还有一些基于特定语言或播放器或工具的图形界面工具/包也可以完成这样的工作,比如java的AWT/SWING,eclipse的SWT/JFace(基于eclipse),python的wxPython, micromedia的flash/flex(我觉得也算,只不过要在flash播放器或浏览器的fash插件上跑)
posted @
2008-01-04 14:57 保尔任 阅读(1210) |
评论 (0) |
编辑 收藏
推荐:PostgreSQL 8.1 中文文档12.2. 事务隔离:
http://www.itworld21.com/docs/db/postgresql8.1/transaction-iso.html
难点:不可重复读和幻读的区别:
不可重复读的重点是修改:
同样的条件, 你读取过的数据, 再次读取出来发现值不一样了
幻读的重点在于新增或者删除
同样的条件, 第1次和第2次读出来的记录数不一样
当然, 从总的结果来看, 似乎两者都表现为两次读取的结果不一致.
但如果你从控制的角度来看, 两者的区别就比较大
对于前者, 只需要锁住满足条件的记录
对于后者, 要锁住满足条件及其相近的记录
posted @
2007-12-29 14:40 保尔任 阅读(825) |
评论 (0) |
编辑 收藏
对于主/外键/索引来说,在一些开发团队中被认为是处理数据库关系的利器,也被某些开发团队认为是处理某些具体业务的魔鬼,您的观点呢?在实际应用中您会采取哪种方式?
大家共同观点:
主键和索引是不可少的,不仅可以优化数据检索速度,开发人员还省不其它的工作,
矛盾焦点:数据库设计是否需要外键。这里有两个问题:一个是如何保证数据库数据的完整性和一致性;二是第一条对性能的影响。
正方观点:
1,由数据库自身保证数据一致性,完整性,更可靠,因为程序很难100%保证数据的完整性,而用外键即使在数据库服务器当机或者出现其他问题的时候,也能够最大限度的保证数据的一致性和完整性。
eg:数据库和应用是一对多的关系,A应用会维护他那部分数据的完整性,系统一变大时,增加了B应用,A和B两个应用也许是不同的开发团队来做的。他们如何协调保证数据的完整性,而且一年以后如果又增加了C应用呢?
2,有主外键的数据库设计可以增加ER图的可读性,这点在数据库设计时非常重要。
3,外键在一定程度上说明的业务逻辑,会使设计周到具体全面。
反方观点:
1,可以用触发器或应用程序保证数据的完整性
2,过分强调或者说使用主键/外键会平添开发难度,导致表过多等问题
3,不用外键时数据管理简单,操作方便,性能高(导入导出等操作,在insert, update, delete 数据的时候更快)
eg:在海量的数据库中想都不要去想外键,试想,一个程序每天要insert数百万条记录,当存在外键约束的时候,每次要去扫描此记录是否合格,一般还不止一个字段有外键,这样扫描的数量是成级数的增长!我的一个程序入库在3个小时做完,如果加上外键,需要28个小时!
结论:
1,在大型系统中(性能要求不高,安全要求高),使用外键;在大型系统中(性能要求高,安全自己控制),不用外键;小系统随便,最好用外键。
2,用外键要适当,不能过分追求
3,不用外键而用程序控制数据一致性和完整性时,应该写一层来保证,然后个个应用通过这个层来访问数据库。
posted @
2007-12-29 13:16 保尔任 阅读(1438) |
评论 (1) |
编辑 收藏
1, insert Ubuntu 7.10 CD
a, format disc(primary 10G ext3; extend 59G ext3; swap 1G)
b, install(timezone shanghai; en_US; "prepare disc space" manual, or the system will partition autoly)
c, auto restart, go on install system(remenber cut off the net line except the netwidth is large, or it will cost long time to download from far away)
2, config
a, sources list
sudo vim /etc/apt/sources.list
# add "deb http://debian.exoweb.net/debian.cn99.com/debian etch main" into it
sudo apt-get update
sudo apt-get upgrade
b, vedio card driver
在ubuntu7.10下装nvidia 7 series显卡并配置双屏显示:
一,显卡驱动 + 双显示器
(修改X配置命令:sudo dpkg-reconfigure xserver-xorg)
1,到nvidia网站下载7系列显卡的最新驱动
2,ensure that the linux-restricted-modules or linux-restricted-modules-common packages have been uninstalled. Alternatively, you can edit the /etc/default/linux-restricted-modules or /etc/default/linux-restricted-modules-common configuration file and disable the NVIDIA linux-restricted kernel modules (nvidia, nvidia_legacy) via:
DISABLED_MODULES="nv nvidia_new"
3,
sudo apt-get remove --purge nvidia-glx nvidia-glx-new
sudo rm /etc/init.d/nvidia-glx /etc/init.d/nvidia-kernel /lib/linux-restricted-modules/.nvidia_new_installed
4,然后ctrl+alt+1进入tty1
sudo /etc/init.d/gdm stop
sudo sh NVIDIA-Linux-x86-100.14.23-pkg1.run
(这时会出现错误提示,说少了“libc header file...libc development package”)
sudo apt-get install sudo apt-get install build-essential xorg-dev pkg-config linux-headers-$(uname -r), libc6-dev
sudo sh NVIDIA-Linux-x86-100.14.23-pkg1.run
sudo /etc/init.d/gdm start
用application -> system tools里的nvidia工具去配置双显示器
c, multi-language
System -> Administration -> Language support: install English and Chinese
check "input method"
d, Wen Quan Yi font
browse http://wenq.org/, and download 文泉驿点阵宋体 and 文泉驿矢量正黑, then install them
System -> Preference -> Appearance -> Fonts 前四项选择:点阵宋体(WenQuanYi Bitmap Song), 第五项不改(Monospace)
sudo fc-cache -f -v (刷新字体缓存,每次修改字体都要这样,不然Xorg会很慢)
e, stardict
sudo apt-get install stardict
(http://stardict.sourceforge.net/Dictionaries_zh_CN.php 下载朗道英汉,汉英字典)
tar -xjvf *** --directory /usr/share/stardict/dic/
f, pidgin internet messager
sudo apt-get install gaim-guifications
config: Tools -> Plugins -> (check) Guifications; then, config it to uncheck on "Chat message"
3, install and config Software
sudo apt-get install postgresql-8.1 python2.4 ipython vim-gnome sun-java5-jdk eclipse subversion build-essential ssh build-essential meld kompare
a, postgresql
sudo su - postgres (for user postgres has Null password, so you can't just "su - postgres", or you can sudo "sudo passwd postgres" to set password for postgres, then "su - postgres")
createuser (enter username and password.)
config postgresql as below:
In /etc/postgresql/8.1/main/postgresql.conf, Change listen_addresses to '*' and change datestyle to 'ISO,European' and uncomment them.
In /etc/postgresql/8.1/main/pg_hba.conf, 最后加入一行“host all justin 127.0.0.1/16 trust”
b, eclipse
sudo eclipse, exit, eclipse
c, ssh
When other mathines want to ssh or scp your mathine which is new OS, it should "rm ~/.ssh/known_hosts" to reload the new Cert.
d, kompare
add a file svndiff in src with context
"""
if [ $1 ] ; then
svn up -r $1
svn st -q
svn log -r $1
PRE=`expr $1 - 1`
svn diff --diff-cmd=diff -x "-U 10000" -r$PRE:$1 > /tmp/$1.diff
cat /tmp/$1.diff | kompare -
else
svn up
svn st
svn diff --diff-cmd=diff -x "-U 10000" | kompare -
fi
"""
then, in src, ./svndiff 9827 will show diff about r9827
e, firefox add-ons
firebug, flashblock
3, chroot
a,
sudo apt-get install debootstrap
sudo debootstrap --arch i386 etch /home/etch http://debian.exoweb.net/debian.cn99.com/debian/
(if in 64 bit system, use --arch amd64)
sudo chroot /home/etch
#in etch as root
apt-get install locales
dpkg-reconfigure locales #(choose en_us UTF8 as before)
apt-get install vim vim-gnome xbase-clients less sudo postgresql-client subversion
echo "etch" > /etc/debian-chroot
visudo (add user justin to sudo)
adduser justin (删除的命令是userdel justin)
在ubuntu的/usr/bin/etch加入:
sudo cp /etc/passwd /home/etch/etc/
sudo cp /etc/shadow /home/etch/etc/
sudo cp /etc/group /home/etch/etc/
sudo cp /etc/sudoers /home/etch/etc/
sudo cp /etc/resolv.conf /home/etch/etc/
sudo cp /etc/hosts /home/etch/etc/
在/etc/fstab加入:
/home /home/etch/home none bind 0 0
/tmp /home/etch/tmp none bind 0 0
/dev /home/etch/dev none bind 0 0
/proc /home/etch/proc none bind 0 0
sudo chroot /home/etch/ su - justin
现在就可一享受chroot的双系统了
b, run X in etch 3 steps
b1, (etch)mkdir /tmp/.X11-unix
(ubuntu)sudo echo "/tmp/.X11-unix/x0 /home/justin/etch/tmp/.X11-unix/x0 none bind 0 0" >> /etc/fstab
# another way is write it in to /etc/fstab, or sudo mount --bind /tmp/*** /home/justin/etch/tmp/***
b2, (etch)vim ~/.bashrc # add "export DISPLAY=:0.0"
b3, (ubuntu) cp ~/.Xauthority ~/etch/home/justin/ (其实这步不需要,因为上面已经把/home mount到了/home/etch/home了)
c, install java
#download jdk-1_5_0_14-linux-i586.bin to /opt/, and into etch/opt/
sudo chmod +x jdk-1_5_0_14-linux-i586.bin
sudo ./jdk-1_5_0_14-linux-i586.bin
vim ~/.bashrc
#add below in the end of .bashrc
#export JAVA_HOME=/opt/jdk1.5.0_14
#export CLASSPATH=.:$JAVA_HOME/lib/tools.jar:$JAVA_HOME/lib/dt.jar
#export PATH=$JAVA_HOME/bin:$PATH
java -version
#java version "1.5.0_14"
#Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_14-b03)
#Java HotSpot(TM) Client VM (build 1.5.0_14-b03, mixed mode, sharing)
配置默认Java使用哪个 sudo update-alternatives --config java
posted @
2007-12-19 17:29 保尔任 阅读(1746) |
评论 (0) |
编辑 收藏
1, svn property设置:
设置svn忽略某些文件:
svn propedit|pe svn:ignore ~/work/src
在打开的编辑器里面输入要忽略的文件格式(一种格式一行):
*.pyc
*.swo
设置svn使某文件可执行:
svn propset|ps svn:executable ON ~/work/test.py
2,svn merge
svn merge https://nordicbet.dev.exoweb.net/svn/trunk/src@12159 https://nordicbet.dev.exoweb.net/svn/branches/etch@12163
把从src@12159到etch@12163的改变都输出到当前工作目录。(src和etch的顺序不能颠倒,都则是相反的改变)
-------------------修订版本关键字解释:转自http://kstack.blogbus.com/logs/3080410.html--------------------
1. 概念
-------------
HEAD - The lastest revision in the repository.
BASE - The “pristine” revision of an item in a working copy.
COMMITTED - The last revision in which an item changed before (or at) BASE.
PREV - The revision just before the last revision in which an item changed. (Technically, COMMITTED - 1.)
HEAD - 目前repository上最新的revision,也许个人在操作wc的时候,有其他的成员commit成果,那么HEAD会变化的。
BASE - 对于一个item,BASE指我们check out的那个revision。
COMMITTED - 其必然 <= BASE,对于一个item,COMMITTED是最后修改时的那个revision。
PREV - (COMMITTED - 1)
上面的解释看不明白就是正常,请继续阅读……
2. 从item & revision引发的讨论
-------------
item,
就是SVN中被管理的东东(包括folder、file)。而每次commit都会产生一个snapshot,并记录一个revision。对于
revision,it's not valid to talk about “revision 5 of foo.c”. Instead,
one would say “foo.c as it appears in revision 5”.
例如:
有文件 foo.c、bar.c
13 17 24 25 26
-----|----...----|-------...------|-----------|------------|--------- trunk
M foo.c M foo.c M bar.c M bar.c others ...
我们在r24之前,都没有对bar.c进行改动,因此,bar.c在 1 ~ 23 里面的内容是一样的。而在r24, r25,foo.c 的内容与其在 r23 的时候没有区别。
这时,我们check out [trunk@24]。并在trunk目录下
svn st -v
则可以看到
---------------------------------------------
BASE COMMITTED // 这一行SVN不会显示
24 24 .
24 17 foo.c
24 24 bar.c
...
其中foo.c与bar.c的COMMITTED是不一样的。
这时,我们进行r25的行为,修改了bar.c,并commit了。并
svn st -v
可以看到
---------------------------------------------
BASE COMMITTED // 这一行SVN不会显示
24 24 .
24 17 foo.c
25 25 bar.c
...
这说明了此时bar.c的变化情况。为何foo.c的BASE还是24呢?因为此时的foo.c是r24那时的foo.c。ok,我们
svn up
然后
svn st -v
---------------------------------------------
BASE COMMITTED // 这一行SVN不会显示
25 25 .
25 17 foo.c
25 25 bar.c
...
Yeah~,一切ok。
说完了最麻烦的BASE和COMMITTED。
PREV没什么好说的。而HEAD,如果r26为最后一个提交到repository的改动,那么r26就是HEAD罗~
3. 总结
-------------
感谢阅读到这里,打字很辛苦。来,给点掌声。:o)
这里的理解不一定正确,欢迎讨论。
posted @
2007-12-17 18:23 保尔任 阅读(445) |
评论 (0) |
编辑 收藏
一,两个数的最大公约数:
1、欧几里德算法
欧几里德算法又称辗转相除法,用于计算两个整数a,b的最大公约数。其计算原理依赖于下面的定理:
定理:gcd(a,b) = gcd(b,a mod b)
证明:a可以表示成a = kb + r,则r = a mod b
假设d是a,b的一个公约数,则有
d|a, d|b,而r = a - kb,因此d|r
因此d是(b,a mod b)的公约数
假设d 是(b,a mod b)的公约数,则
d | b , d |r ,但是a = kb +r
因此d也是(a,b)的公约数
因此(a,b)和(b,a mod b)的公约数是一样的,其最大公约数也必然相等,得证
欧几里德算法就是根据这个原理来做的,其算法用C++语言描述为:
void swap(int & a, int & b){
int c = a;
a = b;
b = c;
}
int gcd(int a,int b){
if(0 == a ){
return b;
}
if( 0 == b){
return a;
}
if(a > b){
swap(a,b);
}
int c;
for(c = a % b ; c > 0 ; c = a % b){
a = b;
b = c;
}
return b;
}
2、Stein算法
欧几里德算法是计算两个数最大公约数的传统算法,它无论从理论还是从效率上都是很好的。但是有一个致命的缺陷,这个缺陷只有在大素数时才会显现出来。
考虑现在的硬件平台,一般整数最多也就是64位,对于这样的整数,计算两个数之间的模是很简单的。对于字长为32位的平台,计算两个不超过32位的整数的
模,只需要一个指令周期,而计算64位以下的整数模,也不过几个周期而已。但是对于更大的素数,这样的计算过程就不得不由用户来设计,为了计算两个超过
64位的整数的模,用户也许不得不采用类似于多位数除法手算过程中的试商法,这个过程不但复杂,而且消耗了很多CPU时间。对于现代密码算法,要求计算
128位以上的素数的情况比比皆是,设计这样的程序迫切希望能够抛弃除法和取模。
Stein算法由J. Stein 1961年提出,这个方法也是计算两个数的最大公约数。和欧几里德算法 算法不同的是,Stein算法只有整数的移位和加减法,这对于程序设计者是一个福音。
为了说明Stein算法的正确性,首先必须注意到以下结论:
gcd(a,a) = a,也就是一个数和它自身的公约数是其自身
gcd(ka,kb) = k gcd(a,b),也就是最大公约数运算和倍乘运算可以交换,特殊的,当k=2时,说明两个偶数的最大公约数必然能被2整除
C++/java 实现
// c++/java stein 算法
int gcd(int a,int b){
if(a<b){//arrange so that a>b
int temp = a;
a = b;
b=temp;
}
if(0==b)//the base case
return a;
if(a%2==0 && b%2 ==0)//a and b are even
return 2*gcd(a/2,b/2);
if ( a%2 == 0)// only a is even
return gcd(a/2,b);
if ( b%2==0 )// only b is even
return gcd(a,b/2);
return gcd((a+b)/2,(a-b)/2);// a and b are odd
}
二,多个数的最大公约数:(python实现:取出数组a中最小的,从2到最小的循环,找出其中最大的能被数组中所有数整除的那个数,就是最大公约数)
def gcd(a):
a.sort()
min = a[0]
result = 1
for i in range(2, min+1):
flag = True
for j in a:
if j % i != 0:
flag = False
if flag == True:
result = i
return result
posted @
2007-12-15 15:40 保尔任 阅读(590) |
评论 (2) |
编辑 收藏
网络安全协议之比较(SSH、 PKI、SET、SSL)
一、SSH介绍
什么是SSH?
传统的网络服务程序,如:ftp、pop和telnet在本质上都是不安全的,因为它们在网络上用明文传送口令和数据,
别有用心的人非常容易就可以截获这些口令和数据。而且,这些服务程序的安全验证方式也是有其弱点的,
就是很容易受到“中间人”(man-in-the-middle)这种方式的攻击。所谓“中间人”的攻击方式,
就是“中间人”冒充真正的服务器接收你的传给服务器的数据,然后再冒充你把数据传给真正的服务器。
服务器和你之间的数据传送被“中间人”一转手做了手脚之后,就会出现很严重的问题。
SSH的英文全称是Secure
SHell。通过使用SSH,你可以把所有传输的数据进行加密,这样“中间人”这种攻击方式就不可能实现了,
而且也能够防止DNS和IP欺骗。还有一个额外的好处就是传输的数据是经过压缩的,所以可以加快传输的速度。
SSH有很多功能,它既可以代替telnet,又可以为ftp、pop、甚至ppp提供一个安全的“通道”。
最初SSH是由芬兰的一家公司开发的。但是因为受版权和加密算法的限制,现在很多人都转而使用OpenSSH。 OpenSSH是SSH的替代软件,而且是免费的,可以预计将来会有越 来越多的人使用它而不是SSH。
SSH是由客户端和服务端的软件组成的,有两个不兼容的版本分别是:1.x和2.x。 用SSH 2.x的客户程序是不能连接到SSH 1.x的服务程序上去的。OpenSSH 2.x同时支持SSH 1.x和2.x。
SSH的安全验证是如何工作的
从客户端来看,SSH提供两种级别的安全验证。
第一种级别(基于口令的安全验证)只要你知道自己帐号和口令,就可以登录到远程主机。所有传输的数据都会被加密, 但是不能保证你正在连接的服务器就是你想连接的服务器。可能会有别的服务器在冒充真正的服务器, 也就是受到“中间人”这种方式的攻击。
第二种级别(基于密匙的安全验证)需要依靠密匙,也就是你必须为自己创建一对密匙,并把公用密匙放在需要访问的服务器上。
如果你要连接到SSH服务器上,客户端软件就会向服务器发出请求,请求用你的密匙进行安全验证。服务器收到请求之后,
先在你在该服务器的家目录下寻找你的公用密匙,然后把它和你发送过来的公用密匙进行比较。如果两个密匙一致,
服务器就用公用密匙加密“质询”(challenge)并把它发送给客户端软件。
客户端软件收到“质询”之后就可以用你的私人密匙解密再把它发送给服务器。用这种方式,你必须知道自己密匙的口令。但是,与第一种级别相比,第二种级别不
需要在网络上传送口令。
第二种级别不仅加密所有传送的数据,而且“中间人”这种攻击方式也是不可能的(因为他没有你的私人密匙)。 但是整个登录的过程可能需要10秒。
二、SSL介绍(Secure socket Layer & Security Socket Layer)
一个应用程序的安全需求在很大程度上依赖于将如何使用该应用程序和该应用程序将要保护什么。不过,用现有技术实现强大的、 一般用途的安全通常是可能的。认证就是一个很好的示例。
当顾客想从 Web 站点购买某个产品时,顾客和 Web 站点都要进行认证。顾客通常是以提供名字和密码的方式来认证他自己。 另一方面,Web
站点通过交换一块签名数据和一个有效的 X.509 证书(作为 SSL 握手的一部分)来认证它自己。
顾客的浏览器验证该证书并用所附的公用密钥验证签名数据。一旦双方都认证了,则交易就可以开始了。
SSL 能用相同的机制处理服务器认证(就如在上面的示例中)和客户机认证。 Web 站点典型地对客户机认证不依赖 SSL —
要求用户提供密码是较容易的。而 SSL 客户机和服务器认证对于透明认证是完美的, 对等机 — 如 p2p
应用程序中的对等机之间一定会发生透明认证。
安全套接字层(Secure Sockets Layer(SSL)) ,SSL
是一种安全协议,它为网络(例如因特网)的通信提供私密性。SSL 使应用程序在通信时不用担心被窃听和篡改。 SSL
实际上是共同工作的两个协议:“SSL 记录协议”(SSL Record Protocol)和“SSL 握手协议” (SSL Handshake
Protocol)。“SSL 记录协议”是两个协议中较低级别的协议,它为较高级别的协议, 例如 SSL
握手协议对数据的变长的记录进行加密和解密。SSL 握手协议处理应用程序凭证的交换和验证。
当一个应用程序(客户机)想和另一个应用程序(服务器)通信时,客户机打开一个与服务器相连接的套接字连接。然后,
客户机和服务器对安全连接进行协商。作为协商的一部分,服务器向客户机作自我认证。客户机可以选择向服务器作或不作自我认证。
一旦完成了认证并且建立了安全连接,则两个应用程序就可以安全地进行通信。按照惯例,我将把发起该通信的对等机看作客户机,
另一个对等机则看作服务器,不管连接之后它们充当什么角色。
名为 A 和 B 的两台对等机想安全地进行通信。在我们简单的 p2p 应用程序的环境中,对等机 A 想查询对等机 B 上的一个资源。
每个对等机都有包含其专用密钥的一个数据库(名为 keystore)和包含其公用密钥的证书。密码保护数据库的内容。
该数据库还包含一个或多个来自被信任的对等机的自签名证书。 对等机 A
发起这项事务,每台对等机相互认证,两台对等机协商采用的密码及其长度并建立一个安全通道。完成这些操作之后,
每个对等机都知道它正在跟谁交谈并且知道通道是安全的。 SSL (Secure socket
Layer)安全套接层协议主要是使用公开密钥体制和X.509数字证书技术保护信息传输的机密性和完整性,
它不能保证信息的不可抵赖性,主要适用于点对点之间的信息传输,常用Web Server方式。
安全套接层协议(SSL,Security Socket
Layer)是网景(Netscape)公司提出的基于WEB应用的安全协议,它包括:服务器认证、
客户认证(可选)、SSL链路上的数据完整性和SSL链路上的数据保密性。对于电子商务应用来说,使用SSL可保证信息的真实性、
完整性和保密性。但由于SSL不对应用层的消息进行数字签名,因此不能提供交易的不可否认性,这是SSL在电子商务中使用的最大不足。
有鉴于此,网景公司在从Communicator 4.04版开始的所有浏览器中引入了一种被称作“表单签名(Form Signing)”的功能,
在电子商务中,可利用这一功能来对包含购买者的订购信息和付款指令的表单进行数字签名,从而保证交易信息的不可否认性。综上所述,
在电子商务中采用单一的SSL协议来保证交易的安全是不够的,但采用"SSL+表单签名"模式能够为电子商务提供较好的安全性保证。
三、PKI介绍
为解决Internet的安全问题,世界各国对其进行了多年的研究,初步形成了一套完整的Internet安全解决方案,
即目前被广泛采用的PKI体系结构,PKI体系结构采用证书管理公钥,通过第三方的可信机构CA,
把用户的公钥和用户的其他标识信息(如名称、e-mail、身份证号等)捆绑在一起,在Internet网上验证用户的身份,
PKI体系结构把公钥密码和对称密码结合起来,在Internet网上实现密钥的自动管理,保证网上数据的机密性、完整性。
从广义上讲,所有提供公钥加密和数字签名服务的系统,都可叫做PKI系统,PKI的主要目的是通过自动管理密钥和证书,
可以为用户建立起一个安全的网络运行环境,使用户可以在多种应用环境下方便的使用加密和数字签名技术,
从而保证网上数据的机密性、完整性、有效性,数据的机密性是指数据在传输过程中,不能被非授权者偷看,
数据的完整性是指数据在传输过程中不能被非法篡改,数据的有效性是指数据不能被否认。一个有效的PKI系统必须是安全的和透明的,
用户在获得加密和数字签名服务时,不需要详细地了解PKI是怎样管理证书和密钥的,一个典型、完整、 有效的PKI应用系统至少应具有以下部分:
公钥密码证书管理。
黑名单的发布和管理。
密钥的备份和恢复。
自动更新密钥。
自动管理历史密钥。
支持交*认证。
由于PKI体系结构是目前比较成熟、完善的Internet网络安全解决方案,
国外的一些大的网络安全公司纷纷推出一系列的基于PKI的网络安全产品,如美国的Verisign, IBM ,
Entrust等安全产品供应商为用户提供了一系列的客户端和服务器端的安全产品,为电子商务的发展提供了安全保证。
为电子商务、政府办公网、EDI等提供了完整的网络安全解决方案。
PKI是一种新的安全技术,它由公开密钥密码技术、数字证书、
证书发放机构(CA)和关于公开密钥的安全策略等基本成分共同组成的。PKI是利用公钥技术实现电子商务安全的一种体系,
是一种基础设施,网络通讯、网上交易是利用它来保证安全的。从某种意义上讲,PKI包含了安全认证系统,
即安全认证系统-CA/RA系统是PKI不可缺的组成部分。
PKI(Public Key Infrastructure)公钥基础设施是提供公钥加密和数字签名服务的系统或平台,
目的是为了管理密钥和证书。一个机构通过采用PKI框架管理密钥和证书可以建立一个安全的网络环境。 X.509格式的证书和证书废除列表(CRL);
CA/RA操作协议; CA管理协议; CA政策制定。
四、SET协议介绍
电子商务在提供机遇和便利的同时,也面临着一个最大的挑战,即交易的安全问题。在网上购物的环境中,
持卡人希望在交易中保密自己的帐户信息,使之不被人盗用;商家则希望客户的定单不可抵赖,并且,在交易过程中,
交易各方都希望验明其他方的身份,以防止被欺骗。针对这种情况, 由美国Visa和MasterCard两大信用卡组织联合国际上多家科技机构,
共同制定了应用于Internet上的以银行卡为基础进行在线交易的安全标准, “这就是“安全电子交易”(Secure Electronic
Transaction,简称SET)。它采用公钥密码体制和X.509数字证书标准, 主要应用于保障网上购物信息的安全性。
由于SET 提供了消费者、商家和银行之间的认证,确保了交易数据的安全性、完整可靠性和交易的不可否认性, 特别是保证不将消费者银行卡号暴露给商家等优点,因此它成为了目前公认的信用卡/借记卡的网上交易的国际安全标准。
SET(Secure Electronic
Transaction)安全电子交易协议是由美国Visa和MasterCard两大信用卡组织提出的应用于
Internet上的以信用卡为基础的电子支付系统协议。它采用公钥密码体制和X.509数字证书标准, 主要应用于B to
C模式中保障支付信息的安全性。SET协议本身比较复杂,设计比较严格,安全性高,
它能保证信息传输的机密性、真实性、完整性和不可否认性。SET协议是PKI框架下的一个典型实现,同时也在不断升级和完善, 如SET
2.0将支持借记卡电子交易。
posted @
2007-11-26 17:04 保尔任 阅读(391) |
评论 (0) |
编辑 收藏
RSA算法是一种非对称密码算法,所谓非对称,就是指该算法需要一对密钥,使用其中一个加密,则需要用另一个才能解密。
RSA的算法涉及三个参数,n、e1、e2。
其中,n是两个大质数p、q的积,n的二进制表示时所占用的位数,就是所谓的密钥长度。
e1和e2是一对相关的值,e1可以任意取,但要求e1与(p-1)*(q-1)互质;再选择e2,要求(e2*e1)mod((p-1)*(q-1))=1。
(n及e1),(n及e2)就是密钥对。
RSA加解密的算法完全相同,设A为明文,B为密文,则:A=B^e1 mod n;B=A^e2 mod n;
e1和e2可以互换使用,即:
A=B^e2 mod n;B=A^e1 mod n;
补充回答:
对明文进行加密,有两种情况需要这样作:
1、您向朋友传送加密数据,您希望只有您的朋友可以解密,这样的话,您需要首先获取您朋友的密钥对中公开的那一个密钥,e及n。然后用这个密钥进行加密,这样密文只有您的朋友可以解密,因为对应的私钥只有您朋友拥有。
2、您向朋友传送一段数据附加您的数字签名,您需要对您的数据进行MD5之类的运算以取得数据的"指纹",再对"指纹"进行加密,加密将使用您自己的密钥对中的不公开的私钥。您的朋友收到数据后,用同样的运算获得数据指纹,再用您的公钥对加密指纹进行解密,比较解密结果与他自己计算出来的指纹是否一致,即可确定数据是否的确是您发送的、以及在传输过程中是否被篡改。
密钥的获得,通常由某个机构颁发(如CA中心),当然也可以由您自己创建密钥,但这样作,您的密钥并不具有权威性。
计算方面,按公式计算就行了,如果您的加密强度为1024位,则结果会在有效数据前面补0以补齐不足的位数。补入的0并不影响解密运算。
———————————在python中用M2Crypto包实现————————————
这个程序是验证M2Crypto替换POW的效率(时间和内存),结论如下:
Lib Time(500x100 times sign&verify) Mem python used (before threads start) Mem python used (after thread run, threads closed but python doesn't exit)
POW 41s 3.3M - 3.4M 3.9M - 4.0M
M2Crypto0.18 44s 3.3M - 3.4M 3.9M - 4.0M
No RSA, just a+b <1s 3.3M - 3.4M 3.9M - 4.0M
from M2Crypto import BIO, RSA, EVP
import config
import POW
import datetime, threading, time
private_key = """-----BEGIN RSA PRIVATE KEY-----
MIIBPAIBAAJBAMC3aHYeDQNksuWKNd7amAZWvawuyUFOsbEUN4bTWWPC0noozS1o
d3Bal8q8W7SXx1oDihbm90t90HpwV++RFnECAwEAAQJBAKvyeQf6pA3FCUF44bvn
OgFd33oDfJoChtTCfxCS/ozcuFG9mFeKjqxbqTDrBtrX3Stx644iZV/AFZSQoWFP
smkCIQDqeTC6R6cv4VGyvdzbfEHaY0r8hIdpFOQwJt/462giXwIhANJozqGUq5TG
LcJz4wNVWZgn6FJPjCbxrM4Oaqr+GJkvAiAJOES7PnALiO+eeKrDkqpAPSFItqlg
b2rdndm2vwL0PwIhALHsAl7MEtM5SdSWni5ha+OoS2He9kqwLkoIEtcJCs/tAiEA
qIPk7tyVeR8JlTSUiFTus1JjYDmKObJ+ZyfeJucO3No=
-----END RSA PRIVATE KEY-----"""
public_key = """-----BEGIN PUBLIC KEY-----
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAMC3aHYeDQNksuWKNd7amAZWvawuyUFO
sbEUN4bTWWPC0noozS1od3Bal8q8W7SXx1oDihbm90t90HpwV++RFnECAwEAAQ==
-----END PUBLIC KEY-----"""
data = "12dfuilfalfjadoighaiwlhtfalihsalkahjflkajgaklaklhalfhawklfhawkfasdaksjlhawifhyawiofhawifhquioghaeughautguoqhfaiofghaiourthq3iothfioahaiwofhawiorhawiofahfioadfhadwiofuawiofaiofhaiofhasdiofhasifhifhifhasdkljfhsadklfasdhashfilsahffhasdkfasdfj;asdfjdas;klfjnsdklnsadklfnsdklfhadl;fsj;lasjf;lkdfjdsa;klfjdlkfjdkfjds;lafhaofhawifwaqfniowfnwafldsaknfdaslkfndaklfjiojioajfaiopfnaklfnaklnfklanfalkfnklanklfdnlakfnklafnaklfnaklfnsklafnkldafinaigvn0irnifgjhaoihaiofjaoifjoiaje[qtobeornottobethat'saquestionjapfjpiofjapfiajipawjaiopjaproiajlafjasdklfjasdklfjdalfjlaa;fjad;lff'alfjkal;fja'luiopdfxghfxdgfdyuiop[afhdmfn9zfinkbdfrmh;io/ljrpjilnj[RQ"
threads = 500
times = 100
def verify():
rsa = RSA.load_key_string(private_key)
digest = EVP.MessageDigest('sha1')
digest.update(data)
signature = rsa.sign(digest.digest())
rsa_pub = RSA.load_pub_key_bio(BIO.MemoryBuffer(public_key))
digest2 = EVP.MessageDigest('sha1')
digest2.update(data)
if not rsa_pub.verify(digest2.digest(), signature):
raise Exception("Failed!")
def verifyByPOW():
digest = POW.Digest(POW.SHA1_DIGEST)
digest.update(data)
private = POW.pemRead(POW.RSA_PRIVATE_KEY, private_key, 'pass')
signature = private.sign(digest.digest(), POW.SHA1_DIGEST)
public = POW.pemRead(POW.RSA_PUBLIC_KEY, public_key, 'pass')
digest2 = POW.Digest(POW.SHA1_DIGEST)
digest2.update(data)
if not public.verify(signature, digest2.digest(), POW.SHA1_DIGEST):
raise Exception("Failed!")
class ThreadTest(threading.Thread):
def __init__(self, index, times):
threading.Thread.__init__(self)
self.times = times
self.id = index
def run(self):
a = 1478904489
for i in xrange(times):
a += a
verify()
#verifyByPOW()
print "Thread test %s Finished at %s" % (self.id, str(datetime.datetime.now()))
def main():
print ""n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
start = datetime.datetime.now()
for i in xrange(threads):
t = ThreadTest(i, times)
t.start()
time.sleep(300)
end = datetime.datetime.now()
# see the used time between two package
print "total time is %s" % (end - start)
# use linux tools, such as command "top" to see the memory useage before and after the method to see whether M2Crypto
# has momery leak(assume POW has no memory leak problem)
if __name__ == '__main__':
main()
posted @
2007-11-21 11:52 保尔任 阅读(404) |
评论 (0) |
编辑 收藏
我打赌当你见到Base64这个词的时候你会觉得在哪里见过,因为在你能够上网看到这篇文章的时候你已经在后台使用它了。如果您对二进制数有所了解,你就可以开始读它了。
打开一封Email,查看其原始信息(您可以通过收取、导出该邮件用文本编辑器查看)。你会看到类似这样的一个效果:
Date: Thu, 25 Dec 2003 06:33:07 +0800
From: "eSX?!" <
snaix@yeah.net'>
snaix@yeah.net'>
snaix@yeah.net'>
snaix@yeah.net>
Reply-To:
snaix@yeah.net'>
snaix@yeah.net'>
snaix@yeah.net'>
snaix@yeah.net
To: "snaix" <
snaix@126.com'>
snaix@126.com>
Subject:
X-mailer: Foxmail 5.0 beta2 [cn]
Mime-Version: 1.0
Content-Type: text/plain;
charset="gb2312"
Content-Transfer-Encoding: base64
xOO6w6OsU25haVgNCg0KoaGhodXiysfSu7j2QmFzZTY0tcSy4srU08q8/qOhDQoNCkJlc3QgV2lz
aGVzIQ0KIAkJCQkNCqGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaEgICAgICAgICAgICAgICBl
U1g/IQ0KoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoSAgICAgICAgICAgICAgIHNuYWl4QHll
YWgubmV0DQqhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhICAgICAgICAgMjAwMy0x
Mi0yNQ0K
是否看到了“base64”标记?是否看到了标记下面的一行乱码?也许你会恍然大悟,对!这就是Base64编码。
什么是Base64?
按
照RFC2045的定义,Base64被定义为:Base64内容传送编码被设计用来把任意序列的8位字节描述为一种不易被人直接识别的形式。(The
Base64 Content-Transfer-Encoding is designed to represent arbitrary
sequences of octets in a form that need not be humanly readable.)
为什么要使用Base64?
在设计这个编码的时候,我想设计人员最主要考虑了3个问题:
1.是否加密?
2.加密算法复杂程度和效率
3.如何处理传输?
加密是肯定的,但是加密的目的不是让用户发送非常安全的Email。这种加密方式主要就是“防君子不防小人”。即达到一眼望去完全看不出内容即可。
基
于这个目的加密算法的复杂程度和效率也就不能太大和太低。和上一个理由类似,MIME协议等用于发送Email的协议解决的是如何收发Email,而并不
是如何安全的收发Email。因此算法的复杂程度要小,效率要高,否则因为发送Email而大量占用资源,路就有点走歪了。
但是,如果是基于以上两点,那么我们使用最简单的恺撒法即可,为什么Base64看起来要比恺撒法复杂呢?这是因为在Email的传送过程中,由于历史原
因,Email只被允许传送ASCII字符,即一个8位字节的低7位。因此,如果您发送了一封带有非ASCII字符(即字节的最高位是1)的Email通
过有“历史问题”的网关时就可能会出现问题。网关可能会把最高位置为0!很明显,问题就这样产生了!因此,为了能够正常的传送Email,这个问题就必须
考虑!所以,单单靠改变字母的位置的恺撒之类的方案也就不行了。关于这一点可以参考RFC2046。
基于以上的一些主要原因产生了Base64编码。
算法详解
Base64编码要求把3个8位字节(3*8=24)转化为4个6位的字节(4*6=24),之后在6位的前面补两个0,形成8位一个字节的形式。
具体转化形式间下图:
字符串“张3”
11010101 11000101 00110011
00110101 00011100 00010100 00110011
表1
可以这么考虑:把8位的字节连成一串110101011100010100110011
然后每次顺序选6个出来之后再把这6二进制数前面再添加两个0,就成了一个新的字节。之后再选出6个来,再添加0,依此类推,直到24个二进制数全部被选完。
让我们来看看实际结果:
字符串“张3”
11010101 HEX:D5 11000101 HEX:C5 00110011 HEX:33
00110101 00011100 00010100 00110011
字符’5’ 字符’^"’ 字符’^T’ 字符’3’
十进制53 十进制34 十进制20 十进制51
表2
这样“张3 ”这个字符串就被Base64表示为”5^"^T3”了么?。错!
Base64编码方式并不是单纯利用转化完的内容进行编码。像’^"’字符是控制字符,并不能通过计算机显示出来,在某些场合就不能使用了。Base64有其自身的编码表:
Table 1: The Base64 Alphabet
Value Encoding Value Encoding Value Encoding Value Encoding
0 A 17 R 34 i 51 z
1 B 18 S 35 j 52 0
2 C 19 T 36 k 53 1
3 D 20 U 37 l 54 2
4 E 21 V 38 m 55 3
5 F 22 W 39 n 56 4
6 G 23 X 40 o 57 5
7 H 24 Y 41 p 58 6
8 I 25 Z 42 q 59 7
9 J 26 a 43 r 60 8
10 K 27 b 44 s 61 9
11 L 28 c 45 t 62 +
12 M 29 d 46 u 63 /
13 N 30 e 47 v (pad) =
14 O 31 f 48 w
15 P 32 g 49 x
16 Q 33 h 50 y
表3
这
也是Base64名称的由来,而Base64编码的结果不是根据算法把编码变为高两位是0而低6为代表数据,而是变为了上表的形式,如”A”就有7位,
而”a”就只有6位。表中,编码的编号对应的是得出的新字节的十进制值。因此,从表2可以得到对应的Base64编码:
字符串“张3”
11010101 HEX:D5 11000101 HEX:C5 00110011 HEX:33
00110101 00011100 00010100 00110011
字符’5’ 字符’^"’ 字符’^T’ 字符’3’
十进制53 十进制34 十进制20 十进制51
字符’1’ 字符’i’ 字符’U’ 字符’z’
表4
这样,字符串“张3”经过编码后就成了字符串“1iUz”了。
Base64将3个字节转变为4个字节,因此,编码后的代码量(以字节为单位,下同)约比编码前的代码量多了1/3。之所以说是“约”,是因为如果代码量正好是3的整数倍,那么自然是多了1/3。但如果不是呢?
细心的人可能已经注意到了,在The Base64 Alphabet中的最后一个有一个(pad) =字符。这个字符的目的就是用来处理这个问题的。
当代码量不是3的整数倍时,代码量/3的余数自然就是2或者1。转换的时候,结果不够6位的用0来补上相应的位置,之后再在6位的前面补两个0。转换完空出的结果就用就用“=”来补位。譬如结果若最后余下的为2个字节的“张”:
字符串“张”
11010101 HEX:D5 11000101 HEX:C5
00110101 00011100 00010100
十进制53 十进制34 十进制20 pad
字符’1’ 字符’i’ 字符’U’ 字符’=’
表6
这样,最后的2个字节被整理成了“1iU=”。
同理,若原代码只剩下一个字节,那么将会添加两个“=”。只有这两种情况,所以,Base64的编码最多会在编码结尾有两个“=”
至于将Base64的解码,只是一个简单的编码的逆过程,读者可以自己探讨。我将在文章的最后给出解码算法。
算法实现
其实在算法详解的时候基本上已经说的很清楚了。用于程序上,除去约束判断,大概可以分为如下几步几步:
读取数据3字节用AND取前6位,放入新的变量中右移两位,高两位清0AND取第一个字节的后2位和第二个字节的前4位移位放入新变量中右移两位,清0……依此类推。
解码的类C语言实现的算法:
BYTE LMoveBit(int base, int MoveNum)
{
BYTE result=base;
if(MoveNum==0)return 1;
if(MoveNum==1)return MoveNum;
result=base<<(MoveNum-1);
return result;
}
char base64_alphabet[]=
{'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/','='};
BYTE Base64Decode(char *base64code, DWORD base64length)
{
char buf[4];
int i,j;
int k;
int l=0;
BYTE temp1[4],temp2;
BYTE *Buffer=new BYTE[base64length*3/4];
DWORD base64a=(base64length/4)-1;
DWORD base64b=0;
for(;base64b<base64a+1;base64b++)
{
for(i=0;i<4;i++)
{
buf[i]=*(base64code+(base64b*4)+i);
for(j=0;j<65;j++)
{
if(buf[i]==base64_alphabet[j])
{
temp1[i]=j;
break;
}
}
}
i--;
for(k=1;k<4;k++)
{
if(temp1[i-(k-1)]==64){m_padnum++; continue;}
temp1[i-(k-1)]=temp1[i-(k-1)]/LMoveBit(2,(k-1)*2);
temp2=temp1[i-k];
temp2=temp2&(LMoveBit(2,k*2)-1);
temp2*=LMoveBit(2,8-(2*k));//move 4
temp1[i-(k-1)]=temp1[i-(k-1)]+temp2;
Buffer[base64b*3+(3-k)]=temp1[i-(k-1)];
}
}
return Buffer;
}
根据这段算法,文章最开始给出的Email内容,可以解码为:
你好,SnaiX
这是一个Base64的测试邮件!
Best Wishes!
eSX?!
snaix@yeah.net'>
snaix@yeah.net'>
snaix@yeah.net'>
snaix@yeah.net
2003-12-25
如文章有问题恳请指出并与我联系:
snaix@126.com'>
snaix@126.com
主要参考资料:
RFC2045
RFC2046
《奇妙的Base64编码》,罗聪
以及一些来自互联网上的其他资料
posted @
2007-11-19 14:30 保尔任 阅读(217) |
评论 (0) |
编辑 收藏