327 lines
157 KiB
XML
Executable File
327 lines
157 KiB
XML
Executable File
<?xml version="1.0" encoding="UTF-8"?><rss version="2.0">
|
||
<channel>
|
||
<title>风笑痴</title>
|
||
<link>http://lunny.info/</link>
|
||
<description>风笑痴,一个脱离了低级懒惰的人</description>
|
||
<managingEditor>xiaolunwen@gmail.com (lunny)</managingEditor>
|
||
<pubDate>Wed, 16 Aug 2017 18:09:39 +0800</pubDate>
|
||
<item>
|
||
<title>Gop - 编译和管理在GOPATH之外的Go工程</title>
|
||
<link>http://lunny.info/2017/8/15/Gop---编译和管理在GOPATH之外的Go工程.html</link>
|
||
<description><h1 id="安装">安装</h1>
<pre><code class="language-go">go get github.com/lunny/gop
</code></pre>

<h1 id="起子">起子</h1>

<p>自开始使用Go进行开发之后,工程一直都保存在GOPATH之下,Go1.5支持 <code>vendor</code> 机制之后开始使用 <code>govendor</code> 来管理依赖项。其实一直都有需求要将 Go 的工程放在 GOPATH 之外,因为在一个大的项目中,各种语言写的内容放在一个 git 工程的子文件夹中,但一直没有很好的工具来解决依赖的问题。</p>

<p>几个月之前,这个问题已经严重影响到工作了,终于不能忍受了。于是动手写了 <code>Gop</code> 的首个版本,最近又升级到了0.3版本。最新版本的 <code>Gop</code> 工程目录结构如下:</p>
<pre><code>&lt;project root&gt;
├── gop.yml
├── bin
├── doc
└── src
 ├── main
 │ └── main.go
 ├── models
 │ └── models.go
 ├── routes
 │ └── routes.go
 └── vendor
 └── github.com
 ├── go-xorm
 │ ├── builder
 │ ├── core
 │ └── xorm
 └── lunny
 ├── log
 └── tango
</code></pre>

<p>通过以上的目录结构可以看到,其实gop是兼容GOPATH的。只要把 <code>&lt;project root&gt;</code> 设置到GOPATH中,即使没有安装<code>gop</code>命令,通过<code>go</code>命令也可以编译,但是这时对 <code>vendor</code> 管理也是不太方便。但如果使用 <code>gop</code> 来管理项目,则问题迎刃而解。</p>

<p>其中 <code>gop.yml</code> 的结构如下:</p>
<pre><code class="language-yml">targets:
- name: myproject1
 dir: main
 assets:
 - templates
 - public
 - config.ini
 - key.pem
 - cert.pem
- name: myproject2
 dir: web
 assets:
 - templates
 - public
 - config.ini
</code></pre>

<p>除了可以放到 <code>GOPATH</code> 之外,Gop 工程还具有以下特性:</p>

<ul>
<li>多编译目标支持</li>
</ul>

<p>默认的编译目标是 <code>src/main</code> 目录,编译名字是 <code>src</code> 的父目录的名字,当然你也可以通过 <code>gop.yml</code> 来自定义。如果输入<code>gop build</code> 命令,默认会编译第一个编译目标。如果指定了编译目标,如 <code>gop build myproject2</code> 那么将会编译指定的目标。默认编译好的可执行文件会放在 <code>src/main/</code> 目录下,也可以通过 <code>gop build -o ./bin/my.exe</code> 来进行指定。除了 <code>&lt;target&gt;</code> 参数外,其它的参数和 <code>go build</code> 的参数相同。</p>

<ul>
<li>依赖管理</li>
</ul>

<p>一般只需要执行 <code>gop ensure -g</code> 即可将所有的依赖项全部下载且拷贝到 <code>vendor</code> 下。如果依赖项需要更新,也可以使用 <code>gop ensure -u</code> 将所有的依赖项更新到最新的版本。不再需要的依赖项,可以通过 <code>gop rm &lt;package1&gt; &lt;package2&gt;</code> 删除,通过 <code>gop status</code> 可以查看默认编译目标的依赖项的安装情况。</p>

<ul>
<li>编译并运行某个目标</li>
</ul>

<p><code>gop run</code> 可以让你编译并运行某个目标,通过 <code>-w</code>,在源码修改之后还可以自动重新编译并运行。</p>

<ul>
<li>发布管理</li>
</ul>

<p><code>gop release</code> 编译,打包资源并拷贝到 <code>bin</code> 目录下。</p>
</description>
|
||
<pubDate>Tue, 15 Aug 2017 22:04:00 +0000</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>Go语言Web框架Tango中的中间件应用级别</title>
|
||
<link>http://lunny.info/2016/5/12/Go语言Web框架Tango中的中间件应用级别.html</link>
|
||
<description><p>Tango在创建之初就支持了全局中间件的支持,经过最近的改进,同时支持了Group级别和Route级别的中间件。下面我们就一起来看下这三种级别的中间件:</p>

<p>比如我们自定义了一个新的中间件:</p>
<pre><code class="language-go">func MyMiddleware() tango.HandlerFunc {
 return func(ctx *tango.Context) {
 ctx.Info(&quot;I'm a middleware&quot;)
 ctx.Next()
 }
}
</code></pre>

<h1 id="全局中间件">全局中间件</h1>

<p>全局中间件在任何一个请求调用时均会进行调用。用法如下:</p>
<pre><code class="language-go">t := tango.Classic()
t.Use(MyMiddleware())
t.Get(&quot;/&quot;, func() string {return &quot;global&quot;})
</code></pre>

<h1 id="group中间件">Group中间件</h1>

<p>Group中间件在Group下匹配的路由被调用时会被调用。用法如下:</p>
<pre><code class="language-go">t := tango.Classic()
t.Group(&quot;/group&quot;, func(g *tango.Group) {
 g.Use(MyMiddleware())
 g.Get(&quot;/&quot;, func() string {return &quot;group&quot;})
})
</code></pre>

<h1 id="route中间件">Route中间件</h1>

<p>Route中间件在该Route被调用时会被调用。如果有多个,会按照先后顺序调用。用法如下:</p>
<pre><code class="language-go">t := tango.Classic()
t.Get(&quot;/route&quot;, func() string {return &quot;route&quot;}, MyMiddleware())
</code></pre>

<h1 id="中间件优先级">中间件优先级</h1>

<ul>
<li>全局中间件被先调用,Group中间件次之,最后是Route中间件</li>
<li>相同级别的中间件,先加入的被先调用</li>
</ul>
</description>
|
||
<pubDate>Thu, 12 May 2016 14:32:00 +0000</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>Go语言Web框架Tango在Session中间件中的设计</title>
|
||
<link>http://lunny.info/2016/2/18/Go语言Web框架Tango在Session中间件中的设计.html</link>
|
||
<description><p>Tango在创建之初的目标就是既有Beego的效率,又有martini的灵活中间件。因此Session也是在核心库 <a href="https://github.com/lunny/tango">tango</a> 之外作为官方中间件存在,地址是 <a href="https://github.com/tango-contrib/session">tango-session</a>。</p>

<p>Session因为有着灵活的设计,因此可以完成很多我们希望的功能。首先看下Session中间件的选项:</p>
<pre><code class="language-go">type Options struct {
 MaxAge time.Duration
 SessionIdName string
 Store Store
 Generator IdGenerator
 Tracker Tracker
 OnSessionNew func(*Session)
 OnSessionRelease func(*Session)
}
</code></pre>

<p>其中我将要着重讲的是Store,Generator和Tracker。</p>

<h2 id="store">Store</h2>

<p>Store是一个接口,主要作用是存储Session中的内容,其定义如下:</p>
<pre><code class="language-go">type Store interface {
 Add(id Id) bool
 Exist(id Id) bool
 Clear(id Id) bool

 Get(id Id, key string) interface{}
 Set(id Id, key string, value interface{}) error
 Del(id Id, key string) bool

 SetMaxAge(maxAge time.Duration)
 SetIdMaxAge(id Id, maxAge time.Duration)

 Run() error
}
</code></pre>

<p>默认的内核自带了<code>MemoryStore</code>,这将会把所有Session内容保存在内存中。同时官方中间件中也提供了</p>

<ul>
<li><a href="http://github.com/tango-contrib/session-nodb">nodb</a></li>
<li><a href="http://github.com/tango-contrib/session-redis">redis</a></li>
<li><a href="http://github.com/tango-contrib/session-ledis">ledis</a></li>
<li><a href="http://github.com/tango-contrib/session-ssdb">ssdb</a></li>
</ul>

<p>这几种方式进行Session内容的存储。当然如果你愿意,也可以自己来实现一个Store。</p>

<h2 id="generator">Generator</h2>

<p>Generator是一个接口,主要封装了SessionID的生成算法,其定义如下:</p>
<pre><code class="language-go">type IdGenerator interface {
 Gen(req *http.Request) Id
 IsValid(id Id) bool
}
</code></pre>

<p>默认的Generator是Sha1Generator,他是通过<code>req.RemoteAddr</code>,当前时间和随机字符串生成。
当然你也可以自定义更好的算法来生成SessionID。</p>

<h2 id="tracker">Tracker</h2>

<p>Tracker是一个接口,主要封装了Session的跟踪方式,其定义如下:</p>
<pre><code class="language-go">type Tracker interface {
 SetMaxAge(maxAge time.Duration)
 Get(req *http.Request) (Id, error)
 Set(req *http.Request, rw http.ResponseWriter, id Id)
 Clear(rw http.ResponseWriter)
}
</code></pre>

<p>默认的Tracker实现是<code>CookieTracker</code>,就是我们最常见的,将SessionID保存在cookie中,通过cookie来进行跟踪。Session中间件中也同时提供了<code>HeaderTracker</code>,支持将SessionID保存在自定义的Http Header中。当然你也可以自定义Tracker,比如通过URL参数来进行跟踪等等方式。</p>

<h2 id="最后">最后</h2>

<p>看起来似乎很复杂,但是一般情况下都不需要去改变,你只需要</p>
<pre><code class="language-go">t := tango.Classic()
t.Use(session.New())
t.Run()
</code></pre>
</description>
|
||
<pubDate>Thu, 18 Feb 2016 16:00:00 +0000</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>Go语言库net/smtp发送邮件故障一则</title>
|
||
<link>http://lunny.info/2016/1/7/Go语言库net/smtp发送邮件故障一则.html</link>
|
||
<description><p>在通过net/smtp包发送邮件时遇到一个奇怪的错误,在mac和windows上运行都能够成功发送邮件,但是将程序跨平台编译后上传到debian linux执行报:</p>
<pre><code>x509: failed to load system roots and no roots provided
</code></pre>

<p>的错误,看样子是在认证邮件服务器证书的根证书时没有找到本地的根证书库。最后通过运行</p>
<pre><code>#apt-get install ca-certificates
</code></pre>

<p>之后,邮件成功发送。</p>
</description>
|
||
<pubDate>Thu, 07 Jan 2016 16:00:00 +0000</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>Tango v0.4版本发布,带来统一高性能的新路由</title>
|
||
<link>http://lunny.info/2015/4/6/Tango-v0.4版本发布,带来统一高性能的新路由.html</link>
|
||
<description><h1 id="起子">起子</h1>

<p>自Tango发布之后大概有3个月了,受到了很多人的关注,也提了很多的改进意见。我自己也通过不断
的用他开发,从而发现问题,不断改进。昨天我发布了0.4.0版本,很明显,最近版本号升得比较快。</p>

<p>从0.3到0.4,最重大的改变是路由方面的。路由基本上完全重写,目前路由变得更统一,更灵活,性能上也有不少提升。来看看新的路由如何使用。</p>

<h1 id="新路由">新路由</h1>

<p>目前支持4种路由,静态路由,命名路由,Catch-All路由,正则路由。下面分别说说4种路由:</p>

<h2 id="静态路由">静态路由</h2>

<p>静态路由是最普遍的,默认的net/http目前只支持静态路由,比如:</p>
<pre><code class="language-go">tango.Get(&quot;/&quot;, new(Action))
tango.Get(&quot;/public/bootstrap.min.css&quot;, new(Action))
</code></pre>

<h2 id="命名路由">命名路由</h2>

<p>在0.4之前的版本中,命名路由的形式一般是<code>:name</code>,在0.4版本兼容以前的模式,并且新增了<code>(:name)</code>的形式。这个新的设计将允许使用字符或者数字作为分隔符。比如,新旧版本都支持这种形式:</p>
<pre><code class="language-go">tango.Get(&quot;/:name&quot;, new(Action))
tango.Get(&quot;/:name1-:name2&quot;, new(Action))
tango.Get(&quot;/:name1/:name2&quot;, new(Action))
</code></pre>

<p>而新版本增加了对下列形式的支持:</p>
<pre><code class="language-go">tango.Get(&quot;/(:name1)abc(:name2)&quot;, new(Action))
tango.Get(&quot;/(:name1)123(:name2)&quot;, new(Action))
</code></pre>

<h2 id="catch-all路由">Catch-All路由</h2>

<p>Catch-All路由是0.4新增的一种路由形式。Catch-All路由和命名路由很相似,他们的的唯一区别在于
命名路由不能匹配/,而Catch-All路由可以匹配/。比如:</p>
<pre><code class="language-go">tango.Get(&quot;/*name&quot;, new(Action))
tango.Get(&quot;/*name/123&quot;, new(Action))
</code></pre>

<p>比如当访问 <code>/name1/name2/123</code> 时,第二个路由会匹配出 <code>*name = name1/name2</code>。当访问<code>/name1/name2</code>时,则第一个路由会匹配出 <code>*name = name1/name2</code>。</p>

<p>获取Catch-All路由请通过 <code>Params.Get(&quot;*name&quot;)</code>。</p>

<h2 id="正则路由">正则路由</h2>

<p><strong>注意:0.4版本的正则路由和老版本的正则路由不兼容</strong></p>

<p>为了统一表达形式,0.4版本抛弃了以前版本中完全使用Go的正则的方式来进行匹配的方式,如:</p>

<p>0.3版本以前:</p>
<pre><code class="language-go">tango.Get(&quot;/(.*)&quot;, new(Action))
</code></pre>

<p>而获取参数通过 Params.Get(&rdquo;:0&rdquo;)来获取。</p>

<p>0.3版本到0.4版本之间:</p>
<pre><code class="language-go">tango.Get(&quot;/(?P&lt;name&gt;.*)&quot;, new(Action))
</code></pre>

<p>而获取参数通过 Params.Get(&rdquo;:name&rdquo;)来获取,同时也可以使用<code>Params.Get(&quot;:0&quot;)</code>来获取。</p>

<p>0.4版本不兼容前面的语法,再次提醒注意,而采用如下方法,匿名的正则已不被允许,<code>Params.Get(&quot;:0&quot;)</code>也不再受支持:</p>
<pre><code class="language-go">tango.Get(&quot;/(:name.*)&quot;, new(Action))
tango.Get(&quot;/(:name[0-9]+)&quot;, new(Action))
</code></pre>

<p>而获取参数通过 Params.Get(&rdquo;:name&rdquo;)来获取,也可以通过Params[0].Value来获取,Params目前变成了一个Slice。</p>

<h1 id="路由优先级">路由优先级</h1>

<ol>
<li>静态路由和其它路由都匹配时,静态路由优先,跟添加的顺序无关;</li>
<li>其它路由之间根据添加的顺序,先添加的优先。</li>
</ol>

<p>例如:</p>
<pre><code class="language-go">t := tango.Classic()
t.Get(&quot;/:name&quot;, new(Others))
t.Get(&quot;/admin&quot;, new(Admin))
t.Run()
</code></pre>

<p>以上代码,当请求 <code>/admin</code> 时, <code>Admin</code> 的 <code>Get</code> 方法将被执行。</p>
<pre><code class="language-go">t := tango.Classic()
t.Get(&quot;/:name&quot;, new(Admin))
t.Get(&quot;/*name&quot;, new(Others))
t.Run()
</code></pre>

<p>以上代码,当请求 <code>/admin</code> 时, <code>Admin</code> 的 <code>Get</code> 方法将被执行;当请求 <code>/admin/ui</code>, <code>Others</code> 的 <code>Get</code> 方法将被执行。</p>
<pre><code class="language-go">t := tango.Classic()
t.Get(&quot;/*name&quot;, new(Admin))
t.Get(&quot;/:name&quot;, new(Others))
t.Run()
</code></pre>

<p>以上代码, <code>Others</code> 的 <code>Get</code> 方法将永远不会被执行, 因为所有匹配的请求均会调用 <code>Admin</code> 的 <code>Get</code> 方法。</p>

<h1 id="注意事项">注意事项</h1>

<p>关于新路由也有几点需要注意的事项:</p>

<ul>
<li><p>命名和正则等可以混合使用,如:<code>/:name1-(:name2[0-9]+)</code>,但是要注意,必须要有分隔符,没有分隔符,在新增路由时会panic,比如如下形式就不受支持:<code>/:name1:name2</code></p></li>

<li><p>命名理论上是可以相同的,并且程序不会报错,但是不建议这样使用。通过Params.Get(&rdquo;:name&rdquo;)只会返回第一个,可通过自己遍历Params来获取多个相同的名字。比如:<code>/:name-:name</code>,这种规则是允许的,但是不推荐。</p></li>
</ul>

<h1 id="收尾">收尾</h1>

<p>新的路由形式上更加统一,相信可以满足大家的绝大部分需求。希望大家多提宝贵意见,可加QQ群:369240307,或者在 <a href="https://github.com/lunny/tango">github.com/lunny/tango</a> 以及 <a href="http://git.oschina.net/lunny/tango">git.oschina.net/lunny/tango</a> 中提出issue。</p>
</description>
|
||
<pubDate>Mon, 06 Apr 2015 15:23:00 +0000</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>在Xorm中使用Join和Extends标记</title>
|
||
<link>http://lunny.info/2015/1/13/在Xorm中使用Join和Extends标记.html</link>
|
||
<description><p>本文主要针对对Xorm已经有了一定了解的读者,如果您是第一次了解Xorm,请先阅读<a href="http://xorm.io/docs">xorm操作手册</a>。</p>

<p>Xorm的基本操作都是比较简单的,可能大家也都比较熟悉了。今天主要讲解extends标记和join的使用。</p>

<p>一般我们会针对数据库中的每一个表,建立一个对应的结构体。比如:</p>
<pre><code class="language-go">type User struct {
 Id int64
 Name string
}

type Account struct {
 Id int64
 UserId int64 `xorm:&quot;index&quot;`
 Amount int64
}

type Car struct {
 Id int64
 UserId int64 `xorm:&quot;index&quot;`
 Type int
}
</code></pre>

<p>我们定义了三个结构体,对应数据库的三个表,我们在启动时通过:</p>
<pre><code class="language-go">engine.Sync2(new(User), new(Account), new(Car))
</code></pre>

<p>来进行数据库结构的同步。在这个数据库结构中,我们假设一个用户拥有一个Account,一个用户拥有多个Car。</p>

<p>OK。复杂需求来了。</p>

<p>1)我们需要获得所有的用户的姓名和对应的账户的余额:</p>
<pre><code class="language-go">type AccountUser struct {
 Account `xorm:&quot;extends&quot;`
 User `xorm:&quot;extends&quot;`
}

var accounts = make([]*AccountUser, 0)
engine.Table(&quot;account&quot;).Join(&quot;INNER&quot;, &quot;user&quot;, &quot;account.user_id = user.id&quot;).Find(&amp;accounts)
</code></pre>

<p>OK。这样,我们就取出了user和对应的account,我们通过<code>account.Account</code>可以获取到Account的信息,通过<code>account.User</code>可以获取到User的信息。</p>

<p>这个是两个表Join,那么如果是三个表也是类似的做法。</p>

<p>2)我只需要用户名,不需要其它的内容:</p>
<pre><code class="language-go">type AccountUser struct {
 Account `xorm:&quot;extends&quot;`
 Name string
}
var accounts = make([]*AccountUser, 0)
engine.Table(&quot;account&quot;).Join(&quot;INNER&quot;, &quot;user&quot;, &quot;account.user_id = user.id&quot;).Find(&amp;accounts)
</code></pre>

<p>其实我们代码也是差不多的,但是这里我们实际上在查询数据库的时候是查询了user表的所有内容的。只是在最后赋值到结构体时,按需赋值。</p>

<p>3)更复杂的,我们还想知道每人有几辆车。</p>
<pre><code class="language-go">type AccountUser struct {
 Account `xorm:&quot;extends&quot;`
 Name string
 NumCars int
}
var accounts = make([]*AccountUser, 0)
engine.Sql(&quot;select account.*, user.name, (select count(id) from car where car.user_id = user.id) as num_cars from account, user where account.user_id = user.id&quot;).Find(&amp;accounts)
</code></pre>

<p>在这样的复杂需求下,我们使用了Sql函数和extends标记结合来完成这个操作。</p>
</description>
|
||
<pubDate>Tue, 13 Jan 2015 10:13:00 +0000</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>Tango,微内核可扩展的Go语言Web框架</title>
|
||
<link>http://lunny.info/2015/1/7/Tango,微内核可扩展的Go语言Web框架.html</link>
|
||
<description><h1 id="简介">简介</h1>

<p>Golang的web框架基本上处于一个井喷期,那么为什么要再造一个轮子。这是因为,目前可扩展性比较强的都是基于函数作为可执行体的,而以结构体作为执行体的框架目前可扩展性都不够强,包括我原先写的框架xweb也是如此。因此,一个全新的框架出来了,先上地址:<a href="https://github.com/lunny/tango">https://github.com/lunny/tango</a>。</p>

<p>初看Tango框架,感觉和Martini及Macaron非常的像。比如这段代码:</p>
<pre><code class="language-go">package main

import &quot;github.com/lunny/tango&quot;

func main() {
 t := tango.Classic()
 t.Get(&quot;/&quot;, func() string {
 return &quot;Hello tango!&quot;
 })
 t.Run()
}
</code></pre>

<p>这种其实大家都支持的。再看这个吧:</p>
<pre><code class="language-go">package main

import &quot;github.com/lunny/tango&quot;

type Action struct {}
func (Action) Get() string {
 return &quot;Hello tango!&quot;
}

func main() {
 t := tango.Classic()
 t.Get(&quot;/&quot;, new(Action))
 t.Run()
}
</code></pre>

<p>Tango同时支持函数和结构体作为执行体,不过主打是结构体,函数只作为兼容而用。下面对Tango的各种功能一一道来。</p>

<h1 id="路由">路由</h1>

<p>Tango目前同时支持3种路由规则:</p>

<ul>
<li><p>静态路由</p>
<pre><code class="language-go">tg.Get(&quot;/&quot;, new(Action))
</code></pre></li>

<li><p>命名路由</p>
<pre><code class="language-go">tg.Get(&quot;/:name&quot;, new(Action))
</code></pre>

<p>命名路由对应的参数内容可通过<code>ctx.Params().Get(&quot;:name&quot;)</code>来获得</p></li>

<li><p>正则路由</p></li>
</ul>
<pre><code class="language-go">tg.Get(&quot;/(.*)&quot;, new(Action))
</code></pre>

<p>正则路由对应的参数内容可通过<code>ctx.Params().Get(&quot;:0&quot;)</code>来获得</p>

<p>这里要注意命名路由和正则路由不可混用。</p>

<h1 id="执行体">执行体</h1>

<p>同时支持函数执行体和结构体执行体,支持的函数执行体形式如下,可以有零个或一个返回值,返回值由Return插件提供,后面会再提:</p>
<pre><code class="language-go">func()
func(http.ResponseWriter, *http.Request)
func(*tango.Context)
func(http.Response.Writer)
func(*http.Request)
</code></pre>

<p>同时支持包含<code>Get,Post,Head,Options,Trace,Patch,Delete,Put</code>作为成员方法的结构体作为执行体。</p>
<pre><code class="language-go">type Action struct {}
func (Action) Get() string {
 return &quot;Get&quot;
}

func (Action) Post() string {
 return &quot;Post&quot;
}

func (Action) Head() string {
 return &quot;Head&quot;
}

func (Action) Options() string {
 return &quot;Options&quot;
}

func (Action) Trace() string {
 return &quot;Trace&quot;
}

func (Action) Patch() string {
 return &quot;Patch&quot;
}

func (Action) Delete() string {
 return &quot;Delete&quot;
}

func (Action) Put() string {
 return &quot;Put&quot;
}
</code></pre>

<p>在使用路由时,可以用对应的方法或者Any来加路由:</p>
<pre><code class="language-go">t := tango.Classic()
t.Get(&quot;/1&quot;, new(Action))
t.Any(&quot;/2&quot;, new(Action))
</code></pre>

<h1 id="路由分组">路由分组</h1>

<p>Tango提供了Group来进行路由分组,Group的最简单形式如下:</p>
<pre><code class="language-go">g := tango.NewGroup()
g.Get(&quot;/1&quot;, func() string {
 return &quot;/1&quot;
})
g.Post(&quot;/2&quot;, func() string {
 return &quot;/2&quot;
})

t := tango.Classic()
t.Group(&quot;/api&quot;, g)
</code></pre>

<p>这样访问/api/1就会返回/1,访问/api/2就会返回/2</p>

<p>同时也可以这样:</p>
<pre><code class="language-go">t := tango.Classic()
t.Group(&quot;/api&quot;, func(g *tango.Group) {
 g.Get(&quot;/1&quot;, func() string {
 return &quot;/1&quot;
 })
 g.Post(&quot;/2&quot;, func() string {
 return &quot;/2&quot;
 })
})
</code></pre>

<p>Group也支持子Group:</p>
<pre><code class="language-go">t := tango.Classic()
t.Group(&quot;/api&quot;, func(g *tango.Group) {
 g.Group(&quot;/v1&quot;, func(cg *tango.Group) {
 cg.Get(&quot;/1&quot;, func() string {
 return &quot;/1&quot;
 })
 cg.Post(&quot;/2&quot;, func() string {
 return &quot;/2&quot;
 })
 })
})
</code></pre>

<p>最后,Group也支持逻辑分组:</p>
<pre><code class="language-go">o := tango.Classic()
o.Group(&quot;&quot;, func(g *tango.Group) {
 g.Get(&quot;/1&quot;, func() string {
 return &quot;/1&quot;
 })
})

o.Group(&quot;&quot;, func(g *tango.Group) {
 g.Post(&quot;/2&quot;, func() string {
 return &quot;/2&quot;
 })
})
</code></pre>

<p>这样,即使路由间只是逻辑上分开,而并没有上级路径分开,也是可以分成不同的组。</p>

<h1 id="返回值">返回值</h1>

<p>通过前面的例子,我们会发现,所有执行体函数,可以有一个返回值或者没有返回值。Tango的内部插件ReturnHandler来负责根据函数的第一个返回值的类型来自动的生成输出,默认规则如下:</p>

<ul>
<li><p><code>string</code>
返回string,则string会被转换为bytes并写入到ResponseWriter,默认状态码为200</p></li>

<li><p><code>[]byte</code>
返回[]byte, 则会直接写入ResponseWriter,默认状态码为200</p></li>

<li><p><code>error</code>
返回error接口,如果不为nil, 则返回状态码为500,内容为<code>error.Error()</code></p></li>

<li><p><code>AbortError</code>
返回tango.AbortError接口,如果不为nil,则返回状态码为<code>AbortError.Code</code>,内容为<code>AbortError.Error()</code></p></li>
</ul>

<p>当然了,你可以撰写你自己的插件来判断更多的返回值类型。返回值结合tango的一些tricker,可以极大的简化代码,比如:</p>

<p>如果Action结构体包含匿名结构体<code>tango.Json</code>或者<code>tango.Xml</code>,则返回值结果如下:</p>

<p>如果包含<code>tango.Json</code>匿名结构体,则返回头的<code>Content-Type</code>会自动设置为:<code>application/json</code>:</p>

<ul>
<li><p>如果返回值为error,则返回值为{&ldquo;err&rdquo;: err.Error()},状态码为200</p></li>

<li><p>如果返回值为AbortError,则返回值为{&ldquo;err&rdquo;: err.Error()},状态码为err.Code()</p></li>

<li><p>如果返回值为string,则返回值为{&ldquo;content&rdquo;: content},状态码为200</p></li>

<li><p>如果返回值为[]byte,则返回值为{&ldquo;content&rdquo;: string(content)},状态码为200</p></li>

<li><p>如果返回值为map,slice,结构体或其它可自动Json化的内容,则返回值为map自动json对应的值,状态码为200</p></li>
</ul>

<p>例如:</p>
<pre><code class="language-go">type Action struct {
 tango.Json
}

var i int
func (Action) Get() interface{} {
 if i == 0 {
 i = i + 1
 return map[string]interface{}{&quot;i&quot;:i}
 }
 return errors.New(&quot;could not visit&quot;)
}

func main() {
 t := tango.Classic()
 t.Any(&quot;/&quot;, new(Action))
 t.Run()
}
</code></pre>

<p>以上例子,访问时会始终返回json,第一次访问会返回map,第二次返回error。(注:这里只是演示代码,实际执行i必须加锁)</p>

<ul>
<li>Compress</li>
</ul>

<p>Tango拥有一个默认的压缩中间件,可以按照扩展名来进行文件的压缩。同时,你也可以要求某个Action自动或强制使用某种压缩。比如:</p>
<pre><code class="language-go">type CompressExample struct {
 tango.Compress // 添加这个匿名结构体,要求这个结构体的方法进行自动检测压缩
}

func (CompressExample) Get() string {
 return fmt.Sprintf(&quot;This is a auto compress text&quot;)
}

o := tango.Classic()
o.Get(&quot;/&quot;, new(CompressExample))
o.Run()
</code></pre>

<p>以上代码默认会检测浏览器是否支持压缩,如果支持,则看是否支持gzip,如果支持gzip,则使用gzip压缩,如果支持deflate,则使用deflate压缩。</p>
<pre><code class="language-go">type GZipExample struct {
 tango.GZip // add this for ask compress to GZip, if accept-encoding has no gzip, then not compress
}

func (GZipExample) Get() string {
 return fmt.Sprintf(&quot;This is a gzip compress text&quot;)
}

o := tango.Classic()
o.Get(&quot;/&quot;, new(GZipExample))
o.Run()
</code></pre>

<p>以上代码默认会检测浏览器是否支持gzip压缩,如果支持gzip,则使用gzip压缩,否则不压缩。</p>
<pre><code class="language-go">type DeflateExample struct {
 tango.Deflate // add this for ask compress to Deflate, if not support then not compress
}

func (DeflateExample) Get() string {
 return fmt.Sprintf(&quot;This is a deflate compress text&quot;)
}

o := tango.Classic()
o.Get(&quot;/&quot;, new(DeflateExample))
o.Run()
</code></pre>

<p>以上代码默认会检测浏览器是否支持deflate压缩,如果支持deflate,则使用deflate压缩,否则不压缩。</p>

<h1 id="static">Static</h1>

<p>Static 让你用一行代码可以完成一个静态服务器。</p>
<pre><code class="language-go">func main() {
 t := tango.New(tango.Static())
 t.Run()
}
</code></pre>

<p>然后,将你的文件放到 <code>./public</code> 目录下,你就可以通过浏览器放问到他们。比如:</p>
<pre><code>http://localhost/images/logo.png --&gt; ./public/images/logo.png
</code></pre>

<p>当然,你也可以加入<code>basicauth</code>或者你自己的认证中间件,这样就变为了一个私有的文件服务器。</p>
<pre><code class="language-go">func main() {
 t := tango.New()
 t.Use(AuthHandler)
 t.Use(tango.Static())
 t.Run()
}
</code></pre>

<h1 id="handler">Handler</h1>

<p>Handler 是tango的中间件。在tango中,几乎所有的事情都由中间件来完成。撰写一个你自己的中间件非常简单,并且我们鼓励您只加载需要的中间件。</p>

<p>tango的中间件只需要符合以下接口即可。</p>
<pre><code class="language-go">type Handler interface {
 Handle(*tango.Context)
}
</code></pre>

<p>同时,tango也提供了<code>tango.HandlerFunc</code>,以方便你将一个函数包装为中间件。比如:</p>
<pre><code class="language-go">func MyHandler() tango.HandlerFunc {
 return func(ctx *tango.Context) {
 fmt.Println(&quot;this is my first tango handler&quot;)
 ctx.Next()
 }
}

t := tango.Classic()
t.Use(MyHandler())
t.Run()
</code></pre>

<p>正常的形式也可以是:</p>
<pre><code class="language-go">type HelloHandler struct {}
func (HelloHandler) Handle(ctx *tango.Context) {
 fmt.Println(&quot;before&quot;)
 ctx.Next()
 fmt.Println(&quot;after&quot;)
}

t := tango.Classic()
t.Use(new(HelloHandler))
t.Run()
</code></pre>

<p>当然,你可以直接将一个包含tango.Context指针的函数作为中间件,如:</p>
<pre><code class="language-go">tg.Use(func(ctx *tango.Context){
 fmt.Println(&quot;before&quot;)
 ctx.Next()
 fmt.Println(&quot;after&quot;)
})
</code></pre>

<p>为了和标准库兼容,tango通过UseHandler支持http.Handler作为中间件,如:</p>
<pre><code class="language-go">tg.UseHandler(http.Handler(func(resp http.ResponseWriter, req *http.Request) {

}))
</code></pre>

<p>老的中间件会被action被匹配之前进行调用。</p>

<h1 id="call-stack">Call stack</h1>

<p>以下是中间件的调用顺序图:</p>
<pre><code>tango.ServeHttp
|--Handler1
 |--Handler2
 |-- ...HandlerN
 |---Action(If matched)
 ...HandlerN--|
 Handler2 ----|
 Handler1--|
(end)--|
</code></pre>

<p>在中间件中,您的中间件代码可以在<code>Next()</code>被调用之前或之后执行,Next表示执行下一个中间件或Action被执行(如果url匹配的话)。如果不调用Next,那么当前请求将会被立即停止,之后的所有代码将不会被执行。</p>

<h1 id="注入">注入</h1>

<p>更多的注入方式参见以下示例代码:</p>

<h2 id="context">Context</h2>
<pre><code class="language-go">type Action struct {
 tango.Ctx
}
</code></pre>

<h2 id="logger">Logger</h2>
<pre><code class="language-go">type Action struct {
 tango.Log
}
</code></pre>

<h2 id="params">Params</h2>
<pre><code class="language-go">type Action struct {
 tango.Params
}
</code></pre>

<h2 id="json">Json</h2>
<pre><code class="language-go">type Action struct {
 tango.Json
}
</code></pre>

<h2 id="xml">Xml</h2>
<pre><code class="language-go">type Action struct {
 tango.Xml
}
</code></pre>

<h1 id="第三方插件">第三方插件</h1>

<p>目前已经有了一批第三方插件,更多的插件正在陆续开发中,欢迎大家进行贡献:</p>

<ul>
<li><a href="https://github.com/tango-contrib/session">session</a> - Session管理</li>
<li><a href="https://github.com/tango-contrib/xsrf">xsrf</a> - 生成csrf token和进行校验</li>
<li><a href="https://github.com/tango-contrib/bind">bind</a> - 自动绑定参数到结构体</li>
<li><a href="https://github.com/tango-contrib/renders">renders</a> - Go模板引擎插件</li>
<li><a href="https://github.com/tango-contrib/dispatch">dispatch</a> - 多应用支持</li>
<li><a href="https://github.com/tango-contrib/tpongo2">tpongo2</a> - <a href="https://github.com/flosch/pongo2">Pongo2</a> 模板引擎支持</li>
<li><a href="https://github.com/tango-contrib/captcha">captcha</a> - 验证码插件</li>
<li><a href="https://github.com/tango-contrib/events">events</a> - 事件插件</li>
<li><a href="https://github.com/tango-contrib/flash">flash</a> - 在requests之间共享数据</li>
</ul>

<h1 id="案例">案例</h1>

<ul>
<li><a href="https://github.com/go-tango/wego">Wego</a> - golanghome.com论坛的修改版</li>
<li><a href="https://github.com/gofxh/blog">Blog</a> - 一个新型博客</li>
<li><a href="http://godaily.org">Godaily</a> - <a href="https://github.com/godaily/news">github.com/godaily/news</a> - Go新闻</li>
<li><a href="http://github.com/go-xorm/dbweb">dbweb</a> - go语言写的数据库管理web界面</li>
</ul>
</description>
|
||
<pubDate>Wed, 07 Jan 2015 16:00:00 +0000</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>foxmail for mac试用,效果不错</title>
|
||
<link>http://lunny.info/2013/3/12/foxmail-for-mac试用,效果不错.html</link>
|
||
<description><p>foxmail for mac v1.0 今天发布了。链接地址:<a href="http://foxmail.com.cn/mac/">http://foxmail.com.cn/mac/</a>,</p>

<p>稍微试用了下,初一看,简直就是sparrow的clone。再仔细用了用,感觉有几点吸引人的地方:</p>

<ul>
<li><p>有邮件规则这个功能,特别是增加了通知邮件这个功能,把常见的一些规则内置在邮箱中。</p></li>

<li><p>gmail的标签直接平铺在界面上,而不是像sparrow那样,需要点击才能够查看,更方便了,不过颜色不太好看。</p></li>

<li><p>邮件分组算法似乎比sparrow要好点。</p></li>
</ul>

<p>总之,果断转到了foxmail,删除了sparrow。</p>
</description>
|
||
<pubDate>Tue, 12 Mar 2013 09:51:00 +0000</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>VIM快捷键大全</title>
|
||
<link>http://lunny.info/2013/2/27/VIM快捷键大全.html</link>
|
||
<description><h1 id="vim快捷键大全">VIM快捷键大全</h1>

<p>Vim是一个超级牛的编辑器,可以说是专为程序员设计的编辑器,强大的有些不可思议 。不过其学习曲线稍显陡峭,前两天在网上找到一个小图表,比较全,如果对表中所列的命令学习熟悉以后,处理日常的文本就已经足够,所以贴出来,大家参考参考。</p>

<p>Vim的牛B之处不在于其功能之繁多,更不在于其学习曲线之陡峭,而在于这些命令大都可以进行组合 ,比如,9yy命令表示复制9行内容,9表示要复制的行数,同样100dd表示删除100行,当数字和命令合作的时候,就比单纯的命令更强大,同样,c命令表示擦除,w表示word即单词,那么cw就表示擦除一个单词,c5w就表示删除5个单词等等。将这些简单命令合成在一起,就可以发挥出难以想象的强大功能,而且可以使你的编辑工作充满乐趣。</p>

<h2 id="宏命令-macros">宏命令(Macros)</h2>

<p>高级一些的编辑器,都会包含宏功能,vim当然不能缺少了,在vim中使用宏是非常方便的:</p>

<ul>
<li>:qx 开始记录宏,并将结果存入寄存器x<br /></li>
<li>q 退出记录模式<br /></li>
<li>@x 播放记录在x寄存器中的宏命令</li>
</ul>

<p>稍微解释一下,当在normal模式下输入:qx后,你对文本的所有编辑动作将会被记录下来,再次输入q即退出了记录模式,然后输入@x对刚才记录下来的命令进行重复,此命令后可跟数字,表示要重复多少次,比如@x20,可以重复20次。这个在文本的批处理中是非常有用的。</p>

<h2 id="同时编辑多个文件">同时编辑多个文件</h2>

<p>在vim众多的插件中,有一个叫minibuffer的插件,就是下面所说的标签页功能了,可以支持同时编辑多个文件。</p>

<h3 id="标签命令">标签命令</h3>

<ul>
<li>:tabe fn 在一个新的标签页中编辑文件fn<br /></li>
<li>gt 切换到下一个标签页<br /></li>
<li>gT 切换到上一个标签页<br /></li>
<li>:tabr 切换到第一个标签页<br /></li>
<li>:tabl 切换到最后一个标签页<br /></li>
<li>:tabm [N] 把当前tab移动到第N个tab之后</li>
</ul>

<p>对,正如你所想象的那样,跟eclipse, ue等的标签页是一个意思!</p>

<h3 id="窗口命令">窗口命令</h3>

<ul>
<li>ctrl+w s 水平分割窗口<br /></li>
<li>ctrl+w w 切换窗口<br /></li>
<li>ctrl+w q 退出当前窗口(由于同时有多个文件,此命令不会影响其他窗口)<br /></li>
<li>ctrl+w v 垂直分割窗口</li>
</ul>

<h3 id="其他">其他</h3>

<p>vim在保存之前不会对文件做实际的修改,只是加载到缓冲区中,对文件的编辑其实是对缓冲区的编辑,直到:w时才会存入物理文件。</p>

<ul>
<li>:e file 把file加载到新的缓冲区中<br /></li>
<li>:bn 跳转到下一个缓冲区<br /></li>
<li>:bd 删除缓冲区(关闭文件)<br /></li>
<li>:sp fn 分割窗口,并将fn加载到新的窗口中</li>
</ul>

<h3 id="退出编辑器">退出编辑器</h3>

<ul>
<li>:w 将缓冲区写入文件,即保存修改<br /></li>
<li>:wq 保存修改并退出<br /></li>
<li>:x 保存修改并退出<br /></li>
<li>:q 退出,如果对缓冲区进行过修改,则会提示<br /></li>
<li>:q! 强制退出,放弃修改</li>
</ul>

<h3 id="查找替换">查找替换</h3>

<ul>
<li>/pattern 向后搜索字符串pattern<br /></li>
<li>?pattern 向前搜索字符串pattern<br /></li>
<li>n 下一个匹配(如果是/搜索,则是向下的下一个,?搜索则是向上的下一个)<br /></li>
<li>N 上一个匹配(同上)<br /></li>
<li>:%s/old/new/g 搜索整个文件,将所有的old替换为new<br /></li>
<li>:%s/old/new/gc 搜索整个文件,将所有的old替换为new,每次都要你确认是否替换</li>
</ul>

<h3 id="复制粘贴">复制粘贴</h3>

<ul>
<li>dd 删除光标所在行<br /></li>
<li>dw 删除一个字(word)<br /></li>
<li>x 删除当前字符<br /></li>
<li>X 删除前一个字符<br /></li>
<li>D 删除到行末<br /></li>
<li>yy 复制一行,此命令前可跟数字,标识复制多行,如6yy,表示从当前行开始复制6行<br /></li>
<li>yw 复制一个字<br /></li>
<li>y$ 复制到行末<br /></li>
<li>p 粘贴粘贴板的内容到当前行的下面<br /></li>
<li>P 粘贴粘贴板的内容到当前行的上面<br /></li>
<li>]p 有缩进的粘贴,vim会自动调节代码的缩进<br /></li>
<li>&ldquo;a 将内容放入/存入a寄存器,可以支持多粘贴板</li>
</ul>

<p>附:比如常用的一个寄存器就是系统寄存器,名称为+,所以从系统粘贴板粘贴到vim中的命令为&rdquo;+p,注意此处的+不表示操作符,二十一个寄存器。</p>

<h3 id="移动光标">移动光标</h3>

<p>在vim中移动光标跟其他的编辑器中有很大的区别,不过一旦学会了,就会飞速 的在文本中移动了。</p>

<ul>
<li>h,j,k,l 上,下,左,右<br /></li>
<li>ctrl-f 上翻一页<br /></li>
<li>ctrl-b 下翻一页<br /></li>
<li>% 跳到与当前括号匹配的括号处,如当前在{,则跳转到与之匹配的}处<br /></li>
<li>w 跳到下一个字首,按标点或单词分割<br /></li>
<li>W 跳到下一个字首,长跳,如end-of-line被认为是一个字<br /></li>
<li>e 跳到下一个字尾<br /></li>
<li>E 跳到下一个字尾,长跳<br /></li>
<li>b 跳到上一个字<br /></li>
<li>B 跳到上一个字,长跳<br /></li>
<li>0 跳至行首,不管有无缩进,就是跳到第0个字符<br /></li>
<li>^ 跳至行首的第一个字符<br /></li>
<li>$ 跳至行尾<br /></li>
<li>gg 跳至文件的第一行<br /></li>
<li>gd 跳至当前光标所在的变量的声明处<br /></li>
<li>[N]G 跳到第N行,如0G,就等价于gg,100G就是第100行<br /></li>
<li>fx 在当前行中找x字符,找到了就跳转至<br /></li>
<li>; 重复上一个f命令,而不用重复的输入fx<br /></li>
<li>tx 与fx类似,但是只是跳转到x的前一个字符处<br /></li>
<li>Fx 跟fx的方向相反<br /></li>
<li>),( 跳转到上/下一个语句<br /></li>
<li>* 查找光标所在处的单词,向下查找<br /></li>
<li># 查找光标所在处的单词,向上查找<br /></li>
<li>`. 跳转至上次编辑位置</li>
</ul>

<h3 id="在屏幕上移动">在屏幕上移动</h3>

<ul>
<li>H 移动光标到当前屏幕上最上边的一行<br /></li>
<li>M 移动光标到当前屏幕上中间的一行<br /></li>
<li>L 移动光标到当前屏幕上最下边的一行</li>
</ul>

<h3 id="书签">书签</h3>

<ul>
<li>ma 把当前位置存成标签a<br /></li>
<li>`a 跳转到标签a处</li>
</ul>

<h3 id="编辑">编辑</h3>

<ul>
<li>r 替换一个字符<br /></li>
<li>J 将下一行和当前行连接为一行<br /></li>
<li>cc 删除当前行并进入编辑模式<br /></li>
<li>cw 删除当前字,并进入编辑模式<br /></li>
<li>c$ 擦除从当前位置至行末的内容,并进入编辑模式<br /></li>
<li>s 删除当前字符并进入编辑模式<br /></li>
<li>S 删除光标所在行并进入编辑模式<br /></li>
<li>xp 交换当前字符和下一个字符<br /></li>
<li>u 撤销<br /></li>
<li>ctrl+r 重做<br /></li>
<li>. 重复上一个编辑命令<br /></li>
<li>~ 切换大小写,当前字符<br /></li>
<li>g~iw 切换当前字的大小写<br /></li>
<li>gUiw 将当前字变成大写<br /></li>
<li>guiw 将当前字变成小写<br /></li>
<li>&gt;&gt; 将当前行右移一个单位<br /></li>
<li>&lt;&lt; 将当前行左移一个单位(一个tab符)<br /></li>
<li>== 自动缩进当前行</li>
</ul>

<h2 id="插入模式">插入模式</h2>

<ul>
<li>i 从当前光标处进入插入模式<br /></li>
<li>I 进入插入模式,并置光标于行首<br /></li>
<li>a 追加模式,置光标于当前光标之后<br /></li>
<li>A 追加模式,置光标于行末<br /></li>
<li>o 在当前行之下新加一行,并进入插入模式<br /></li>
<li>O 在当前行之上新加一行,并进入插入模式<br /></li>
<li>Esc 退出插入模式</li>
</ul>

<h2 id="可视模式">可视模式</h2>

<h3 id="标记文本">标记文本</h3>

<ul>
<li>v 进入可视模式,单字符模式<br /></li>
<li>V 进入可视模式,行模式<br /></li>
<li>ctrl+v 进入可视模式,列模式 ,类似于UE的列模式<br /></li>
<li>o 跳转光标到选中块的另一个端点<br /></li>
<li>U 将选中块中的内容转成大写<br /></li>
<li>O 跳转光标到块的另一个端点<br /></li>
<li>aw 选中一个字<br /></li>
<li>ab 选中括号中的所有内容,包括括号本身<br /></li>
<li>aB 选中{}括号中的所有内容<br /></li>
<li>ib 选中括号中的内容,不含括号<br /></li>
<li>iB 选中{}中的内容,不含{}</li>
</ul>

<h2 id="对标记进行动作">对标记进行动作</h2>

<ul>
<li>&gt; 块右移<br /></li>
<li>&lt; 块左移<br /></li>
<li>y 复制块<br /></li>
<li>d 删除块<br /></li>
<li>~ 切换块中内容的大小写</li>
</ul>

<p>好了,这张简表就说到这里,当然它涵盖的范围有限,但是正如片头说的那样,vim的强大之处在于这些命令可以组合起来使用,那样才能体现出其强大的编辑功能。我在网上找到的这个文档也顺便贴出来,大家可以同时参考。如果有机会,我尽量找些例子来说明这些命令在实际中的用法,那样才可以更好的使用它,提高我们的工作效率。</p>
</description>
|
||
<pubDate>Wed, 27 Feb 2013 02:49:39 +0000</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>三步在Debian中创建自己的KVM虚拟机</title>
|
||
<link>http://lunny.info/2012/8/15/三步在Debian中创建自己的KVM虚拟机.html</link>
|
||
<description><p>1 电脑一台,必须的,并且注意必须要支持VT-x,同时在BIOS中Enable。然后就安装Debian吧,推荐64位的6.0版本,安装过程基本都是下一步,就不用详细介绍了。
 捎带一笔,国内比较快的Debian的源mirrors.163.com,速度还可以。</p>

<p>2 下一步就是要安装虚拟机软件了。</p>
<pre><code class="language-sh">apt-get install qemu-kvm bridge-utils uml-utilities
</code></pre>

<p>安装完毕就需要配置网络了,修改/etc/network/interfaces,将eth0的配置注释掉,加入br0的配置:</p>
<pre><code>auto br0 
iface br0 inet static #dhcp 
bridge_ports eth0 
address 192.168.1.39 
netmask 255.255.255.0 
gateway 192.168.1.6
name-servers 8.8.8.8
</code></pre>

<p>修改完毕记得重启网络,实在不行就重启吧。</p>

<p>3 第三步自然就是创建虚拟机,安装运行了。</p>

<ul>
<li>先创建磁盘啊</li>
</ul>
<pre><code class="language-sh">kvm-img create disk.img 20G
</code></pre>

<ul>
<li>然后安装系统了</li>
</ul>
<pre><code class="language-sh">kvm -boot d -cdrom xp.iso -m 1024 -hda ./disk.img -net nic -net tap -vnc :1
</code></pre>

<ul>
<li>最后运行啊,tap网卡会自动创建的,不用担心</li>
</ul>
<pre><code class="language-sh">kvm -boot c -m 1024 -hda ./disk.img -net nic -net tap -vnc :1
</code></pre>

<ul>
<li>当然如果需要后台运行的话,建议安装screen</li>
</ul>
<pre><code class="language-sh">apt-get install screen
</code></pre>
</description>
|
||
<pubDate>Wed, 15 Aug 2012 08:06:04 +0000</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>py2exe打包BeautifulSoup遇到问题及解决</title>
|
||
<link>http://lunny.info/2012/3/25/py2exe打包BeautifulSoup遇到问题及解决.html</link>
|
||
<description><p>今天用py2exe打包一个小程序,但是怎么都不能够找到BeautifulSoup模块,经过Google得知,py2exe无法读取压缩的egg文件,解决方法重新用解压方法安装BeautifulSoup,同时发现pip比easy_install要更方便,于是安装了pip。OK,打包成功,大功告成。</p>
</description>
|
||
<pubDate>Sun, 25 Mar 2012 04:49:39 +0000</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>一个json格式在线错误检测的网址</title>
|
||
<link>http://lunny.info/2012/3/8/一个json格式在线错误检测的网址.html</link>
|
||
<description><p><a href="http://json.parser.online.fr/">http://json.parser.online.fr/</a></p>
</description>
|
||
<pubDate>Thu, 08 Mar 2012 01:04:57 +0000</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>开源许可证的区别图示</title>
|
||
<link>http://lunny.info/2012/1/19/开源许可证的区别图示.html</link>
|
||
<description><p>看了<a href="http://www.ruanyifeng.com">阮一峰</a>的一片关于开源许可证的文章<a href="http://www.ruanyifeng.com/blog/2011/05/how_to_choose_free_software_licenses.html">《如何选择开源许可证?》</a>,感觉里面这个图还有改进的余地,因此在他这个图的基础上作了一点改动。所有的许可证从左至右是从限制较多到限制较少的进行排列。如下图所示:</p>

<p><a href="/static/media/images/2012/01/OpenSource.png"><img src="/static/media/images/2012/01/OpenSource.png" alt="开源许可证" title="开源许可证" /></a></p>
</description>
|
||
<pubDate>Thu, 19 Jan 2012 02:48:11 +0000</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>Chrome扩展更新:Smart RSS Subscriber for Google Reader™(v0.1.5)</title>
|
||
<link>http://lunny.info/2011/8/25/Chrome扩展更新:Smart-RSS-Subscriber-for-Google-Reader™(v0.1.5).html</link>
|
||
<description><p>就是原先的Google Reader RSS Subscriber,因为google的品牌政策,所以修改了扩展名称,另做了少量修改和修复。扩展地址为:
<a href="https://chrome.google.com/webstore/detail/ngjinemddlnikfdlbnbdbajdhagbcjmg">https://chrome.google.com/webstore/detail/ngjinemddlnikfdlbnbdbajdhagbcjmg</a></p>
</description>
|
||
<pubDate>Thu, 25 Aug 2011 07:06:18 +0000</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>用VBScript实现生成目录树以Excel格式保存</title>
|
||
<link>http://lunny.info/2011/7/4/用VBScript实现生成目录树以Excel格式保存.html</link>
|
||
<description><p>帮朋友实现这个功能,本想采用Python,熟门熟路的,但是朋友机器可能没有安装。对于小白,担心他会有更多的麻烦,只好试试好久没用的VBScript了。保存为Excel格式其实偷了懒,保存的是HTML格式,不过可以很容易的转到Excel中。实现代码如下:</p>
<pre><code class="language-vbscript">''''''''''''''''''''''''''''''''''''
' Copyright (C) 2011
' Author: Lunny Xiao
' Email: xiaolunwen@gmail.com
' Date: 2011-7-1
' Description: traversing directory and building directory tree as html file format. If you want excel format,
' you can open the html, select all content and paste to a new excel file. That's all.
''''''''''''''''''''''''''''''''''''

'Option Explicit

set fso = createobject(&quot;scripting.filesystemobject&quot;)

Sub TraverFolder(fd, folder, level)
 Set f = fso.GetFolder(folder)
 Set files = f.Files
 Set sfs = f.SubFolders
 dim i
 dim j
 i = 0
 For Each a in sfs
 fd.write(&quot;\n&quot;)
 For j = 1 To level
 fd.write(&quot;\n\n&quot;)
 Next
 fd.write(&quot;\n&quot; &amp; a.name &amp; &quot;\n\n&quot;)
 TraverFolder fd, a.Path, (level+1)
 Next

 i = 0
 For Each b In files
 fd.write(&quot;\n&quot;)
 For j = 1 To level
 fd.write(&quot;\n\n&quot;)
 Next
 fd.write(&quot;\n&quot; &amp; b.name &amp; &quot;\n\n&quot;)
 Next
 If i = 0 Then
 fd.write(vbcrlf)
 End If
End Sub

msg=&quot;请输入根目录:&quot;
dir=Inputbox(msg, &quot;根目录&quot;) 

set ts = fso.opentextfile(&quot;d:\\res.html&quot;, 2, true)
ts.write(&quot;\n&quot;)
TraverFolder ts, dir, 0
ts.write(&quot;\n&quot;)
ts.close()
</code></pre>

<ul>
<li>使用方法</li>
</ul>

<p>将上述代码保存为一个.vbs文件,直接双击该文件,会弹出框询问根目录(可从资源管理器复制粘贴)。点击确定,会在D盘根目录生成一个res.html文件。打开该文件,全选,复制,粘贴到一个新的Excel文件即可。</p>
</description>
|
||
<pubDate>Mon, 04 Jul 2011 01:03:32 +0000</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>Python实现图书10位ISBN号转换为13位</title>
|
||
<link>http://lunny.info/2011/5/25/Python实现图书10位ISBN号转换为13位.html</link>
|
||
<description><p>ISBN是图书的一个国际通用编号,详细信息可参考维基百科中的词条:<a href="http://zh.wikipedia.org/wiki/ISBN">http://zh.wikipedia.org/wiki/ISBN</a>。</p>

<p>ISBN在07年前使用的是10位编号,07年后并入国际货品编号,升级为13位,以下为将10位编号转换为13位编号的Python代码:</p>
<pre><code class="language-python">def isbn10to13(isbn):
 if len(isbn) != 10:
 return None
 isbn = &quot;978&quot;+isbn[:-1]
 sum = 0
 for i, b in enumerate(isbn):
 sum += (1 if i % 2 == 0 else 3) * int(b)
 return &quot;%s%d&quot; % (isbn, (10 - sum % 10) % 10)
</code></pre>

<p>输入内容为10位的ISBN号去除连接符“-”的字符串,输出内容为13位的ISBN号去除连接符“-”的字符串。主要的工作在于计算出最后一位的校验码。</p>
</description>
|
||
<pubDate>Wed, 25 May 2011 05:28:03 +0000</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>MySQL中的insert ignore into, replace into, insert select, on duplicate key update的用法小结</title>
|
||
<link>http://lunny.info/2011/5/23/MySQL中的insert-ignore-into,-replace-into,-insert-select,-on-duplicate-key-update的用法小结.html</link>
|
||
<description><p>在MySQL中进行条件插入数据时,可能会用到以下语句,现小结一下。我们先建一个简单的表来作为测试:</p>
<pre><code class="language-sql">CREATE TABLE `books` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `name` varchar(200) NOT NULL,
 PRIMARY KEY (`id`),
 UNIQUE KEY `NewIndex1` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
</code></pre>

<h2 id="insert-ignore-into">insert ignore into</h2>

<p>当插入数据时,如出现错误时,如重复数据,将不返回错误,只以警告形式返回。所以使用ignore请确保语句本身没有问题,否则也会被忽略掉。例如:</p>
<pre><code class="language-sql">insert ignore into books (name) values ('MySQL Manual')
</code></pre>

<h2 id="on-duplicate-key-update">on duplicate key update</h2>

<p>当primary或者unique重复时,则执行update语句,如update后为无用语句,如id=id,则同1功能相同,但错误不会被忽略掉。例如,为了实现name重复的数据插入不报错,可使用一下语句:</p>
<pre><code class="language-sql">insert into books (name) values ('MySQL Manual') on duplicate key update id = id
</code></pre>

<h2 id="insert-select-where-not-exist">insert &hellip; select &hellip; where not exist</h2>

<p>根据select的条件判断是否插入,可以不光通过primary 和 unique来判断,也可通过其它条件。例如:</p>
<pre><code class="language-sql">insert into books (name) select 'MySQL Manual' from dual where not exists (select id from books where id = 1)
</code></pre>

<h2 id="replace-into">replace into</h2>

<p>如果存在primary or unique相同的记录,则先删除掉。再插入新记录。</p>
<pre><code class="language-sql">replace into books SELECT 1, 'MySQL Manual' FROM books
</code></pre>
</description>
|
||
<pubDate>Mon, 23 May 2011 04:29:27 +0000</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>Redis在Windows下的安装配置及Python调用</title>
|
||
<link>http://lunny.info/2011/5/17/Redis在Windows下的安装配置及Python调用.html</link>
|
||
<description><p>Redis是一个Key-Value数据库,优点是简单,高效,稳定;缺点是所有数据存放于内存(会定期dump到磁盘),当然内存小时可以配置虚拟内存,不过性能会有所下降。
官方只提供源代码下载,并且不支持Windows,可从第三方下载Windows下的预编译文件,下载地址:</p>

<p><a href="https://github.com/dmajkic/redis/downloads">https://github.com/dmajkic/redis/downloads</a></p>

<p>解压后,所有命令均在一个目录下,服务器端直接运行redis-server.exe即可,如果需要特殊配置,可以指定配置文件或者使用redis-client进行配置。配置文件其实就是配置命令的一个集合。redis的配置命令(2.2之后)可以做到在不重启服务的情况下生效,包括redis版本更新,slave转为master等等。</p>

<p>Redis的Python接口目前比较成熟的是redis-py,项目地址为<a href="https://github.com/andymccurdy/redis-py,通过git可将源码下载到本地,git的安装和使用请参考[《Git使用123》](http://www.lunny.info/html/2011/01/24/625330.html)。">https://github.com/andymccurdy/redis-py,通过git可将源码下载到本地,git的安装和使用请参考[《Git使用123》](http://www.lunny.info/html/2011/01/24/625330.html)。</a></p>
<pre><code class="language-sh">git clone https://github.com/andymccurdy/redis-py.git
cd redis-py
python setup install
</code></pre>

<p>redis-py的使用请参考<a href="https://github.com/andymccurdy/redis-py">https://github.com/andymccurdy/redis-py</a>。</p>
</description>
|
||
<pubDate>Tue, 17 May 2011 07:42:30 +0000</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>一个Python函数:执行MySQL的SQL文件</title>
|
||
<link>http://lunny.info/2011/4/29/一个Python函数:执行MySQL的SQL文件.html</link>
|
||
<description><p>有些时候,我们需要通过python来执行SQL文件,那么这个函数就有用武之地了。调用之前确保安装了mysql。Linux和Windows应该是都可以用的,Linux下没有测试过。</p>
<pre><code class="language-python">from subprocess import Popen, PIPE

def excSQLFile(host, db, user, passwd, charset, filename):
 process = Popen('mysql -h%s -D%s -u%s -p%s --default-character-set=%s' \
 % (host, db, user, passwd, charset),
 stdout=PIPE, stdin=PIPE, shell=True)
 output = process.communicate('source ' + filename)[0]
 return output
</code></pre>
</description>
|
||
<pubDate>Fri, 29 Apr 2011 07:30:26 +0000</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>在输入框/搜索框中显示提示文字的JS效果</title>
|
||
<link>http://lunny.info/2011/4/26/在输入框/搜索框中显示提示文字的JS效果.html</link>
|
||
<description><p>我敢说,做前端的十个里八个做过这个。今天把我的做法记录一下哈,直接上代码:</p>
<pre><code class="language-javascript">function trim(str){
 return str.replace(/^\s*(.*?)[\s\n]*$/g,'$1')
}
var ic = false
function a(){return document.getElementById(&quot;a&quot;)}

function cha() {return trim(a().value).length &gt; 0}

function ifblur() {
 ic = cha()
 if (!ic) {
 a().value = &quot;我是提示文字&quot;
 a().style.color = &quot;gray&quot;
 }
 }

function iffocus() {
 if (!ic) {
 a().value = &quot;&quot;
 a().style.color = &quot;black&quot;
 ic = true
 }
}
</code></pre>
</description>
|
||
<pubDate>Tue, 26 Apr 2011 06:14:43 +0000</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>手写JS代码,十行搞定ajax异步请求!!!</title>
|
||
<link>http://lunny.info/2011/4/26/手写JS代码,十行搞定ajax异步请求!!!.html</link>
|
||
<description><p>据说要创业的人都必须会讲故事,好吧,我开始忽悠了。
开始的开始,有一个需求,要用非常少的js代码从后台获得数据。然后,听说要用到ajax,听说jquery很小很好用,结果下载下来一看。。。xxKB。不是吧,比页面本身还大。杀鸡焉用牛刀,于是google,百度,复制,粘贴,就有了下面这段代码:</p>
<pre><code class="language-javascript">function handler() {
 if(this.readyState == 4 &amp;&amp; this.status == 200 &amp;&amp; this.responseText != null) {
 obj = eval('(' + this.responseText + ')')
 alert(obj)
 }
}
function getCnt() {
 var objXmlHttp = null
 if(window.XMLHttpRequest){
 objXmlHttp = new XMLHttpRequest()
 }else if(window.ActiveXObject){
 try{objXmlHttp = new ActiveXObject(&quot;Msxml2.XMLHTTP&quot;)}
 catch(failed){
 try{objXmlHttp = new ActiveXObject(&quot;Microsoft.XMLHTTP&quot;)}catch(failed){}
 }
 }
 if (objXmlHttp) {
 objXmlHttp.onreadystatechange = handler
 objXmlHttp.open(&quot;GET&quot;, &quot;/url?v=&quot;+(new Date()).valueOf(), true)
 objXmlHttp.send()
 }
}
</code></pre>

<p>最后的最后,说十行夸张了点,不过基本上已经很小了,一般的使用应该没什么问题了。至于浏览器兼容性嘛,绝大部分应该支持。ie7及chrome dev至少是经过验证的。
参考请见:
<a href="http://www.w3.org/TR/XMLHttpRequest/">http://www.w3.org/TR/XMLHttpRequest/</a></p>
</description>
|
||
<pubDate>Tue, 26 Apr 2011 05:54:57 +0000</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>编译Pandion</title>
|
||
<link>http://lunny.info/2011/3/11/编译Pandion.html</link>
|
||
<description><p>Pandion(潘迪安)是一款Windows平台下的XMPP客户端,因其界面比较简洁,所以引起了我的兴趣。详细信息可访问<a href="http://pandion.im/">http://pandion.im/</a></p>

<p>编译之前,需要先安装些软件:</p>

<ul>
<li><p>VC++ Express 2010
<a href="http://download.microsoft.com/download/5/c/1/5c156922-ca10-49d8-b7e7-9bf092c3b6eb/VS2010ExpressCHS.iso">http://download.microsoft.com/download/5/c/1/5c156922-ca10-49d8-b7e7-9bf092c3b6eb/VS2010ExpressCHS.iso</a></p></li>

<li><p>Wix 3.5
<a href="http://wix.sf.net">http://wix.sf.net</a></p></li>

<li><p>RoboCopy,如果是Vista之前的Windows版本,默认没有robocopy这个命令,可下载Windows Server 2003 Resource Kit Tools,安装好后就有了。
<a href="http://www.microsoft.com/downloads/en/confirmation.aspx?familyid=9d467a69-57ff-4ae7-96ee-b18c4790cffd&amp;displaylang=en">http://www.microsoft.com/downloads/en/confirmation.aspx?familyid=9d467a69-57ff-4ae7-96ee-b18c4790cffd&amp;displaylang=en</a></p></li>

<li><p>Git,因为pandion的源码是存放在github的,所以需要用git来获取</p></li>
</ul>

<p>下面是编译过程:</p>

<p>1.安装以上软件,基本上都是“下一步”;</p>

<p>2.设置环境变量%VS100COMNTOOLS%到&rdquo;c:\program files\microsoft visual studio 10.0\common7\tools\&rdquo;</p>

<p>3.打开git bash,输入命令:</p>
<pre><code class="language-sh">git clone https://github.com/pandion/pandion.git
</code></pre>

<p>4.找到源码,执行build_all.bat即可,编译成功后在Installer\Wix下会生成一个安装包文件Pandion_2.6.0.msi</p>

<p>5.编译过程出现一个警告和2个错误,未详究,暂未发现影响。</p>
</description>
|
||
<pubDate>Fri, 11 Mar 2011 04:35:56 +0000</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>Git使用123之(二)</title>
|
||
<link>http://lunny.info/2011/3/8/Git使用123之(二).html</link>
|
||
<description><p>1.如果使用ssh非共享账号方式使用Git时,在Git库初始化时请加上--shared的参数,加上这个参数会自动将所有生成的文件或目录设置为库根目录所在的组可读可写。否则可能会遇到一个账号push的内容,另一个账号无法修改的问题。如果已经设置好了库,也可使用</p>
<pre><code class="language-sh">git init --bare --shared
</code></pre>

<p>来设置权限,这个命令是初始化或重新初始化,不会删除git库的内容。但这个命令似乎只对以后的内容权限有改变,对于以前的内容,可自己修改下权限。
2.git reset可恢复到某个commit,但也有可能会删掉未跟踪的文件,用时要小心。
3.在MyEclipse上安装Egit时,最好是下载离线安装包放到dropins下然后重启MyEclipse,否则经常会更新失败。如果找不到下载的地方,可以自己下载源码进行编译。
4.git bash或者Egit插件使用私钥登录的问题,打开git bash,输入:</p>
<pre><code class="language-sh">ssh-keygen
</code></pre>

<p>然后yes和回车,再输入:</p>
<pre><code class="language-sh">scp ~/.ssh/id_rsa yourname@yourserver:~/.ssh/authorized_keys
</code></pre>

<p>*此方法要求安装git时选择的是openssh而不是putty。
MyEclipse中,打开Preference,搜索ssh,选择“load private key”,找到私钥存放的目录,即可。
如果不知道,可在git bash下输入:</p>
<pre><code class="language-sh">cd ~/.ssh
pwd
</code></pre>
</description>
|
||
<pubDate>Tue, 08 Mar 2011 01:42:05 +0000</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>Tornado在Windows下解析文件mime类型错误一则及解决</title>
|
||
<link>http://lunny.info/2011/2/15/Tornado在Windows下解析文件mime类型错误一则及解决.html</link>
|
||
<description><p>今日遇到一则错误,tornado服务器在解析文件的mime类型时报错,无法获取,错误如下。</p>
<pre><code>codeUnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 0
</code></pre>

<p>出错误原因:
Windows注册表中保存的mime类型对应项中包含非ascii字符</p>

<p>解决方法:
执行regedit,找到
<code>HKEY_LOCAL_MACHINE\SOFTWARE\Classes\MIME\Database\Content Type</code>
在其中找到非英文字母的mime类型,例如我遇到的“视频/x-m4v”,删除即可。</p>

<p>如何避免:
有些程序在安装时会新增一些mime类型,如果胡乱加,那么肯定就会出问题。我遇到的这个问题,估计是哪个视频播放软件犯了一个低级错误,不该翻译的地方翻译了。这个错误一般情况下不会有什么危害,真正除了问题却有可能摸不着头脑。</p>

<p>附:
mime类型标准的网址:<a href="http://www.iana.org/assignments/media-types/">http://www.iana.org/assignments/media-types/</a>。访问可以看到,顶级类型里不包含“视频”,只有“vedio&rdquo;。</p>
</description>
|
||
<pubDate>Tue, 15 Feb 2011 08:13:06 +0000</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>Git使用123</title>
|
||
<link>http://lunny.info/2011/1/24/Git使用123.html</link>
|
||
<description><ol>
<li>Git是一个分布式的源代码管理软件,相较于其它类似软件有很多特性。具体的介绍及下载,请访问官网:<a href="http://git-scm.com">http://git-scm.com</a></li>
<li>学习Git的几个网址:
1)官方文档:<a href="http://git-scm.com/documentation">http://git-scm.com/documentation</a>
2)Pro Git: <a href="http://progit.org/book/">http://progit.org/book/</a>, 简体中文版:<a href="http://progit.org/book/zh/">http://progit.org/book/zh/</a></li>
</ol>

<p>基本上就够用了,下面只是简单记录下常用命令:
1. 在服务器上创建一个库:</p>
<pre><code class="language-sh">mkdir src.git
cd src.git
git init --bare
</code></pre>

<p>OK. 这就好了。
2. 从服务器上Clone代码库到本地:</p>
<pre><code class="language-sh">git clone lunny@myserver:/home/lunny/src.git
</code></pre>

<p>这会通过ssh协议从远程拷贝代码库到本地的src.git目录,我们的源代码就可以放到这个目录下了。</p>

<ol>
<li>可以将需要管理的文件都放到这个目录下。git将文件分为四种状态:
[untracked]:在代码库目录下,但是没有进行跟踪的文件或目录
[unmodified]:在上一次版本基础上未修改过的文件
[modified]:在上一次版本基础上修改过的文件
[staged]:已经标记为需要在下一次提交中上传的文件</li>
</ol>

<p>使用git status可查看当前的文件跟踪状态,请经常此命令进行查看。</p>

<ol>
<li>跟踪新文件或新修改用</li>
</ol>
<pre><code class="language-sh">git add [filename]
</code></pre>

<p>如果要添加所有新增或修改过的文件可使用</p>
<pre><code class="language-sh">git add .
</code></pre>

<ol>
<li>从已跟踪里面删除不需要跟踪的文件用</li>
</ol>
<pre><code class="language-sh">git rm [filename]
</code></pre>

<ol>
<li>不需要跟踪的文件,可建立.gitignore文件,并将*.bak等类似语句加入进行屏蔽</li>
<li>如果所有需要进行跟踪的文件都已添加好,则可进行一次提交</li>
</ol>
<pre><code class="language-sh">git commit -m &quot;add a file&quot;
</code></pre>

<ol>
<li>提交好后,如果需要推送到远程服务器上:
<code>sh
git push origin master
</code></li>
<li>如果服务器上其他人有更新,本地要进行更新:
<code>sh
git pull
</code>
刚开始,有这几步就够用了。如果要进一步了解,特别是分支的概念,可以访问前面提到的两个网址。</li>
</ol>
</description>
|
||
<pubDate>Mon, 24 Jan 2011 05:32:08 +0000</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>加密软件TrueCrypt安装及使用简介(三)</title>
|
||
<link>http://lunny.info/2010/12/21/加密软件TrueCrypt安装及使用简介(三).html</link>
|
||
<description><h2 id="3-4-创建分区-u盘加密卷">3.4 创建分区/U盘加密卷</h2>

<p>创建U盘加密卷和创建文件型加密卷过程大致相同。如图3-15所示,在主界面选择【创建加密卷】后,选择【加密非系统分区/设备】。</p>

<p><a href="/static/media/images/2010/12/3-15.jpg"><img src="/static/media/images/2010/12/3-15.jpg" alt="3-15" title="3-15" /></a></p>

<p>图3-15</p>

<p>点击【下一步】,将出现卷类型的界面,这个和文件型卷是相同的。这里我们以“隐藏的TrueCrypt”为例进行讲解。如图3-16所示。</p>

<p><a href="/static/media/images/2010/12/3-16.jpg"><img src="/static/media/images/2010/12/3-16.jpg" alt="3-16" title="3-16" /></a></p>

<p>图3-16</p>

<p>标准卷和隐藏卷是有区别的:隐藏卷实际会创建两个卷,会要求输入两次密码,第一个卷和第一次密码和标准卷一样。第二个密码是给隐藏卷的。当在使用时输入第一个密码,则会加载标准卷,当输入的密码是第二个密码,则会显示隐藏卷。</p>

<p>点击【下一步】之后将到一个加密卷模式选择界面,这个界面在选择【隐藏卷】时有,在选择【标准卷】时是没有的。这里可以选择【常规模式】,即新创建卷,如果选择【直接模式】则会在已有的标准卷中新增一个隐藏卷。</p>

<p><a href="/static/media/images/2010/12/3-17.jpg"><img src="/static/media/images/2010/12/3-17.jpg" alt="3-17" title="3-17" /></a></p>

<p>图3-17</p>

<p>这里我们选择【常规模式】,点击【下一步】,将会到一个选择加密分区或设备的界面。如图3-18所示:</p>

<p><a href="/static/media/images/2010/12/3-18.jpg"><img src="/static/media/images/2010/12/3-18.jpg" alt="3-18" title="3-18" /></a></p>

<p>图3-18</p>

<p>这里可以选择加密硬盘上的一个分区或者U盘上的一个分区,也可以加密整个硬盘或者整个U盘。这里有一个小提示,如果加密了整个U盘,那么你到一个没有安装TrueCrypt的计算机上,就必须先安装好,才能够将U盘挂载。所以,实际上一般有两种做法:</p>

<p>1) 将U盘分成多个分区,区分其中的加密分区和非加密分区。将TrueCrypt的解压版本拷贝到非加密分区中,到一台新计算机上不需要安装,直接运行即可。</p>

<p>2) 使用文件型加密卷。</p>

<p>选择好设备后,点击【确定】,并点击【下一步】,会弹出【外层加密卷】的界面,先进行外层加密卷的设置,如图3-19所示。</p>

<p><a href="/static/media/images/2010/12/3-19.jpg"><img src="/static/media/images/2010/12/3-19.jpg" alt="3-19" title="3-19" /></a></p>

<p>图3-19</p>

<p>接下来的步骤和创建文件型加密卷并无区别,这里需要注意的是,最后一步会对磁盘分区进行格式化,所以请注意先备份该分区中原有的数据。</p>

<p><a href="/static/media/images/2010/12/3-20.jpg"><img src="/static/media/images/2010/12/3-20.jpg" alt="3-20" title="3-20" /></a></p>

<p>图3-20</p>

<p>在完成之后将会进行隐藏卷的设置,设置方法和外层卷类似。这里需要注意一点,标准卷和隐藏卷的密码不要相同。</p>

<p>这里有一个需要注意的地方,如果一个分区是1G大小,那么外层卷会显示为1G,隐藏卷会显示为你设置的大小。不要在外层卷放太多文件,当使用太大时会破坏隐藏卷。</p>

<h2 id="3-5-使用分区-u盘加密卷">3.5 使用分区/U盘加密卷</h2>

<p>当分区或U盘加密卷创建好之后,就可以使用了。如图3-21所示,点击选择设备,选择加密的分区或者U盘,然后点击载入,将会弹出密码输入框,后面步骤和文件型加密卷相同。</p>

<p>这里有一点需要注意的,如果创建了隐藏卷。那么输入标准卷密码程序时就加载标准卷;输入隐藏卷密码就加载隐藏卷。如果加载了一个卷,就不能加载另一个卷了。两个卷同时只能加载一个。</p>

<p><a href="/static/media/images/2010/12/3-21.jpg"><img src="/static/media/images/2010/12/3-21.jpg" alt="3-21" title="3-21" /></a></p>

<p>图3-21</p>

<h2 id="3-6-加密系统启动分区或者整个硬盘">3.6 加密系统启动分区或者整个硬盘</h2>

<p>加密方式基本和加密分区相同,不同的是,加密完成后,在启动Windows之前会要求输入密码,否则无法启动Windows。</p>

<h2 id="3-7-修改加密卷密码">3.7 修改加密卷密码</h2>

<p>在加密卷已经载入后,点击界面上的【加密卷工具】,将会弹出一个菜单,第一个菜单项即为修改加密卷密码。</p>

<p><a href="/static/media/images/2010/12/3-22.jpg"><img src="/static/media/images/2010/12/3-22.jpg" alt="3-22" title="3-22" /></a></p>

<p>图3-22</p>
</description>
|
||
<pubDate>Tue, 21 Dec 2010 08:16:40 +0000</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>加密软件TrueCrypt安装及使用简介(二)</title>
|
||
<link>http://lunny.info/2010/12/21/加密软件TrueCrypt安装及使用简介(二).html</link>
|
||
<description><h1 id="3-truecrypt使用">3 TrueCrypt使用</h1>

<h2 id="3-1-基本原理">3.1 基本原理</h2>

<p>TrueCrypt在使用时类似于虚拟光驱之类的软件。首先将加密的卷文件、或者加密的U盘,加密的分区等等都加载为某个Windows盘符,然后就跟使用本地分区一样进行使用就可以了。使用完成之后再卸载掉。</p>

<p>这里需要注意,TrueCrypt的加密方式是先创建加密容器(加密卷或者分区、设备),然后将需要加密的内容存放到这个加密容器中。</p>

<h2 id="3-2-创建文件加密卷">3.2 创建文件加密卷</h2>

<p>TrueCrypt启动后默认在Windows通知区有一个小图标,点击该图标,将显示TrueCrypt的主界面,如图3-1所示。</p>

<p><a href="/static/media/images/2010/12/3-1.jpg"><img src="/static/media/images/2010/12/3-1.jpg" alt="3-1" title="3-1" /></a></p>

<p>图3-1</p>

<p>点击【创建加密卷】,将显示加密卷创建向导,如图3-2所示:</p>

<p><a href="/static/media/images/2010/12/3-2.jpg"><img src="/static/media/images/2010/12/3-2.jpg" alt="3-2" title="3-2" /></a></p>

<p>图3-2</p>

<p>加密卷有三种,分别为:</p>

<p>1) 文件型:即在磁盘中创建一个加密文件;</p>

<p>2) 加密非系统分区/设备:即将某个非系统分区或者加密U盘等移动设备变成加密卷;</p>

<p>3) 加密系统分区或整个硬盘:即将硬盘某个系统分区或者整个硬盘加密。</p>

<p>这里我们选择【创建文件型加密卷】,点击之后会出现图3-3的界面。</p>

<p><a href="/static/media/images/2010/12/3-3.jpg"><img src="/static/media/images/2010/12/12/3-3.jpg" alt="3-3" title="3-3" /></a></p>

<p>图3-3</p>

<p>点击【下一步】后,如图3-4所示:</p>

<p><a href="/static/media/images/2010/12/3-4.jpg"><img src="/static/media/images/2010/12/3-4.jpg" alt="3-4" title="3-4" /></a></p>

<p>图3-4</p>

<p>将要求选择一个文件来作为加密卷文件,这个文件的扩展名可随意,最好是用一个不容易被认出是加密文件的名称。这里要特别注意:</p>

<p>1) 不要选择一个已经存在的文件,否则该文件将会被覆盖。</p>

<p>2) 不要自己误删除这个文件,否则加密内容将丢失,无法恢复。</p>

<p><a href="/static/media/images/2010/12/3-5.jpg"><img src="/static/media/images/2010/12/3-5.jpg" alt="3-5" title="3-5" /></a></p>

<p>图3-5</p>

<p>文件选择好之后,会出现【加密选项】界面,如图3-6所示,在这里可以选择加密的算法,一般选择默认即可。</p>

<p><a href="/static/media/images/2010/12/3-6.jpg"><img src="/static/media/images/2010/12/3-6.jpg" alt="3-6" title="3-6" /></a></p>

<p>图3-6</p>

<p>点击【下一步】会出现输入文件加密卷大小的界面,这里我们可以根据自己的需求输入一个值。除非有大文件需要存,一般都不要设置太大,方便保存到U盘中或文件服务器中。如果一个文件满了,那么可以再建一个,也可根据规则,比如一个月一个文件或者一年。</p>

<p><a href="/static/media/images/2010/12/3-7.jpg"><img src="/static/media/images/2010/12/3-7.jpg" alt="3-7" title="3-7" /></a></p>

<p>图3-7</p>

<p>点击【下一步】要求输入加密卷的密码(图3-8),这里密码一定要记牢。如果忘记了密码,加密卷中的文件基本没有找回来的可能。同时这里也可以采用密钥文件的形式,如果使用了文件密钥,注意文件密钥一定要保存好,如果丢失,加密卷也无法打开。</p>

<p><a href="/static/media/images/2010/12/3-8.jpg"><img src="/static/media/images/2010/12/3-8.jpg" alt="3-8" title="3-8" /></a></p>

<p>图3-8</p>

<p>密码输入完毕后将会到最后一步,进行加密卷的格式化。这里可以选择FAT格式和NTFS格式。如果小于4G,建议用FAT,这个文件格式可以在Linux和MacOS中读写。如果选择NTFS,则大小可以超出4G,但是只能在Windows系列操作系统之间读写,Linux中默认可以读取,需要修改配置允许写入才可以写;MacOS中默认无法读取和写入。</p>

<p><a href="/static/media/images/2010/12/3-9.jpg"><img src="/static/media/images/2010/12/3-9.jpg" alt="3-9" title="3-9" /></a></p>

<p>图3-9</p>

<p><a href="/static/media/images/2010/12/3-10.jpg"><img src="/static/media/images/2010/12/3-10.jpg" alt="3-10" title="3-10" /></a></p>

<p>图3-10</p>

<p>至此,文件型加密卷创建完成,我们可以来使用了。</p>

<h2 id="3-3-使用文件加密卷">3.3 使用文件加密卷</h2>

<p>文件加密卷创建完成后,就跟普通文件类似,可以像普通文件那样复制粘贴。在需要使用时必须通过TrueCrypt进行挂载。挂载的方式如图3-11所示:</p>

<p><a href="/static/media/images/2010/12/3-11.jpg"><img src="/static/media/images/2010/12/3-11.jpg" alt="3-11" title="3-11" /></a></p>

<p>图3-11</p>

<p>在盘符列表框中选择某个盘符,例如选择M:,然后在【加密卷】下的输入框中输入或点击【选择文件】选中刚才创建的加密卷文件,点击【载入】,将会弹出密码输入框,如图3-12所示。</p>

<p><a href="/static/media/images/2010/12/3-12.jpg"><img src="/static/media/images/2010/12/3-12.jpg" alt="3-12" title="3-12" /></a></p>

<p>图3-12</p>

<p>输入此加密卷文件的密码,如果生成了密钥文件,选择密钥文件的路径。点击确定,如果密码正确,系统将会将加密卷中的内容载入到M:盘中。如图3-13所示,在M:盘上点击右键,选择打开,或者通过windows资源管理器选择M盘(如图3-14所示)均可打开。此时可以对M盘进行读写操作,和使用普通的分区没有任何区别。</p>

<p><a href="/static/media/images/2010/12/3-13.jpg"><img src="/static/media/images/2010/12/3-13.jpg" alt="3-13" title="3-13" /></a></p>

<p>图3-13</p>

<p><a href="/static/media/images/2010/12/3-14.jpg"><img src="/static/media/images/2010/12/3-14.jpg" alt="3-14" title="3-14" /></a></p>

<p>图3-14</p>
</description>
|
||
<pubDate>Tue, 21 Dec 2010 08:14:49 +0000</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>加密软件TrueCrypt安装及使用简介(一)</title>
|
||
<link>http://lunny.info/2010/12/21/加密软件TrueCrypt安装及使用简介(一).html</link>
|
||
<description><h1 id="1-简介"><strong>1 简介</strong></h1>

<p>TrueCrypt是一款开源免费的数据加密软件,它支持如下特性:</p>

<p>1) 支持Windows 7/Vista/XP, Mac OS X, Linux等操作系统,加密文件或者设备可在不同操作系统间使用;</p>

<p>2) 可以选择AES、Serpent和Twofish等算法,也可以组合他们使用,暴力破解的难度非常高;</p>

<p>3) 支持加密文件,加密硬盘分区,加密整个硬盘,加密U盘等移动设备等;</p>

<p>4) 支持密码认证和私钥文件认证;</p>

<p>5) 支持双加密卷,即普通卷和隐藏卷。</p>

<h1 id="2-安装及配置">2 安装及配置</h1>

<h2 id="2-1-安装truecrypt">2.1 安装TrueCrypt</h2>

<p>TrueCrypt在Windows上的安装很简单,一直下一步即可。双击安装程序,如图1-1将显示TrueCrypt的使用许可,此时选择接受。</p>

<p><a href="/static/media/images/2010/12/2-1.jpg"><img src="/static/media/images/2010/12/2-1.jpg" alt="2-1" title="2-1" /></a></p>

<p>图2-1</p>

<p>接下来询问你是用安装版本还是解压版本,如果我们是安装在台式电脑上,那么我们选择安装,见图2-2;如果是选择将软件放到U盘中,方便在别的地方试用,则选择解压。</p>

<p><a href="/static/media/images/2010/12/2-2.jpg"><img src="/static/media/images/2010/12/2-2.jpg" alt="2-2" title="2-2" /></a></p>

<p>图2-2</p>

<p>点击下一步,会出现安装选项(图2-3),这一步可以选择给所有用户安装或者只安装个当前用户,另外创建系统还原点可以不选。</p>

<p><a href="/static/media/images/2010/12/2-3.jpg"><img src="/static/media/images/2010/12/2-3.jpg" alt="2-3" title="2-3" /></a></p>

<p>图2-3</p>

<p>点击安装,系统将显示安装日志,过一会儿安装完成,如图2-4所示:</p>

<p><a href="/static/media/images/2010/12/2-4.jpg"><img src="/static/media/images/2010/12/2-4.jpg" alt="2-4" title="2-4" /></a></p>

<p>图2-4</p>

<p>此时安装已经完成,会有一个快捷方式在桌面上显示。</p>

<h2 id="2-2-配置简体中文界面">2.2 配置简体中文界面</h2>

<p>TrueCrypt支持多语言,它是通过不同的语言文件来达成多语言的。已经下载了语言文件可以直接跳到2.2.2。</p>

<h3 id="2-2-1-下载简体中文语言包">2.2.1 下载简体中文语言包</h3>

<p>通过TrueCrypt界面可以直接链接到语言包下载网址,步骤如图2-5所示,点击菜单栏上的【Settings】,选择【Language】,将显示语言切换界面,如图2-6所示。点击界面上的【download language pack】,将会打开语言包下载网址。</p>

<p><a href="/static/media/images/2010/12/2-5.jpg"><img src="/static/media/images/2010/12/2-5.jpg" alt="2-5" title="2-5" /></a></p>

<p>图2-5</p>

<p><a href="/static/media/images/2010/12/2-6.jpg"><img src="/static/media/images/2010/12/2-6.jpg" alt="2-6" title="2-6" /></a></p>

<p>图2-6</p>

<p>将下载到的语言包文件解压,将Language.zh-cn.xml文件拷贝到c:\program files\TrueCrypt文件夹下。关闭并重新打开TrueCrypt。</p>

<p><a href="/static/media/images/2010/12/2-7.jpg"><img src="/static/media/images/2010/12/2-7.jpg" alt="2-7" title="2-7" /></a></p>

<p>图2-7</p>

<h3 id="2-2-1将界面语言修改为简体中文">2.2.1将界面语言修改为简体中文</h3>

<p>点击菜单栏上的【Settings】,选择【Language】,将显示语言切换界面,此时选择【简体中文】,点击【OK】即可。</p>

<p><a href="/static/media/images/2010/12/2-8.jpg"><img src="/static/media/images/2010/12/2-8.jpg" alt="2-8" title="2-8" /></a></p>

<p>图2-8</p>
</description>
|
||
<pubDate>Tue, 21 Dec 2010 08:12:02 +0000</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>博客加入静态化及缓存机制,提升访问速度</title>
|
||
<link>http://lunny.info/2010/12/20/博客加入静态化及缓存机制,提升访问速度.html</link>
|
||
<description><p>博客访问速度很慢,256M的VPS还在美国,只好做做优化了。本次优化只是将首页和日志页进行了静态化。下面记录下步骤:
1.登录WordPress后台,在“永久链接”里选择自定义,改为“/html/%year%/%monthnum%/%day%/%post_id%.html”,保存。</p>

<p>2.修改nginx的配置,加入以下rewrite配置,加入的位置,我是在server_name之后</p>
<pre><code class="language-nginx">if (!-e $request_filename) {
rewrite ^([_0-9a-zA-Z-]+)?(/wp-.*) $2 last;
rewrite ^([_0-9a-zA-Z-]+)?(/.*.php)$ $2 last;
rewrite ^ /index.php last;
}
</code></pre>

<p>3.安装Cos-Html-Cache插件,并启用
同时需要
3.1 在wordpress的根目录创建index.bak并执行chmod 0666 index.bak
3.2 在wordpress的根目录创建html目录并执行chmod 0777 html</p>

<p>4.重新生成sitemap并提交给各搜索引擎</p>

<p>Over.</p>
</description>
|
||
<pubDate>Mon, 20 Dec 2010 08:41:27 +0000</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>玲珑小巧的桌面搜索工具 - Everything</title>
|
||
<link>http://lunny.info/2010/12/17/玲珑小巧的桌面搜索工具---Everything.html</link>
|
||
<description><p>如果你是一个条理非常清晰的人,可能会记得自己电脑里面所有的文件的存放地点,否则也许你会希望借助搜索来找到你的文件。很不幸的是Windows里面的文件搜索功能不是一般的差,自从google桌面发布后,类似的产品很多很多了,不过大多为重量级产品。今天介绍的这款软件Everything大小只有几百K,而且搜索速度飞快,支持即时搜索(看来这个google推出的新玩意,其实早有人做了)。并且支持绿色安装,直接下载解压即可。不多说了,下载试试就知道了。下载地址:<a href="http://www.voidtools.com/">http://www.voidtools.com/</a></p>
</description>
|
||
<pubDate>Fri, 17 Dec 2010 03:09:02 +0000</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>Hibernate中映射枚举类型</title>
|
||
<link>http://lunny.info/2010/12/3/Hibernate中映射枚举类型.html</link>
|
||
<description><p>Hibernate中提供了org.hibernate.type.EnumType类来进行枚举类型的映射,可以将枚举实例的name或者ordinal映射到数据库,具体在HBM文件中配置如下:</p>
<pre><code class="language-xml">&lt;property name=&quot;bank&quot;&gt;
 &lt;column name=&quot;BANK_MARK&quot; length=&quot;20&quot; not-null=&quot;true&quot;/&gt;
 &lt;type name=&quot;org.hibernate.type.EnumType&quot;&gt;
 &lt;param name=&quot;enumClass&quot;&gt;com.lunny.Bank&lt;/param&gt;
 &lt;param name=&quot;type&quot;&gt;12&lt;/param&gt;
 &lt;/type&gt;
&lt;/property&gt;
</code></pre>

<p>其中type的值指的java.sql.Types.*的某个值12对应的是VARCHAR,表示将枚举的name存到数据库中。默认是存的ordinal,也可指明4,即Integer。</p>

<p>OK。问题来了,如果枚举用的是一个自定义的数值或字符来分开的,如何映射?我的做法是看看EnumType的源代码,自己写一个(本来是想继承的,但是发现很多方法都是private,基本无法重用)。主要的改变有如下几点:
1.加入成员变量</p>
<pre><code class="language-java">public static final String KEY = &quot;key&quot;;
private Getter getter;
</code></pre>

<p>其中的Getter是枚举中获取key值的方法</p>

<p>2.重写nullSafeGet, nullSafeSet,setParameterValues,objectToSQLString,toXMLString,fromXMLString这几个方法。
2.1 在setParameterValues方法中增加新的配置项解析,用于解析Key,并将key的Getter方法保存起来。</p>
<pre><code class="language-xml">&lt;property name=&quot;bank&quot;&gt;
 &lt;column name=&quot;BANK_MARK&quot; length=&quot;20&quot; not-null=&quot;true&quot; /&gt;
 &lt;type name=&quot;org.hibernate.type.EnumType&quot;&gt;
 &lt;param name=&quot;enumClass&quot;&gt;com.lunny.Bank&lt;/param&gt;
 &lt;param name=&quot;type&quot;&gt;12&lt;/param&gt;
 &lt;param name=&quot;key&quot;&gt;key&lt;/param&gt;
 &lt;/type&gt;
&lt;/property&gt;
</code></pre>

<p>2.2 在上述其它方法中加入判断Getter是否为空,如果不为空,则调用该方法来获得要存入数据库的值从而替代name或者ordianl。</p>
</description>
|
||
<pubDate>Fri, 03 Dec 2010 03:34:49 +0000</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>启用新域名lunny.info</title>
|
||
<link>http://lunny.info/2010/11/22/启用新域名lunny.info.html</link>
|
||
<description><p>原先的域名是lunny.org,想来想去,我可不是什么组织,就是一个人,所以lunny.me或者lunny.info还比较符合,可惜lunny.me已经被注册了。所以以后就用lunny.info这个域名了。</p>
</description>
|
||
<pubDate>Mon, 22 Nov 2010 01:51:10 +0000</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>发布Chrome扩展:Google Reader RSS Subscriber Plus</title>
|
||
<link>http://lunny.info/2010/11/22/发布Chrome扩展:Google-Reader-RSS-Subscriber-Plus.html</link>
|
||
<description><p>写这个扩展是因为原先一直用的Google Reader RSS Subscriber扩展基本停止更新了,几个必须的关键特性不支持,只好自己动手,丰衣足食。扩展地址为:<a href="https://chrome.google.com/extensions/detail/ngjinemddlnikfdlbnbdbajdhagbcjmg">https://chrome.google.com/extensions/detail/ngjinemddlnikfdlbnbdbajdhagbcjmg</a>。支持如下特性:一键订阅,支持https,支持当前页,支持订阅检测,支持黑名单。</p>
</description>
|
||
<pubDate>Mon, 22 Nov 2010 01:45:27 +0000</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>Go语言的简介及Ubuntu下的安装和编译</title>
|
||
<link>http://lunny.info/2010/1/12/Go语言的简介及Ubuntu下的安装和编译.html</link>
|
||
<description><p><strong>简介</strong></p>

<p>GO语言是Google基于BSD发布的开源系统级编程语言,目标是融合Python的开发效率和C的运行时效率于一体。该项目的网址是<a href="http://golang.org。目前只支持Linux,freebsd和Mac">http://golang.org。目前只支持Linux,freebsd和Mac</a> OS X平台的amd64和386架构。</p>

<h1 id="安装">安装</h1>

<p>有一个快速的编译器安装说明,见<a href="http://golang.org/doc/install.html">http://golang.org/doc/install.html</a>。我在虚拟机中的Ubuntu9.10下安装过程如下:</p>

<p>1.设置环境变量</p>

<p>一共需要设置4个变量</p>
<pre><code class="language-sh">export GOROOT=$HOME/go

export GOARCH=386

export GOOS=linux

export GOBIN=$HOME/bin
</code></pre>

<p>如果你的平台是AMD64,请将GOARCH替换成amd64;另外GOBIN是可选的,可以</p>
<pre><code class="language-sh">mkdir ~/bin
</code></pre>

<p>这里是存放GO语言编译器连接器的目录,需要加入到PATH:</p>
<pre><code class="language-sh">PATH=${PATH}:$HOME/bin
</code></pre>

<p>将以上这行和上面4个export拷贝到你的.bashrc中。重新打开终端窗口。</p>

<p>2.获取GO源码</p>

<p>GO使用C写的,需要获取源码后编译,在命令行执行以下命令:</p>
<pre><code class="language-sh">apt-get install python-setuptools python-dev

sudo easy_install mercurial

hg clone -r release https://go.googlecode.com/hg/ $GOROOT

sudo apt-get install bison gcc libc6-dev ed make

cd $GOROOT/src

make all
</code></pre>

<p>等到出现:</p>
<pre><code>--- cd ../test

N known bugs; 0 unexpected bugs
</code></pre>

<p>(N为某个数字,我这里为4)后编译完成。</p>

<h1 id="编写hello-go">编写Hello GO!</h1>

<p>针对不同的架构,编译器和链接器都是不一样的:</p>

<p>386对应的编译器是8g,链接器是8l;</p>

<p>amd64对应的编译器是6g,链接器是6l;</p>

<p>OK.我们来编写一个最简单的GO程序,代码如下:</p>
<pre><code class="language-go">package main

import &quot;fmt&quot;

func main() {

 fmt.Printf(&quot;hello, world\n&quot;)

}
</code></pre>

<p>将上面的代码保存为hello.go并编译之:</p>
<pre><code class="language-sh">8g hello.go
</code></pre>

<p>编译后生成hello.8,再链接:</p>
<pre><code class="language-sh">8l hello.8
</code></pre>

<p>生成hello.out,执行:</p>
<pre><code class="language-sh">./hello.out
</code></pre>

<p>终端上将显示:</p>
<pre><code>Hello, GO!
</code></pre>

<p>如果要编写大型程序,Make工具依然有效。</p>

<h1 id="语法及类库文档">语法及类库文档</h1>

<p>语法见这里:</p>

<p><a href="http://golang.org/doc/go_spec.html">http://golang.org/doc/go_spec.html</a></p>

<p>类库见这里:</p>

<p><a href="http://golang.org/pkg/">http://golang.org/pkg/</a></p>

<p>有用的文档:</p>

<p><a href="http://golang.org/doc/effective_go.html">http://golang.org/doc/effective_go.html</a></p>

<p><a href="http://golang.org/doc/go_tutorial.html">http://golang.org/doc/go_tutorial.html</a></p>

<p>如果以前学习的C++可以参考这里:</p>

<p><a href="http://golang.org/doc/go_for_cpp_programmers.html">http://golang.org/doc/go_for_cpp_programmers.html</a></p>

<h1 id="保持最新版本">保持最新版本</h1>

<p>目前GO语言还在不断完善中,还没有到可以进入生产环境的时候,如果想及时更新最新的版本,如下:</p>
<pre><code class="language-sh">hg pull

hg update release

make all
</code></pre>
</description>
|
||
<pubDate>Tue, 12 Jan 2010 05:40:00 +0000</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>Firefox 扩展 Flashblock</title>
|
||
<link>http://lunny.info/2008/3/14/Firefox-扩展-Flashblock.html</link>
|
||
<description><p>现在用Flash的东东太多,广告/网络视频/某些网络应用等等,估计将来这个趋势也将会继续。这个Firefox扩展可以阻止</p>

<ul>
<li>Macromedia Flash</li>
<li>Macromedia Shockwave</li>
<li>Macromedia Authorware</li>
</ul>

<p>等,安装页面:
<a href="https://addons.mozilla.org/en-US/firefox/addon/433">https://addons.mozilla.org/en-US/firefox/addon/433</a></p>
</description>
|
||
<pubDate>Fri, 14 Mar 2008 14:57:54 +0000</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>IpMsg for Linux(Ubuntu)安装手记</title>
|
||
<link>http://lunny.info/2007/12/9/IpMsg-for-Linux(Ubuntu)安装手记.html</link>
|
||
<description><p>ipmsg是一个开源的局域网消息和文件传送工具,其最大的优点是可以直接传送文件夹,并且传送速度非常快。ipmsg目前已有了windows, mac, linux版本。为了从我的Linux(Ubuntu 7.04)传送文件到局域网内一台windows机器,我试着安装了一下。</p>

<p>1.先下载源码</p>

<p>我下载的是for gnome2版本的源码
<a href="http://www.ipmsg.org/archive/g2ipmsg-0.9.1.tar.gz">http://www.ipmsg.org/archive/g2ipmsg-0.9.1.tar.gz</a>
目前最新的版本是0.9.3,不过这个版本在编译是会出现问题,所以我选择了0.9.1版本,差别不大。</p>

<p>2.解压
在ubuntu中用命令行</p>
<pre><code class="language-sh">tar xzvf g2ipmsg-0.9.1.tar.gz
</code></pre>

<p>或者菜单右键用归档管理器解压即可。</p>

<p>3.修改语言
用文本编辑工具,比如gedit,打开src/codeset.c文件,将其中的CP932更改为CP936(英文)或者GBK(中文)并保存。</p>

<p>4.安装编译依赖项</p>
<pre><code class="language-sh">sudo apt-get install libxml-parser-perl libgnomeui-dev libpanel-applet2-dev
sudo apt-get install gettext intltool
</code></pre>

<p>5.编译</p>
<pre><code class="language-sh">./configure –enable-systray
make
sudo make install
</code></pre>

<p>这下可用了,重启后在主菜单的附件中将会有Gnome2 IP Messenger的快捷方式。
OK,完成,可以在windows和linux之间传送文件或者文件夹了。</p>
</description>
|
||
<pubDate>Sun, 09 Dec 2007 11:55:29 +0000</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>从西宁回来</title>
|
||
<link>http://lunny.info/2007/7/5/从西宁回来.html</link>
|
||
<description><p>早就知道青海,却忘记了它的首府是西宁。飞机要下降的时候,满眼是荒山,跟新疆的山有一拼,不过山与山的谷地有绿色。飞机场很小,好像是筑在一个高台上,我都担心跑道不够长。机场高速两边的树倒是很多,比我来时的想象要好。这里海拔大概在2200米,将来要是海平面上涨,是不是大家都得往这边跑?</p>

<p>没想到象我这么身体好的人居然也高原反应了。跑到青海湖旁边,海拔在3200米左右,居然头有点晕。老实说,青海湖真没什么好看的,不过黄鱼的味道还不错(呵呵,捕捞和吃这个好像是违法)。在湖边转了转,看了看藏獒就往原子城走了。</p>

<p><a href="/static/media/images/2007/07/p6260141.jpg"><img src="/static/media/images/2007/07/p6260141.jpg" alt="青海湖" /></a></p>

<p>听说半路会看到沙漠,于是半路停车跟沙漠合了一个影,没有想象中的沙漠好</p>

<p><a href="/static/media/images/2007/07/p6260151.jpg"><img src="/static/media/images/2007/07/p6260151.jpg" alt="青海湖边沙漠" /></a></p>

<p>原子城,金银滩基本上在一起,据说王洛宾创作《在那遥远的地方》就是在金银滩,据说卓玛在藏语里是仙女的意思,所以叫卓玛的很多,王洛宾的这首歌就是写给一位卓玛的。这里有新鲜的牦牛酸奶,不过干喝似乎不咋样,加点蜂蜜味道还不错。原子城,就是当年爆炸中国第一颗核弹和氢弹的地方,现在开放了,爱国主义教育基地,去档案馆看了看展览,看了一场小电影。</p>

<p><a href="/static/media/images/2007/07/p6260154.jpg"><img src="/static/media/images/2007/07/p6260154.jpg" alt="原子城" /></a></p>

<p>回到西宁,高原反应好点了。睡了一晚上,高原反应彻底消失了。</p>

<p>第二天,去了藏传佛教中赫赫有名的塔尔寺,据说这个寺院是黄教的发源地,创始人就是宗喀巴,号称位于如来之下的第二佛,也是达赖和班禅的师父。这些寺院建造在八个山头之间,呈现一个莲花状。虽然不信佛,却也装得象个信徒似的拜了拜,幸好可以不用跪拜。到了供香火的地方,正好手里头有硬币,于是看到有油的地方,把硬币用力摁上去,居然没掉下来,看来还不错。小金瓦殿,大金瓦殿,感觉都差不多。一个细节要讲一讲,就是这里的门槛是不能踩的,必须跨过去。</p>

<p><a href="/static/media/images/2007/07/001.jpg"><img src="/static/media/images/2007/07/001.jpg" alt="塔尔寺" /></a></p>

<p>西宁是东西长,南北窄的一个城市,北边是山称为北山,南边也是山,称为南山。在南山的山顶有一个很醒目的建筑,据说是上海浦东出钱建的,因为浦东和西宁是友好城市?(好像区和市是不能这样称呼吧?)从这个建筑上基本上可以看全整个西宁。</p>

<p><a href="/static/media/images/2007/07/006.jpg"><img src="/static/media/images/2007/07/006.jpg" alt="西宁全貌" /></a></p>

<p>见过这么大的土豆吗?</p>

<p><a href="/static/media/images/2007/07/014.jpg"><img src="/static/media/images/2007/07/014.jpg" alt="西宁的土豆" /></a></p>

<p>这里的农家乐,土鸡店生意红火,在农家院子的石桌上吃这些东西,别有一番风味,虽然环境一般般,味道还不错。</p>

<p>回到上海,居然已经32度,并且闷热闷热的。看来象他们说的,夏天呆在西宁比呆在上海好。</p>
</description>
|
||
<pubDate>Thu, 05 Jul 2007 15:11:34 +0000</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>犹豫间,终于还是去买了菜回来</title>
|
||
<link>http://lunny.info/2007/6/17/犹豫间,终于还是去买了菜回来.html</link>
|
||
<description><p>我的懒惰还不是一般的懒,学车回来,路过菜场,为今天要不要做饭犹豫了半天。一般此种情况,最终肯定会是我的懒惰胜利,今天却出了意外!最终还是跑到菜场里面了,跟卖菜的大妈大叔们砍砍价,终于提搂着黄瓜、西红柿、辣椒、鸡蛋,还有一只烧鸡往回走了。回到家,蒸上米饭,打两个鸡蛋,切根黄瓜,切成片,捡两片尝尝,还不错。其实做饭也没有想像的复杂,除了要做得好吃之外。老婆不在身边的日子,怎么看都不像个大老爷们,什么都得自己照顾。看看我做的黄瓜炒鸡蛋。</p>

<p><a href="/static/media/2007/06/00001.jpg"><img src="/static/media/2007/06/00001.jpg" alt="黄瓜炒鸡蛋" /></a></p>
</description>
|
||
<pubDate>Sun, 17 Jun 2007 11:14:39 +0000</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>最近又写得少了</title>
|
||
<link>http://lunny.info/2007/6/13/最近又写得少了.html</link>
|
||
<description><p>倒不是懒了,只是有很多很多的事情要去做,总也做不完,等到想到博客的时候已是该睡觉的时候了。澳大利亚之行还是要继续写的,有部分已经写了草稿,只是还未完工。最近系统升级到<a href="http://www.ubuntu.org.cn">Ubuntu 7.04</a>了,桌面也从中文换成了英文,发一张桌面!</p>

<p><a href="/static/media/2007/06/screenshot.png"><img src="/static/media/2007/06/screenshot.png" alt="我的桌面" /></a></p>
</description>
|
||
<pubDate>Wed, 13 Jun 2007 12:27:17 +0000</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>澳大利亚之行 第一天 悉尼</title>
|
||
<link>http://lunny.info/2007/5/13/澳大利亚之行-第一天-悉尼.html</link>
|
||
<description><p>其实5月8日就从澳大利亚回了,不过因为照片没有到我的手中,所以不能完成一篇图文并茂的文章。那样肯定会是一种遗憾,因此直到今天才写下这次旅行。</p>

<p>首先要讲一讲这次旅行的幸运:原定14人的团,结果最后基本上都是六个人,每次游玩都是6个人加1个领队,1个当地导游,1个司机。相当于VIP团了吧,呵呵。</p>

<p>我们的首站是在悉尼,1号晚从上海浦东上飞机,飞行了一夜。第二天早上九点多,我们快要降落了。此时可以从飞机上看到悉尼,刚开始是一片片绿色的丘陵,慢慢的开始有房子了,慢慢到海边时已变得密密麻麻的了,但是房子都不高,这点跟上海不一样。机场是在海边修建的,降落的时候,飞机要从海上降落到跑道上去,很像航母上飞机的降落过程。</p>

<p>下了飞机,还没去宾馆,就先拉到鱼市场去吃饭。在鱼市场里吃饭,没去之前肯定会觉得不可思议。去了才明白,那里跟国内的鱼市场完全不同。空气里没有丝毫的鱼腥和血腥的味道,在里面吃东西感觉非常好。这里的海鲜看起来也非常干净,让我这个在国内非常怕吃海鲜的人也放开胆子吃了起来。</p>

<p><a href="/static/media/images/100_1669.JPG"><img src="/static/media/images/100_1669.JPG" alt="悉尼鱼市场" /></a></p>

<p>离开鱼市场,我们去了Bandi海滩。到了海滩,下车之前我们都鞋子脱掉留在车上,光着脚跑到海滩上去玩。这个海滩和海南的海滩差不多,不过这里沙子更细,天更蓝,水也蓝,太阳却没有海南的那么晒。并且海边有浪,浪高的时候有1米多,有些老外在冲浪。在这个海滩上时间只有1小时,其次也没有带下水的衣服,所以最终就湿了湿裤脚,照了照像就往回走了。</p>

<p><a href="/static/media/images/100_1672.JPG"><img src="/static/media/images/100_1672.JPG" alt="悉尼bandi海滩" /></a></p>

<p>接着去的地方是海德公园,海德公园大概是这个城市的一个中心,因为我们后面去的几个景点都在这里绕来绕去。海德公园附近是一个很大的教堂,教堂里面很高,估计超过30米,是我见过的最大的教堂。在海德公园的喷池附近照了照相,在这个照相的地方可以看到悉尼塔似乎就在眼前,这个塔是后面的一个景点。</p>

<p><a href="/static/media/images/200705-075.jpg"><img src="/static/media/images/200705-075.jpg" alt="海德公园" /></a></p>

<p>离开公园我们就去吃晚饭,在一个大概可以称为唐人街的地方。我们去的是一个广东人开的馆子,老实讲味道还可以。吃完饭就往宾馆走了。我们被安排住在一个叫<a href="http://www.ibishotel.com">Ibis Hotel</a>的地方,类似于中国的“<a href="http://www.homeinns.com">如家</a>”这样的连锁旅店。房间不大,不过还好,比较干净。</p>

<p>第一天就这样急急忙忙的结束了,一夜的飞机没休息好,所以很快就睡着了。</p>
</description>
|
||
<pubDate>Sun, 13 May 2007 10:35:03 +0000</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>佳能A710 IS数码相机 在 Ubuntu Linux 6.10下 设备访问问题的解决</title>
|
||
<link>http://lunny.info/2007/4/21/佳能A710-IS数码相机-在-Ubuntu-Linux-6.10下-设备访问问题的解决.html</link>
|
||
<description><p>虽然7.04已经发布了,不过我还没来得及更新。买了相机拍照后要将图片导入到电脑中,将相机连接到usb口,此时会弹出框显示检测到了数码相机,在导入照片时却出现如下错误:</p>
<pre><code>io-库 (“无法请求 USB 设备”) 中出现错误:Could not claim interface 0 (Operation not permitted). Make sure no other program or kernel module (such as sdc2xx, stv680, spca50x) is using the device and you have read/write access to the device.
</code></pre>

<p>Google了一下,发现了一个人的博客,是一个在日本的中国程序员的,他的富士通的相机,解决问题的文章地址如下:
<a href="http://my.donews.com/booker/2007/02/10/finepix_f30_camera_on_edgy_ubuntu/">http://my.donews.com/booker/2007/02/10/finepix_f30_camera_on_edgy_ubuntu/</a></p>

<p>于是参考了一下:步骤如下:
1. 连上数码相机,打开终端,在终端输入lsusb,可以查看到所有连接的usb设备:</p>

<p><a href="/static/media/images/2007/04//lsusb.jpg"><img src="/static/media/images/2007/04/lsusb.jpg" alt="lsusb" /></a></p>

<ol>
<li><p>可以看到 Bus 001 Device 008: ID <strong>04a9</strong>:<strong>3138</strong> Canon, Inc
04a9,3138这两个数字是我们需要的,这两个数字根据不同的数码相机会有所不同。</p></li>

<li><p>在终端中输入sudo gedit /etc/udev/rules.d/45-libgphoto2.rules,
在其中加入如下行:</p></li>
</ol>
<pre><code>SYSFS{idVendor}==”04a9”, SYSFS{idProduct}==”3138”, MODE=”0660″, GROUP=”plugdev”
</code></pre>

<p>保存并关闭。</p>

<ol>
<li>然后重启udev:</li>
</ol>
<pre><code class="language-sh">sudo /etc/init.d/udev restart
</code></pre>

<ol>
<li>拔插一下usb线,晕还是不行。又搜索了一下,找到另一篇文章
<a href="http://forum.ubuntu.pl/viewtopic.php?t=23060">http://forum.ubuntu.pl/viewtopic.php?t=23060</a></li>
</ol>

<p>居然是pl结尾的文章,XX文,不认识,不过命令应该都是英文的吧。在终端中输入</p>
<pre><code class="language-sh">sudo gedit /etc/udev/rules.d/40-permissions.rules
</code></pre>

<p>修改这一行</p>
<pre><code>SUBSYSTEM==&quot;usb_device&quot;, MODE=&quot;0644&quot;
</code></pre>

<p>为</p>
<pre><code>SUBSYSTEM==&quot;usb_device&quot;, MODE=&quot;0666&quot;
</code></pre>

<p>OK!这下终于可以了。</p>
</description>
|
||
<pubDate>Sat, 21 Apr 2007 14:45:58 +0000</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>买了一个新的数码相机 佳能A710IS</title>
|
||
<link>http://lunny.info/2007/4/21/买了一个新的数码相机-佳能A710IS.html</link>
|
||
<description><p>性能参数:710w象素,6倍光学变焦,1/2.5英寸传感器,2.5英寸液晶屏,光学防抖。</p>

<p><a href="/static/media/images/2007/04/a710is.jpg"><img src="/static/media/images/2007/04/a710is.jpg" alt="A710IS" /></a></p>

<p>用它拍了几张照片,放一张上来瞧瞧!</p>

<p><a href="/static/media/images/2007/04/00001.jpg"><img src="/static/media/images/2007/04/00001.jpg" alt="A710 IS拍的照片" /></a></p>
</description>
|
||
<pubDate>Sat, 21 Apr 2007 14:22:49 +0000</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>Ubuntu 7.04发布了</title>
|
||
<link>http://lunny.info/2007/4/20/Ubuntu-7.04发布了.html</link>
|
||
<description><p>官方下载地址:</p>

<p><a href="http://releases.ubuntu.com/7.04/">http://releases.ubuntu.com/7.04/</a></p>

<p>迅雷下载:</p>

<p><a href="http://61.129.76.84/dload1.html?cid=E871B22B075BF865DD0BB89B3E013CD6F1EBE189">http://61.129.76.84/dload1.html?cid=E871B22B075BF865DD0BB89B3E013CD6F1EBE189</a></p>
</description>
|
||
<pubDate>Fri, 20 Apr 2007 05:08:52 +0000</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>为五一去澳大利亚旅游的准备基本完成</title>
|
||
<link>http://lunny.info/2007/4/19/为五一去澳大利亚旅游的准备基本完成.html</link>
|
||
<description><p>说麻烦也不麻烦,说简单也不简单。说说过程。</p>

<p>1.首先需要护照。到出入境管理处办理,要照相(收费)和交钱(200RMB),可以选择将办好的护照EMS到指定地点(收费20多RMB),只能发本省内的。</p>

<p>2.然后是签证,一般跟团走,旅游团会给你一张列表,把需要准备的东东办好给它,大概如下:</p>

<p>1)护照(最后一页要签上大名)</p>

<p>2)银行存款证明(去银行办,5w以上的定期,三个月证明,三个月内不能取这些钱)</p>

<p>3)身份证复印件/户口簿复印件(全本)</p>

<p>4)中英文公司 的担保信(网上有格式样本。如果找不到,随便找个旅行社,让他发一个给你,呵呵),需要公司领导签字,盖章。</p>

<p>5)要填一张个人资料表(获得途径同上)</p>

<p>3.说说价格,四月三十号的1.4w左右,五一的1.7w左右。选好了团提前订好,交一下订金。</p>

<p>4.后面就是要等着了,准备去澳大利亚的东西了。</p>

<p>5.据说要给当地导游小费,一般给48$,得去银行换外汇,换外汇需要身份证。</p>

<p>6.当地RMB不通用,所以带现金最好换成澳元,不过据说刷卡比较方便,如果有双币的卡不用带多少现金了。</p>
</description>
|
||
<pubDate>Thu, 19 Apr 2007 14:48:45 +0000</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>绝好的歌曲,每个人肯定都曾经听得耳朵生茧</title>
|
||
<link>http://lunny.info/2007/4/15/绝好的歌曲,每个人肯定都曾经听得耳朵生茧.html</link>
|
||
<description><p>听听就知道了。今天偶然从一个朋友的Blog中看到的!<a href="/static/media/2007/04/eye.wma">eye.wma</a></p>
</description>
|
||
<pubDate>Sun, 15 Apr 2007 14:44:15 +0000</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>好用的上网小工具:Google笔记本</title>
|
||
<link>http://lunny.info/2007/4/14/好用的上网小工具:Google笔记本.html</link>
|
||
<description><p>首先声明,我跟Google没有任何关系,只是觉得这个工具对我的用处还可以,所以介绍介绍!</p>

<p>当你上网之时,是否经常想把某些东西记下来,以备不时之需?这种情况估计每个人都会碰到,那么来看看这个过程是如何完成的。</p>

<p>先在浏览器中复制需要的内容,然后在桌面创建一个xx.txt文件,打开,粘贴,当下次需要的时候再打开,如果需要拷贝到别的地方,那么使用U盘拷贝到U盘中。如果需要到没有电脑和网的地方,就需要记到纸上。</p>

<p>Google笔记本给了另一个选择。首先我们需要安装Google笔记本,在浏览器中输入网址<a href="http://www.google.com/notebook">http://www.google.com/notebook</a>,这时会要求你输入Google帐号和密码,如果你没有可以申请一个。</p>

<p>登录之后,你就可以下载浏览器插件了,目前支持IE和firefox,安装好插件重启浏览器之后,它会在浏览器状态栏的最右有一个图标和文字。</p>

<p><img src="http://photo.yo2.cn/photos/7/604460px.jpg" alt="image" /></p>

<p>接下来,我们就可以使用这个工具了。在浏览器复制内容后,点击浏览器右下脚的“打开笔记本”,就会显示“我的笔记本”,我们可以将内容粘贴到其中。到了另外一个地点,我们可以登录google去获得,免了U盘的烦恼,也免了电脑数据丢失造成的麻烦。</p>

<p><img src="http://photo.yo2.cn/photos/7/605460px.jpg" alt="image" /></p>
</description>
|
||
<pubDate>Sat, 14 Apr 2007 07:01:45 +0000</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>Linux下的两款软件Picasa和Skype</title>
|
||
<link>http://lunny.info/2007/4/4/Linux下的两款软件Picasa和Skype.html</link>
|
||
<description><p>我后悔了,因为这是我第二次写这篇文档。因为刚才写完后发表居然失败了。我决定要寻找一款离线博客编写软件了。</p>

<p>没兴致了,简要讲讲这两个软件吧。</p>

<h2 id="picasa">picasa</h2>

<p>google的东东,原先是在windows下的,现在也出了linux版本,我用的Ubuntu 6.10,在cn99的源里面有。也可以到<a href="http://picasa.google.com/linux下载。刚才在我的机器上两次无响应,看来还在测试阶段。其实gnome也有一款照片管理软件F-Spot,跟picasa比它少了一个上传到网络相册的功能。">http://picasa.google.com/linux下载。刚才在我的机器上两次无响应,看来还在测试阶段。其实gnome也有一款照片管理软件F-Spot,跟picasa比它少了一个上传到网络相册的功能。</a></p>

<p><img src="/static/media/images/506.jpg" alt="picasa for linux" title="picasa for linux" /></p>

<h2 id="skype">skype</h2>

<p>官方网站好像没有.deb的包提供下载。Ubuntu 6.10,cn99的源里也有,不过版本较低一点。 在ubuntu下用的过程中断了两次,都是只能一方听到另一方声音。不知道是线路原因还是skype的问题。</p>

<p><img src="/static/media/images/507.jpg" alt="skype for linux" title="skype for linux" /></p>

<p>总之,这两个软件基本还可以用。</p>
</description>
|
||
<pubDate>Wed, 04 Apr 2007 14:51:09 +0000</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>启用了新的域名lunny.org</title>
|
||
<link>http://lunny.info/2007/4/3/启用了新的域名lunny.org.html</link>
|
||
<description><p>本来想申请lunny.net,可惜被人注册去了。将blog.lunny.org转发到现在这个blog的首页,设置了隐藏真实url,不过居然还要递交书面申请才能有效。在<a href="http://www.xinnet.com">新网</a>申请的域名,120RMB/年,不知道别的域名提供商如何。另外申请了免费的论坛,地址<a href="http://lunnyxiao.uu1001.com">lunnyxiao.uu1001.com</a>,是<a href="http://www.phpwind.com">phpwind</a>的,不过还没有链接到这里来。</p>
</description>
|
||
<pubDate>Tue, 03 Apr 2007 14:36:00 +0000</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>终于回到上海了!</title>
|
||
<link>http://lunny.info/2007/4/1/终于回到上海了!.html</link>
|
||
<description><p>历时两周的海南之行终于结束了。虽然海南风光不错,温泉,沙滩,海水,还有晒人的太阳。不过金窝银窝,始终不如自己的家舒服啊。从海南上飞机的时候是37度,到上海下飞机的时候却在下雨,温度据说在15度,真是天壤之别。到家了,一榻糊涂,先开窗通通风。然后是整理东西,买的东西不多,两个椰壳做的饰品,送给老婆的妹妹,两罐黄辣椒,还有两盒珍珠粉,还有在海边买的一些贝壳之类的东西。可惜的是这次没带相机过去,所以没有拍照。明天该开始上班了,今晚要早点睡。</p>
</description>
|
||
<pubDate>Sun, 01 Apr 2007 11:45:07 +0000</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>添加显示浏览者天气栏</title>
|
||
<link>http://lunny.info/2007/3/31/添加显示浏览者天气栏.html</link>
|
||
<description><p>本来没有主动寻找这方面代码的打算,在浏览某个网站时发现了这段代码,所以就拷贝过来了.代码如下:</p>
<pre><code class="language-html">&lt;iframe xsrc=&quot;http://weather.265.com/weather.htm&quot; mce_src=&quot;http://weather.265.com/weather.htm&quot; width=&quot;168&quot; height=&quot;50&quot; frameborder=&quot;0&quot; marginwidth=&quot;0&quot; marginheight=&quot;0&quot; scrolling=&quot;no&quot; name=&quot;265&quot;&gt;&lt;/iframe&gt;
</code></pre>
</description>
|
||
<pubDate>Sat, 31 Mar 2007 15:13:07 +0000</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>打算在这里扎根了</title>
|
||
<link>http://lunny.info/2007/3/30/打算在这里扎根了.html</link>
|
||
<description><p>在海口的这个下午,没有工作,只好上网。在msn上碰到qyt,又去重访了他的博客<a href="http://www.dup2.org/blog">BT的花</a>。碰巧最近也想写写博客,但是对msn博客的访问速度及其不满意,因此本打算自己租一个空间,架设一个博客。经过他的介绍,找到了<a href="http://lunny.yo2.cn/www.yo2.cn">www.yo2.cn</a>,注册试用之后感觉比较满意。再加上它使用的是WordPress,一旦这个站点有什么问题,我可以很容易的将内容导入到另一个WordPress中。Good!我会把这里当根据地,经常来这里浇浇水的!</p>
</description>
|
||
<pubDate>Fri, 30 Mar 2007 10:21:36 +0000</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>自己动手编写c++事件模型</title>
|
||
<link>http://lunny.info/2006/7/30/自己动手编写c++事件模型.html</link>
|
||
<description><p>在Java、C#等语言或者说其类库中,都实现了事件模型。而c++语言本身并没有定义事件机制,并且在目前众多优秀的c++类库,包括STL、Boost等都没有实现类似的事件机制。当我们被MFC的消息搞得头昏眼花之时,是否有冲动自己去实现一个简单的事件模型呢。我想,有着相同想法的人肯定很多,而真正动手来写可能会碰到各种各样的困难。下面就让我们一步步来编写一个简单的事件模型。</p>

<h3 id="了解事件模型的机制">了解事件模型的机制</h3>

<p>在开始之前,我们有必要简单的了解一下事件模型的机制。事实上,事件模型的机制不止一种,</p>

<p>Java和c#的事件机制就不太一样,不过事件模型的基础都是一样,那就是一般都使用Observer设计模式。关于Observer设计模式,希望详细了解的朋友可以参考《》书,在这里我们就不详细的介绍,只是参照c#的事件机制来实现。</p>

<p>在c#中,我们可以以event关键字声明一个事件,然后我们可以将一个函数委托挂接在这个事件上,当事件被调用时,则所有挂接的函数也会被调用。这里有一个比较难以理解的词大概是委托,其实也不难理解。委托其实就是一种函数包装类,这种类可以把任何函数的指针保存起来,等到需要调用的时候再调用,并且这种类的实例必须能够挂接到事件之中。</p>

<p>看了上面这段话,是不是觉得其实事件模型也并不复杂。还想对事件模型了解得更多?我这里就不继续了,感兴趣的朋友可以查找一下相关的资料。下面我们就开始在c++中编写一个简单的事件模型。</p>

<h3 id="设计一个简单的c-事件模型">设计一个简单的c++事件模型</h3>

<p>因为一个事件模型其实就是一个典型的Observer设计模式,因此最重要的就是</p>

<p>Subject(目标类)和Observer(观察者类)的设计。</p>

<p>首先,我们需要一个事件类,它其实是一个抽象的Subject。它是一个基类,所有的其他事件都从它继承,并且用户只需要从这个基类继承就可以自定义事件。我们不仿将这个类定义为CEvent。</p>

<p>CEvent的声明如下:</p>
<pre><code class="language-c++">class CEvent
{
public:

typedef list&lt;CEventHandler&gt; data_type;

CEvent();

virtual ~CEvent();

void operator()()
{
 data_type::iterator it;

 for (it = m_observer.begin(); it != m_observer.end(); ++it)
 {
 (*it)(*this);
 }
}

CEvent&amp; operator+= (const CEventHandler&amp; handler)
{
 Register(handler);
 return *this;
}

CEvent&amp; operator-= (const CEventHandler&amp; handler)
{
 UnRegister(handler);
 return *this;
}

void Register(const CEventHandler&amp; handler)
{
 m_observer.push_back(handler);
}

void UnRegister(const CEventHandler&amp; handler)
{
 m_observer.remove(handler);
}

protected:
 data_type m_observer;
};
</code></pre>

<p>其次,这个事件可以挂接任意的函数,也就是它可以保存所有的挂接函数。在c++中,我们大致有三种类型的函数,全局或者静态函数、成员函数、仿函数。要在CEvent中实现一个或者多个方法来执行挂接的任务不太现实。我们参考c#的实现方法,可以先实现一个委托类,将各种函数保存起来。然后再使用CEvent对这个委托类进行挂接。我们不仿称这个类为CEventHandler。</p>

<p>然后,我们的这个委托类需要能够保存所有的函数。在c++里,我们能够很容易的得到函数的指针,只需要在函数名前加&amp;,但是各种函数,特别是成员函数它是没有类型的,这就要有一种机制,将所有的函数转换为同一种类型。因为函数是没有类型,也相当于每一个函数都是一种类型,解决这个问题的方法其实就是使用模板类。因此,我们先定义一个虚基类CFunImpl。对三种不同的函数,我们分别从CfunImpl继承三种不同的子类,CstaticFun、CmemFun,CFunctor。</p>

<p>CstaticFun表示全局的或者静态的函数,不过这必须是一个模板类,对于每一个静态或者全局函数,都是模板类的一个特化。类声明的代码如下:</p>
<pre><code class="language-c++">template&lt;typename Fun&gt;
class CStaticFun :

public

CFunImpl {
public:
CStaticFun(const Fun&amp; fun) :
m_fun(fun)
{};

virtual ~CStaticFun()
{
}

protected:
 Fun m_fun;
};
</code></pre>

<p>对于<code>CmemFun</code>类,我们如法炮制,不过我们这时需要保存两个成员变量,一个是成员函数的指针,另一个则是该成员函数所属的对象的指针。类声明的代码如下:</p>
<pre><code class="language-c++">template &lt;typename PointerToObj, typename PointerToMemFun&gt;
class CMemFun : public CFunImpl
{
public:

CMemFun(const PointerToObj&amp; pObj, PointerToMemFun pMemFn)
:m_pObj(pObj),
m_pMemFun(pMemFn)
{
}

virtual ~CMemFun()
{
}

protected:

PointerToObj m_pObj;

PointerToMemFun m_pMemFun;
};
</code></pre>

<p>对于仿函数,我们这里先不讲,因为仿函数使用得很少,并且这还涉及到仿函数的概念,有兴趣的朋友可以在阅读完本文之后自己来实现。</p>

<p>好,到现在为止,我们可以将所有的函数使用<code>CfunImpl</code>来表示了。因此,我们可以在委托类<code>CEventHandler</code>中保存<code>CfunImpl*</code>来达到我们的目的。<code>CEventHandler</code>的声明如下:</p>
<pre><code class="language-c++">class CEventHandler
{
public:

virtual ~CEventHandler()
{
Clear();
}

template&lt;class Fun&gt;
CEventHandler(const Fun&amp; fun)
:m_pImpl(
new CStaticFun&lt;Fun&gt;(fun))
{}

CEventHandler(const CEventHandler&amp; fun)
:m_pImpl(NULL)
{
*this = fun;
}

void Clear()
{
 if (m_pImpl)
 {
 delete m_pImpl;
 m_pImpl = NULL;
 }
}

CEventHandler&amp; operator= (const CEventHandler&amp; fun)
{
 Clear();

 if (fun.m_pImpl)
 {
 m_pImpl = fun.m_pImpl

 -&amp;gt;

 Clone();
 }

 return *this;
}

//成员函数
template &lt;typename PointerToObj, typename PointerToMemFun&gt;
CEventHandler(const PointerToObj&amp; pObj, const PointerToMemFun&amp; pMemFun)
:m_pImpl(new CMemFun&lt;PointerToObj, PointerToMemFun&gt;(pObj, pMemFun))
{
}

void operator()(CEvent&amp; e)
{
 if (m_pImpl)
 {
 (*m_pImpl)(e);
 }
}

bool operator== (const CEventHandler&amp; handler)
{
 if (m_pImpl == NULL || handler.m_pImpl == NULL)
 {
 return true;
 }

 if (typeid(m_pImpl) == typeid(handler.m_pImpl))
 {
 return (*m_pImpl) == (*(handler.m_pImpl));
 }

 return false;
}

protected:

CFunImpl *m_pImpl;

};
</code></pre>

<p>不过要实现事件机制,我们还需要<code>CfunImpl*</code>的子类必须实现几个方法。第一个方法就是调用函数的方法,我们这里要求重载</p>
<pre><code class="language-c++">void operator()(CEvent&amp;amp; e);
</code></pre>

<p>来达到目的。</p>

<p>第二个方法就是<code>operator==</code>,为什么需要这个方法呢。因为对于<code>CEvent</code>来说,调用<code>UnRegister()</code>时,我们必须找到我们使用<code>Register()</code>添加到<code>list</code>中的函数,这时我们需要判断两个<code>CEventHandler</code>对象是否相等,这是件麻烦事情。<code>CEventHandler</code>保存的是<code>CFunImpl</code>的指针,好吧,这个我们就要求<code>CfunImpl</code>的子类必须实现重载<code>operator==</code>。</p>

<p>第三个方法,从<code>CfunImpl</code>继承必须实现<code>Clone()</code>方法。<code>Clone()</code>方法可以让<code>CEventHandler</code>实现拷贝。具体的实现方法我们可以查看源码。</p>

<h3 id="使用事件模型">使用事件模型</h3>

<p>一个简单的事件模型基本完成了。现在我们可以编写一个例子来演示一下如何来使用这个事件模型。其实使用起来很简单。</p>

<p>我们可以声明一个<code>CEvent</code>或者<code>CEvent</code>子类的实例。让需要监听的函数挂接到这个<code>CEvent</code>中,我们实现了<code>Register()</code>函数和<code>+=</code>操作符,都可以使用。挂接的语句就像这样:</p>
<pre><code class="language-c++">CEvent evt;

CobserverTest obj;

evt +=CEventHandler(&amp;obj

, &amp;amp;(

CObserverTest::OnStartEvent

));
</code></pre>

<p>当事件的<code>operator()</code>方法调用时,就会自动调用我们的监听函数。具体的例子可以查看提供的源代码。</p>

<h3 id="模型的缺陷和改进">模型的缺陷和改进</h3>

<p>到现在为止,我们终于实现了一个简单的事件模型。它可以有效的工作,并且与平台无关。当然,要求你的编译器尽量的支持<code>c++</code>标准。如果你的编译器对<code>c++98</code>标准支持不够,也许它不能够工作正常。笔者在<code>VC6SP6</code>和<code>VC2005</code>中测试均通过。</p>

<p>不过这个模型还有可以改进的余地。</p>

<ul>
<li><p>目前只是支持同步操作,即当你的事件进行调用时是在同一线程里顺序进行。要改进到支持异步调用就必须在每次调用时在一个新的线程中进行调用。因此我们需要一个线程机制来完成这个工作。如果在一个特定的平台中使用,如<code>VC</code>中,我们可以在响应函数中创建一个新的线程。</p></li>

<li><p>目前的所有的函数参数必须为<code>(CEvent&amp; e)</code>,不能写成<code>(CchildEvent&amp; e)</code>。要对这个限制进行改进,就需要编译器支持虚模板成员函数的机制。不过在<code>VC6</code>和<code>VC2005</code>中,均不支持这种特性。因此要么寻求它法,要么就等到编译器的支持吧。</p></li>

<li><p>线程安全,这个简单的事件模型使用到了<code>STL</code>中的<code>list</code>组件,但是并非所有的list实现都是线程安全的。建议大家采用stlport,因为它的实现是线程安全的,如果没有采用线程安全的实现,那么在调用<code>Register</code>和<code>UnRegister</code>时最好能够实现互斥。</p></li>
</ul>

<p>限于笔者知识水平和文笔有限,不当之处肯定很多。若你有好的想法和建议,可以email告诉我。</p>
</description>
|
||
<pubDate>Sun, 30 Jul 2006 06:38:00 +0000</pubDate>
|
||
</item>
|
||
<item>
|
||
<title>关于函数指针和回调</title>
|
||
<link>http://lunny.info/2004/10/3/关于函数指针和回调.html</link>
|
||
<description><p>最近需要用到这个函数指针的方法,搜索了一下,发现这篇文章 <a href="http://vckbase.com/document/viewdoc/?id=195">http://vckbase.com/document/viewdoc/?id=195</a> ,自己试着写了一个例子程序,只是没有用到CALLBACK关键字,这样应该是可以的。不过还是没有弄明白函数指针和回调的关系,不明白为什么MFC中的回调函数前用CALLBACK来声明。
下面说一下我的步骤:</p>

<p>1.先声明一个函数指针类型</p>
<pre><code class="language-c++">typedef int (*TESTCALLBACK)(BOOL);
</code></pre>

<p>2.然后声明并实现被调用函数</p>
<pre><code class="language-c++">int CallbackTest1(BOOL bIs);
int CallbackTest1(BOOL bIs);
{
 return bIs;
}
</code></pre>

<p>3.声明调用函数</p>
<pre><code class="language-c++">int test(TESTCALLBACK lpTest);
</code></pre>

<p>4.在调用函数的实现中,直接使用函数指针即可</p>
<pre><code class="language-c++">lpTest(TRUE);
</code></pre>
</description>
|
||
<pubDate>Sun, 03 Oct 2004 04:59:00 +0000</pubDate>
|
||
</item>
|
||
</channel>
|
||
</rss> |