很久很久以前

  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  34 随笔 :: 4 文章 :: 17 评论 :: 0 Trackbacks

2006年8月31日 #

Winsock 10053错误分析

 WSAGetLastError可能会返回10053错误,查msdn的解释是:

WSAECONNABORTED 10053

Software caused connection abort.

An established connection was aborted by the software in your host computer, possibly due to a data transmission time-out or protocol error.

神马?软件原因造成的连接中断,这是什么意思,不跟没说一样的么?
google一下呗

Berkeley description:

A connection abort was caused internal to your host machine. The software caused

a connection abort because there is no space on the socket’s queue and the socket

 cannot receive further connections.

       

WinSock description:

Partly the same as Berkeley. The error can occur when the local network system aborts

a connection. This would occur if WinSock aborts an established connection after data

retransmission fails  (receiver never acknowledges data sent on a datastream socket).

       

TCP/IP scenario:

A connection will timeout if the local system doesn’t receive an (ACK)nowledgement for

data sent.  It would also timeout if a (FIN)ish TCP packet is not ACK’d

(and even if the FIN is ACK’d, it will eventually timeout if a FIN is not returned).

 

伯克利说这种连接中断是因为宿主机器的内部原因,因为软件导致的连接中断,可能是因为socket的队列满并且这个socket不能接收更多的连接了。
这还不如不说,越说越糊涂了。
winsocket的描述,似乎还靠谱一些,这种错误一般发生在一个建立的连接被重发失败的情况下产生,接收方没有响应数据发回来。但还是比较模糊。
再看看tcp ip标准文档的说法,如果本地系统没有收到发送数据的响应(ack)那么这连接就会超时。如果tcp的fin包没有被ack(或者fin包被ack了但fin没有返回)那么也会超时。但是,但是,超时跟这个10053有神马关系?
再看后续的解释:
从参考1中找到如下的描述:

The Scenario: 
An HTTP POST is to be sent to an HTTP server.
The server begins reading the POST and notices that the HTTP request header is invalid.
It immediately sends an HTTP response (with an error status, perhaps status=400) and closes the connection without trying to continue reading the remainder of the HTTP request that is forthcoming.

Meanwhile, the client is still happily writing the remainder of the HTTP request to the socket. (Remember a TCP/IP socket connection needs to be closed from both sides. In this case, the server has closed its side, but the client is still pumping data into the half-open connection.)
The client finishes writing the HTTP POST to the socket — meaning that data has been buffered to Winsock. The client application then tries to read the HTTP response, but it cannot because the outgoing retransmission (of the buffered data by WinSock) failed and the socket connection was shutdown on the client side (by Winsock). Even though the HTTP server sent the response, it is lost and cannot be retrieved. The error your application will receive when
trying to read the HTTP response on the socket is WSAECONNABORTED. The word "software" in any of the above error messages refers to "WinSock".

Go back and re-read the original error explanations. Hopefully, after that explanation, you’ll say "Aha! I understand what they’re talking about!".

 

啊哈,又有http了,大概意思就是http server收到请求了,但发现有问题,那么回一个http错误码,然后就关闭了socket,但与此同时,client端还在很开心地向socket写数据,注意哦,tcp是全双工的。client写完毕后,实际上数据只是放到了发送方的缓冲区中,不一定已经发出去了,如果写得不好的程序,这个时候就开始从socket读数据了,这时候就会产生一个WSACONNECTABORTED错误,windows上对应的就是10053错误。

但这个解释实际上是不能让人满意的,只是举出了一种场景,但为什么会产生还没有解释。后面又搜到了个参考2,首先解释10053错误是收到fin后client会放弃发送缓冲区中的数据,同时上报错误。虽然说法还有点一头雾水。

不过这两个参考给我们一个思路,重现这个问题。

于是简单写个测试用的c-s程序,大概流程如下

 

 

图1 CS程序简化流程图

这个简单程序演示如何出现10053错误(以及10054错误)。

如果server在收到client发送的数据后立即关闭socket,那么client再读时,会收到10053错误;如果server收到发送数据后,立即crash,那么随后client再读取时会收到10054错误。

ok,能够重现场景了,那么我们来分析一下更细节的方面,网络问题自然是抓包,本问题处理抓包还要看一下tcp的状态以便辅助分析,我们在client端每次操作之前都打印当前的tcp状态。

下面是client端发送记录和对应的netstat情况

图2 10053错误client端tcp状态流转

client在发送之前tcp状态是established,在发送之后,server会立即关闭,tcp状态也变为close_wait,但这只是单方向的关闭,client可以继续发数据,但client发送后,server立即退出了,导致后续recv会失败并且返回10053。对应抓包情况如下:



图3 10053错误client端tcp抓包

整个通信过程如下:
1-3.三次握手建立连接
4.客户端(10.10.86.93)向服务器端(10.10.86.98)发送数据,1字节
5.server 中止 发送fin(同时ack之前那个push)
6.client ack 那个fin
7.client再发送两个字节
8.server此时已经关闭socket,属于非正常情况,回复复位命令

整个过程可以重现10053情况,tcp发送分组数据的情况也一目了然,事情到此就可以了么?显然不是,你也看到了后面还有很多文字,不知此时你心中的问题是否跟我一样,先说我自己的吧,通过抓包发现这里的异常关闭有个reset,但reset一般是10054(Connection reset by peer)的错误,那么10053与10054的区别在哪里。要搞清楚问题也不难,重现场景抓包分析。
以下是修改上面的cs程序,在client发送的1字节包后,立即crash,这导致的问题是操作系统会立即回收所有资源,包括socket资源。




图4 10054错误client端tcp状态流转

可以看到在crash之前这个tcp都是established状态。crash之后,client端接收数据时会收到10054错误,场景重现了,我们再看一下抓包情况



图5 10054错误client端tcp抓包

这个抓包情况跟10053很像,1-7也同10053,在8时,client收到server发过来的reset,表示当前连接被强制复位了。
对比10053和10054可以发现,如果srv返回fin标志后再reset那么对应的错误就是10053,如果直接reset就是10054错误。回过头来在看参考2中的说法也就有点感觉了。

总结一下:
1.遇到不了解的问题,google是非常好的方法
2.对于一般问题,重现之很重要,可以反复发现问题并验证问题。自己写程序或者搭环境尽量重现。
3.网络问题抓包是利器,包括各种工具的使用netstat wireshark ping traceroute等。
4.多重问题对比其中的差异,这里对比10053错误和10054错误。
5.理论基础要搭好,本次问题主要是tcp的异常断开问题,熟悉tcp断开的半关闭和复位逻辑,不过理论还是理论,同样是复位在不同场景下的错误码不同。并且实现上也跟具体的操作系统相关。
6.实际工作中,
10053错误时,用户主要是处于透明代理情况,那么这一般是又有用户所在的代理服务器异常关闭导致的,可能跟我们的离线文件私有协议被用户所在的代理服务器拒绝掉导致的。

7.回过头来在看一开始的解释,所谓软件原因造成的连接终端,就是本例子中,server端在shoutdown本方向传输时,立即关闭了socket,导致本应该等待对方发送fin来完全结束的正常逻辑被打破,编程单方向强制中止本次tcp,导致client端之后向上报错,就是所谓的10053错误了,这里的软件就是server端的那个程序。(不过也有种说法是,客户端发送错误数据,导致server端保护机制而强制关闭)


参考:

  1. http://www.chilkatsoft.com/p/p_299.asp   
  2. http://bbs.csdn.net/topics/360024280#post-361829232
  3. 《TCP/IP详解(卷一)》18章 TCP连接的建立和中止

 

 

posted @ 2013-11-28 11:22 Long Long Ago 阅读(29079) | 评论 (1)编辑 收藏

最近改造文件传输,参考libcurl,考虑到他支持那么多协议,但我只关心http的,所以考虑是否可以只生成http支持的版本,查了一下,果然可以。
下载,如果不需要最新的,那么只要下载个zip包就好了。
通过download wizard可以指引你下载不同的版本:http://curl.haxx.se/dlwiz/
选择
source code - 平台无关- 找到最新版本下载,解压缩
编译比较简单有build指令
不过我从vs目录下找到一个2005版本的vcproj文件,用这个也可以编译,不过要设置一下include目录为../../../include
 上面是废话了,关键的怎么值生成对http的支持呢
只要在编译指令中增加定义HTTP_ONLY宏就可以了,就这么简单。
详细说明在这里http://curl.haxx.se/docs/install.html
posted @ 2013-11-21 20:40 Long Long Ago 阅读(682) | 评论 (0)编辑 收藏

路由器软件部分的几个概念:CFE、固件(Firmware)、NVRAM
CFE的作用跟PC的BIOS一样是负责引导操作系统的;固件就是路由器的操作系统,就像PC上的Windows一样;NVRAM则用于存储路由器的设置,相当于PC的CMOS。
当路由器插上电后自动进入CFE,CFE进行类似PC的BIOS那样进行自检,自检通过后就引导路由器的固件了(相当于的PC的Windows了),正常情况下最多一分钟路由器的固件就会引导完毕,并且Power灯是常亮的。如果Power灯一闪一闪,那一般是CFE没有引导成功路由器的固件(固件不存在、不正确、已损坏等等)。
那么我们平常刷固件会把CFE也刷了吗?呵呵,不会的,就像你平时装Windows一样,不管你是装XP也好,Vista也好,Win7也好,都不会对你的BIOS进行更新。那我想刷CFE该用什么方法?一般用户用路由器原厂的CFE即可,如果要玩的深入一些想刷CFE,那么可以通过JTAG或者telnet方式来刷(比较危险,而且受固件限制,有些固件不支持)。
主要是对硬件环境进行初始化,image的更新,加载kernel等
posted @ 2013-11-13 20:34 Long Long Ago 阅读(238) | 评论 (0)编辑 收藏

好久没有更新这个blog了,java也放下了许久。现在开始重新更新本blog
posted @ 2010-10-30 13:44 Long Long Ago 阅读(224) | 评论 (0)编辑 收藏

本文主要介绍了JXTA中的各种概念。
1.Peer。一个peer就是实现了一个或多个JXTA协议的网络设备。比如传感器,电话, PDA,PC,服务器,巨型机等等。每一个peer与其他peer都是独立操作并且是异步的。有peer ID来唯一标识一个peer。peer使用JXTA协议公开一个或多个网络接口(network interface),每一个公开的接口都被广告为一个peer端点(peer endpoint),这个peer端点唯一标识了一个网络接口。peer之间不需要有直接的点对点的网络连接。可以使用中间的peer作为peer的消息路由,将由于硬件网络或者网络配置(NATs,防火墙或者代理)而造成的两个通信peer进行互联。peer通常被设计成网络中自然地互相发现,从而构成暂时的或持久的关系成为peer组(peer gorup)。
2.Peer Group.一个peer组是一个peer的集合,这些peer都有一组相同的服务。peer自组织的加入到peer组中,并通过一个唯一的peer组id来区别这些peer组。每一个peer组都可以建立一个属于自己的memebership policy,从任何人可以加入到最严格的安全验证以及受保护方式(需要完全的认证书来加入)。一个peer可以同时属于多于一个peer组。默认地,第一个peer组被实例的组是Net Peer Group。所有的peer都属于Net Peer Group。peer可以选择加入其他的peer group。JXTA协议描述了peer如何公开,发现,加入以及监控peer组。
     如下几点说明了创建peer组的目的。
  1. 建立安全的环境。peer组可以创建一个本地控制域,在这个域中使用一个特定的安全策略。这个安全策略可以简单的只是一个明文的帐号 /口令交换,也可以像PKI一样成熟。peer组界定认证的成员访问和公开受保护的内容(content)。peer组在建立了一个逻辑上的区域,从而对访问peer组的资源进行界定。
  2. 创建一个范围环境。peer组裕兴建立一个本地的专用域。比如,peer可以组织起来实现一个文档共享网络或者一个CPU共享网络。 peer组提供细分网络成抽象的区域来提供内在范围机制(implicit scoping mechanism)。比如,当搜索一个组内容的时,peer组可以界定定义一个搜索范围。
  3. 创建一个监视环境。peer组允许一个peer为了任何目的去监视一组peer(比如,心跳,traffic introspection或者accountability)。peer组也可以构建一个父子层次结构,其中任何一个组都有一个唯一的父亲。查询请求能偶在这个组中传播。对于这个组中的广告也可以在其父组中公开,当然也包裹这个组本身。
peer组提供了一组服务成为peer组服务。在JXTA中敌营了一个核心peer组服务集合。两个peer如果要通过一个服务进行交互,他们配需位于同一个peer中。
    核心的组服务有如下:
  1. 发现服务(Discovery Service)。本服务被组中的peer用来查询per组资源,比如peer,peer组,通道和服务等。
  2. 成员关系服务(Membership Service)。本服务被当前成员用来拒绝或接受一个新的组成员应用。一个peer想要加入到一个group前,首先要确定一个当前的成员,并请求加入。当前成员的集合可以拒绝或者接受某个想加入的应用(application)。本服务可能会发起一个所有peer或者指定组代表的一次投票来决定是否接受或者聚居新的成员应用。
  3. 访问服务(Access Service)。使用访问服务可以用来验证一个peer对另一个peer的请求(request)。如果访问被允许,那么接受请求的peer会提供给请求peer关于该请求所要知道的信息的信任和相关信息。(注意,在peer组中,不是所有的行为(action)都需要通过访问服务的检查,只有那些在某些peer中被限制的行为在需要调用时,才被检查。)
  4. 管道服务(Pipe Service)。本服务用于在peer组成员间的链接管道的建立和管理。
  5. Resolver服务。本服务用来发送一般的查询请求到其他的peer。peer可以定义和交换请求以便发现任何需要的信息(比如一个服务的状态或者一个管道端点的信息。)
  6. 监视服务(Monitoring Service)。本服务用来让一个peer监视本组中的其他成员服务。
    不是所有上面提到的服务都必须要被每个peer实现。一个peer组可以自由的实现那些它认为有用的服务,并可以依赖于默认的Net Peer Group来提供非关键核心服务的一般实现。
3.Network Services。peer之间可以协作并通信以发布,发现和启动网络服务。peer可以发布多个服务。peer通过Peer Discovery Protocol来发现network service。在JXTA协议中组织了两个层次上的network service。
  • Peer Service。一个peer service只有当peer公布了自己的service时才能被访问。如果这个peer失败了,那么它的service也失败了。不同的peer可以运行某个服务的多个实例,但每个实例都必须公开自己的广告(advertisement).
  • Peer Group Service。peer组服务是这个组中成员所运行服务的实例的集合的组织形式,其中这些实例是互相合作的。如果其中任何一个peer失败了,那么这个 peer组服务将不会收到影响(假定这个服务可以从其他peer获得)。peer group服务通过peer 组广告的以部分来发布。
    服务可以是事先被安装到peer上或者通过网络安装的。为了真正的运行一个服务,peer必须为定位一个适合当前peer运行环境的一个实现。这个从网络上查找,下载和安装一个服务的过程很类似于在Internet上搜索一个Web页面,取回这个页面然后再安装需要的插件的过程。
4.Modules。JXTA的module用来描述任何一段用来实现JXTA world中一个行为的“代码”的抽象表示(Abstraction)。Network Service就是在一个peer上实现的行为的一个最一般的例子。这个Module Abstraction不一定特指什么“代码”,它可以是一个Java类,也可以是Java jar,或者动态链接库dll,一个XML消息或是一个脚本。这个module的行为交给了module的实现者。对于一个实例(instance), module表示了一个网络服务(network service)在不同平台上的不同实现,这些平台比如说在java平台,MS windows平台,Solaris 平台。
    Module允许peer实现一个新的行为,通过提供了一个一般的抽象。当peer浏览或者加入一个peer组时,他可以查找新的其打算实现的行为。比如,当加入一个peer组后,一个peer可能必须学习新的搜索服务,这个服务只能在本peer组中使用。为了加入到这个组,这个peer必须实现这个新的搜索服务。这个module框架可以启动平台无关行为的表示(representation)和广告(advertisement)。并允许peer描述和实现任何形式这个行为(behavior)的是实现。比如,一个peer使用java或者c实现一个行为的实现。
& amp; nbsp;   描述和公告一个平台独立的行为的能力能有有效的支持peer组包含异构的peer。module的广告可以使JXTA的peer能够采用平台独立的方式描述一个行为。JXTA平台使用module广告来自描述。
    module抽象(Module abstractIon)包括一个module class,module specification和一个moduleimplementation。
  • Module Class。module class主要用于广告一个行为(behavior)的存在。这个class的定义表述了一个期望的行为和一个期望的对所支持的module的绑定。每一个module class都有一个唯一的ID,成为MoudleClassID
  • Module Specification。Module Specification主要用于访问这个module。它包含了访问或者启动这个module的所有必要的信息。比如,一个服务,他的module specification可能包含了一个用于和其他服务相通信的管道的广告。一个module specification用于提供module class所指明的功能。对于一个module class可能有多个module specification。每一个module specification都有一个唯一的id,ModuleSpecID。ModuleSpecID包含了ModuleClass ID,并指明了所使用的module class。一个module specification暗含了对网络的兼容性。对于一个给定的module specification的所有实现都必须使用相同的协议,这些实现都需要是兼容的,即使使用不同的语言实现的。
  • Module Implementation。Module Implement是一给定module specification的实现。对于一个module specification可以有多个module implementation。每一个module implementation都包含了它所实现的与specification相关联的ModuleSpecID。
    Module可以被peer组服务使用,也可以被独立的服务所使用。JXTA服务通过module abstraction来区别存在的服务(他的Module Class),服务的specification(Module Specification),或者服务的实现(Service Implementation)。所有的这些都有一个联合的广告,并且可以通过其他JXTA peer来公告和发现的。作为一个例子,考虑JXTA的发现服务。它包含一个唯一的ModuleClassID,标识了他作为一个发现服务——他的抽象功能。对于这个发现服务可以有多个不同的规范(Specification),并伴随着不同的实现。对于组的大小和在网络中的传播方式可以使用不同的裁剪策略。每一个Specification都有唯一的MdouleSpecID,其中指明了发现发现服务的ModuleClassID。对于每个规范,都可能有多种实现,每种实现都包含了相同的ModuleSpceID。
    总之,对于一个给定的module Class都可能有多种规范,这些规范可能是完全不同的。然而任何给定规范的所有实现都是假定可以互相兼容的。
posted @ 2007-05-19 23:11 Long Long Ago 阅读(936) | 评论 (0)编辑 收藏

JFace的单独使用很久都没有做过,基本上都是在开发elcipse插件时用JFace。今天使用JFace作为Java Application时遇到了 Exception in thread "main" java.lang.NoClassDefFoundError: org/eclipse/core/runtime/IStatus 错误,虽然已经添加了org.eclipse.jface,org.eclipse.core.runtime,org.eclipse.osgi包,但仍然出错,后来发现org.eclipse.core.runtime.IStatus是在org.eclipse.equonix.common包中,添加后发现 ,还需要添加org.eclipse.core.command包,再添加后搞定。
posted @ 2007-05-17 14:33 Long Long Ago 阅读(1039) | 评论 (2)编辑 收藏

    以前实现SWT中的模式和非模式窗体(modal or non-modal window )是通过在子窗体中是否截取父窗体的消息循环来实现的,现在发现这样好像不行了,但可以通过指定子窗体的样式(style)来制定子窗体是否是模式窗体。
Shell child = new Shell(shell,SWT.SHELL_TRIM|SWT.APPLICATION_MODAL);
上面的语句指定了子窗体child为模式窗体。在上面的style中指定SWT.SHELL_TRIM 是为了显示窗体的三个默认按钮(最大,最小和关闭),也可以用SWT.DIALOG_TRIM,不过此时为对话框样式,只有一个默认按钮(关闭)。默认的Shell是非模式窗体,并且是有默认系统按钮的,即样式为:SWT.SHELL_TRIM|SWT.MODELESS。
顺便说一下,在JFace的Dialog中使用模式对话框只要简单的设置该对话框setBlockOnOpen(true)即可。
posted @ 2007-05-17 14:27 Long Long Ago 阅读(2103) | 评论 (0)编辑 收藏

    在JXTA中分为三个层次,如下图所示。

     下面分别介绍各个层次:
  1. 平台层(platform layer)。平台层即所谓的JXTA核心(JXTA core),专门包装了最小最精华的部分,这部分主要完成了对P2P网络最一般的使用。包括,为P2P应用程序的关键机制构建模块,传输(包括防火墙穿透),创建peer和peer group以及和安全部分的结合。
  2. 服务层(Service layer)。服务层包含了一些网络服务,这些服务不一定是P2P程序中必须的,但却是P2P环境中通常和值得提供的。比如搜索,索引,目录,存储系统,文件共享,分布式文件系统,资源聚合和租借等应用服务,以及协议传输服务和认证和PKI服务等等。
  3. 应用层(Application layer)。应用层包括了综合应用的实现,比如P2P的及时消息,文档和资源的共享,娱乐内容管理和分发,P2P的email系统,分布式拍卖系统以及很多其他的应用。
    实际上,服务层和应用层之间的界限并不是明显的。一个用户的应用程序可以作为另一个用户的服务。整个系统被实际成模块化的,允许开发者选择一个服务和应用的集合来定制自己的需求。
    在JXTA中有三个主要方面是它区别一其他分布式网络模型的:
  1. 使用XML文档(广告)来描述网络资源。
  2. 针对peer间和peer与端点(endpoint)间的抽象管道(abstraction pipe)不需要使用一个可信赖的中心名字/地址认真,比如DNS。
  3. 唯一的peer地址方案(peer IDs)。
在下面的部分将详细介绍JXTA的各个组成部分。
posted @ 2007-05-13 14:13 Long Long Ago 阅读(793) | 评论 (0)编辑 收藏

    JXTA是一个为P2P计算而开发设计的开发网路计算平台。它的目标是通过创建基本组件和服务来为peer group创造新的应用。JXTA是juxtpose的简称,指并列并排,这里是说P2P的方式和C/S方式以及B/S方式是同等地位了,都是传统的分布式计算模型。 JXTA为开发应用程序提供了一组开发协议集合和一个开放源码的参考实现。JXTA协议标准化了peer的风格:
  1. 互相发现;
  2. 在peer group中的自组织;
  3. 广告和发现网络服务;
  4. peer间的通讯;
  5. peer间的交互。
    JXTA协议被设计成独立于程序设计语言和独立于传输协议的。这些协议可以使用Java或者C/C++或者perl实现。同样,也能在TCP/IP,HTTP,Bluetooth或者其他传输协议上实现。JXTA协议使得开发者可以构建和部署P2P的服务或者应用程序,因为这些协议是独立于程序语言和传输协议,所以可以使得采用完全不同软件体系的异构终端设备能够交互通讯。使用JXTA技术,开发人员可以开发出基于网络的,能够交互的应用程序,这些应用程序有如下特点:
  1. 通过动态查询和防火墙穿越来发现本网络中的其他peer;
  2. 任何访问网络的节点都可以方便地共享文档;
  3. 在网络站点中查找minute content;
  4. 创建一个peer group来提供服务;
  5. 远端监视peer的行为;
  6. 在网络中同其他peer进行安全的通信。
posted @ 2007-05-12 23:59 Long Long Ago 阅读(844) | 评论 (0)编辑 收藏

今天打算试试yaws,这是一个使用erlang实现的web服务器,参照blog:
http://yarivsblog.com/articles/2006/07/12/the-hitchhiker
首先需要安装erlang环境,windows和linux下都有,但yaws好像只给了个linux下的安装文件,于是在ubuntu6.06上安装。
从erlang主页www.erlang.org上下载源码,编译步骤:
tar -xzvf xxx.tar.gz
cd xxx
sudo ./configure
(sudo make clean)#可选的,用于非首次编译的情况
sudo make
sudo make install (as root)
但configure时出错:(类似如下)
configure: error: No curseslibraryfunctions found
There is a problem with $ERL_TOP/erts/configure not passing the LDFLAGS
environment variable for test compiles, so ALL library tests fail. I
modified files $ERL_TOP/erts/aclocal.m4 and $ERL_TOP/configure.in in order
to locate pthread_create in the standard C runtime library. The
$ERL_TOP/configure.in produces a configure that works, wheras the
$ERL_TOP/erts/configure.in does not. At the top of file erts/configure.in:

AC_PREREQ(2.13)
AC_INIT(vsn.mk)
应该是一些curses库没有安装,但apt-get install curses提示找不到,google了一下发现需要安装
ncurses-devel包,在网上只找到了相应的rpm包,使用alien命令,将rpm保转换为deb包安装:
sudo apt-get install alien
sudo alien *.rpm,转成deb后,用dpkg -i *.deb
或者
sudo alien -i *.rpm
进行安装
(注意,下载rpm包的时候我将包改名了,导致alien时出错,改成原来的名字就正常了,不知道为什么:P)
这时候cofigure可以过去了,但make又出错了,提示erlc找不到,类似如下的错误信息:
erlc -W +debug_info -I../include -o../ebin otp_ring0.erl
make[4]: erlc: Command not found
make[4]: ***[../ebin/otp_ring0.beam]Error127
make[4]: Leaving directory `/usr/src/packages/erlang-10.b.5/lib/kernel/src'
make[3]: *** [/usr/src/packages/erlang-10.b.5/lib/kernel/ebin/otp_ring0.beam] Error 2
make[3]: Leaving directory `/usr/src/packages/erlang-10.b.5/erts/emulator'
make[2]: *** [generate] Error 2
make[2]: Leaving directory `/usr/src/packages/erlang-10.b.5/erts/emulator'
make[1]: *** [depend] Error 2
make[1]: Leaving directory `/usr/src/packages/erlang-10.b.5'
make: *** [build-stamp] Error 2
make: *** [debs] Error 2

看网上有介绍说使用make clean并不完全,
于是将源码删除,重新解压缩,再configure后,make,没有提示erlc找不到了,又有提示类似如下的错误信息:
> otp_src_R11B-1/lib/kernel/ebin/erlang.beam > i686-pc-linux-gnu/preload.c
> m4 -DTARGET=i686-pc-linux-gnu -DOPSYS=linux -DARCH=x86 hipe/
> hipe_x86_asm.m4 > i686-pc-linux-gnu/opt/plain/hipe_x86_asm.h
> /bin/sh: m4: command not found
> make[2]: *** [i686-pc-linux-gnu/opt/plain/hipe_x86_asm.h] Error 127
> make[2]: Leaving directory `/home/jhancock/otp_src_R11B-1/erts/emulator'
> make[1]: *** [generate] Error 2
> make[1]: Leaving directory `/home/jhancock/otp_src_R11B-1/erts/emulator'
> make: *** [depend] Error 2
这是m4包没有找到(http://www.erlang.org/pipermail/erlang-questions/2006-November/023942.html)
安装m4包后,继续make出现如下错误:
hipe/hipe_mkliterals.c:351: error: 'X86_LEAF_WORDS' undeclared here (not in a function)
hipe/hipe_mkliterals.c:352: error: 'X86_NR_ARG_REGS' undeclared here (not in a function)
网上搜索结果:http://forum.trapexit.org/viewtopic.php?t=6815
将源码删除,再解压缩 再make终于过去了
提示:如下的包最好是在erlang安装之前就安装好的: perl, debhelper (>= 4.0.0), autoconf (>= 2.50), openssl, libssl-dev, m4, libncurses5-dev, dpatch, autotools-dev, unixodbc-dev
make过程大概有1个小时,之后是make install很快。
打开终端,输入erl,出现提示符1>
后面将yaws的安装和使用
yaws的安装挺简单的,主要是他的编译是基于erlang的。
需要注意的是,安装好后,如果没有安装相应的ssl模块的话,需要更改一下conf文件,将其中关于ssl的部分注释掉,既如下部分:
<server xxx>
      ....
          <ssl>
                ...
          </ssl>
</server>
在windows下的安装,在yaws的主页上有说明,需要下载一个bat文件,不过这个文件好像有问题,需要将一些安装的环境变量改一下,在(SET ERLC_FLAGS=)这行之后添加:
SET ProgramFiles=G:\erlang\yaws\yaws_program
SET HOME=G:\erlang\yaws\yaws_data
SET APPDATA=G:\erlang\yaws\yaws_configure
ProgramFiles 为yaws的程序位置,HOME为yaws的数据位置,包括www,log,wiki,ssl等,APPDATA为yaws的配置位置。
如果使用默认安装参数的话,需要将IF NOT DEFINED ProgramFiles SET ProgramFiles="c:\Program Files"和
HOME="%HOMEDRIVE%%HOMEPATH%\My Documents"中的双引号去掉(感觉这个bat文件写的比较乱)
同时不要忘了,在安装好之后注释掉conf文件中关于ssl的部分。
安装好后,默认就可以在%home/www目录或者/tmp(windows下为%home%\www  or C:\tmp)目录添加yaws文件(ehtml)来显示了。
btw:如果在ubuntu or debian上安装,最简单,只要apt-get install yaws 就可以了。运行yaws需要root权限。
posted @ 2007-01-18 23:22 Long Long Ago 阅读(4454) | 评论 (2)编辑 收藏

今天看到一个朋友的Blog, 就忍不住把以前写的这个代码拿出来了, 不然这代码闲着也是闲着. 当然没有必要照搬全部, 只要中间的那个 zoomImage() 方法即可. 当然还有设置图片部分透明的方法.

 

/*
* @(#)BlogMailHandler.java 1.00 2004-10-4
*
* Copyright 2004 . All rights reserved.
* PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.sql.Timestamp;
import java.util.Properties;

import javax.imageio.ImageIO;
import javax.mail.Address;
import javax.mail.FetchProfile;
import javax.mail.Flags;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Part;
import javax.mail.Session;
import javax.mail.Store;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeUtility;

import studio.beansoft.jsp.StringUtil;
import com.keypoint.PngEncoder;
import com.keypoint.PngEncoderB;

import moblog.*;

/**
* BlogMailHandler, 彩E博客邮件的处理程序.
* 每 15 分钟更新一次邮件, 发布博客并更新用户产量.
* 邮件 POP3 帐户信息位于 /blogmail.properties 文件中.
*
* @author 刘长炯
* @version 1.00 2004-10-4
*/
public class BlogMailHandler extends Thread {
/**
* @alias 文件根目录 : String
*/
private String rootDirectory;

// 邮件帐户配置属性
private static Properties props = new Properties();

static {
try {
InputStream in = BlogMailHandler.class
.getResourceAsStream("/blogmail.properties");
props.load(in);
in.close();
} catch (Exception ex) {
System.err.println("无法加载配置文件 blogmail.properties:"
+ ex.getMessage());
ex.printStackTrace();
}
}

// 图像加载的共享实例, 在 Linux 平台上有可能无法生成图形对象
// private static Frame sharedFrame = new Frame();
private boolean shouldExit = false;

public BlogMailHandler() {
}
/** 定时开始读取邮件信息 */
public void startMailProcessCycle() {
start();
}
public void run() {
while(!shouldExit) {
doProcess();
try {
// 每15分钟读取一次
Thread.currentThread().sleep(60 * 15 * 1000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
/** 处理进程 */
private void doProcess() {
try {
Store store = openStore();
Folder inbox = openInbox(store);

processAllMessages(inbox);
inbox.close(true);
store.close();
} catch (Exception e) {
e.printStackTrace();
}
}
protected void finalize() throws Throwable {
shouldExit = true;
}
/**
* 缩放原始图片到合适大小.
*
* @param srcImage - 原始图片
* @return BufferedImage - 处理结果
*/
private BufferedImage zoomImage(BufferedImage srcImage) {
int MAX_WIDTH = 100;// TODO: 缩放后的图片最大宽度
int MAX_HEIGHT = 160;// TODO: 缩放后的图片最大高度
int imageWidth = srcImage.getWidth(null);
int imageHeight = srcImage.getHeight(null);

// determine thumbnail size from MAX_WIDTH and MAX_HEIGHT
int thumbWidth = MAX_WIDTH;
int thumbHeight = MAX_HEIGHT;
double thumbRatio = (double)thumbWidth / (double)thumbHeight;
double imageRatio = (double)imageWidth / (double)imageHeight;
if (thumbRatio < imageRatio) {
thumbHeight = (int)(thumbWidth / imageRatio);
} else {
thumbWidth = (int)(thumbHeight * imageRatio);
}
// 如果图片小于所略图大小, 不作处理
if(imageWidth < MAX_WIDTH && imageHeight < MAX_HEIGHT) {
thumbWidth = imageWidth;
thumbHeight = imageHeight;
}

// draw original image to thumbnail image object and
// scale it to the new size on-the-fly (drawImage is quite powerful)
BufferedImage thumbImage = new BufferedImage(thumbWidth,
thumbHeight, BufferedImage.TYPE_INT_RGB);
//thumbImage.getsc
Graphics2D graphics2D = thumbImage.createGraphics();
graphics2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
graphics2D.drawImage(srcImage, 0, 0, thumbWidth, thumbHeight, null);
System.out.println("thumbWidth=" + thumbWidth);
System.out.println("thumbHeight=" + thumbHeight);
return thumbImage;
}

// Open mail Store
private Store openStore() throws Exception {
Store store;
//--[ Set up the default parameters
props.put("mail.transport.protocol", "pop");
props.put("mail.pop.port", "110");
// props.put("mail.debug", "true");

Session session = Session.getInstance(props);
store = session.getStore("pop3");
// void javax.mail.Service.connect(String host, String user, String
// password) throws
// MessagingException
store.connect(props.getProperty("mail.pop3.host"), props
.getProperty("username"), props.getProperty("password"));
return store;
}

// Open Inbox
private Folder openInbox(Store store) throws Exception {
Folder folder = store.getDefaultFolder();
if (folder == null) {
System.out.println("Problem occurred");
System.exit(1);
}

Folder popFolder = folder.getFolder("INBOX");
popFolder.open(Folder.READ_WRITE);
return popFolder;
}

/** Close mail store. */
private void closeStore(Store store) {
try {
store.close();
} catch (MessagingException e) {
e.printStackTrace();
}
}

/**
* 处理账号中的所有邮件并删除这些邮件.
*
* @param folder - Folder, 收件箱
* @throws Exception
*/
private void processAllMessages(Folder folder) throws Exception {

Message[] listOfMessages = folder.getMessages();
FetchProfile fProfile = new FetchProfile();
fProfile.add(FetchProfile.Item.ENVELOPE);
folder.fetch(listOfMessages, fProfile);

for (int i = 0; i < listOfMessages.length; i++) {
try {
processSingleMail(listOfMessages[i]);
} catch (Exception e) {
e.printStackTrace();
}

// Delete mail
listOfMessages[i].setFlag(Flags.Flag.DELETED, true);
}
}

/**
* 处理单个 Email, 将文章发表, 并将附件图片转换为 PNG 后存入用户目录.
*
* @param message -
* Message, email 消息
*/
private void processSingleMail(Message message) throws Exception {
BlogContent content = new BlogContent();
BlogUser user = new BlogUser();
BlogPicture picture = new BlogPicture();

// 1. 假定发件人为手机号, 并尝试根据手机号查找用户
Address[] addList = message.getFrom();
if (addList.length > 0) {
String userMail = ((InternetAddress) addList[0]).getAddress();
// 取出 彩E 邮件用户手机号, 格式: 手机号@someone.com
String mobileNumber = userMail.substring(0, userMail.indexOf("@"));
System.out.println("用户手机号为:" + mobileNumber);
if (!user.findByMDN(mobileNumber)) {
// Not found user, then return
System.err.println("user " + ((InternetAddress) addList[0]).getAddress() + " not found.");
return;
}
}

// 2. 尝试读取邮件正文
// 复合邮件
if (message.isMimeType("multipart/*")) {
// 标记是否处理过图片
boolean imageHasProcessed = false;
Multipart multipart = (Multipart) message.getContent();
for (int i = 0, n = multipart.getCount(); i < n; i++) {
// System.err.println("Reading multipart " + i);
Part part = multipart.getBodyPart(i);
// System.err.println("ContentType = " + part.getContentType());

// 3. 处理附件图片, 只处理第一个图片
String disposition = part.getDisposition();
// System.err.println("disposition = " + disposition);
if (disposition != null
&& (disposition.equals(Part.ATTACHMENT) || disposition
.equals(Part.INLINE)) && !imageHasProcessed) {
// 需要反编码邮件文件名, 有的邮件的附件的文件名是经过编码的,
// 但是 JavaMail 并不能处理出来(BeanSoft, 2004-10-13)
String fileName = MimeUtility.decodeText(part.getFileName());
String ext = StringUtil.getExtension(fileName)
.toLowerCase();
System.err.println("part.getFileName() = " + fileName);

if ("gif".equals(ext) || "jpg".equals(ext)
|| "png".equals(ext)) {
BufferedInputStream dataIn = null;
// 转换非 PNG 格式图片为 PNG 格式 -- 取消
// if (!"png".equals(ext)) {
ByteArrayOutputStream pngOut = new ByteArrayOutputStream();
try {
// Convert image file to PNG file
BufferedImage buffImg = ImageIO.read(part
.getInputStream());
// Read image file from attachment
// 缩放图片
buffImg = zoomImage(buffImg);

int imageWidth = buffImg.getWidth(null);
int imageHeight = buffImg.getHeight(null);
BufferedImage outImg = new BufferedImage(
imageWidth, imageHeight,
// BufferedImage.TYPE_4BYTE_ABGR 是 24 位色深, TYPE_BYTE_INDEXED 是 8 位
BufferedImage.TYPE_INT_RGB);
// 使图片透明
// embossImage(buffImg, outImg);
outImg.getGraphics().drawImage(buffImg, 0, 0, imageWidth, imageHeight, null);
// Save image to PNG output stream
// ImageIO.write(outImg, "png", pngOut);
// Save using keypoint PNG encoder
PngEncoderB pngb = new PngEncoderB(outImg,
PngEncoder.NO_ALPHA, 0, 9);
pngOut.write(pngb.pngEncode());
dataIn = new BufferedInputStream(
new ByteArrayInputStream(pngOut
.toByteArray()));
} catch (Exception e) {
}
// } else {
// dataIn = new BufferedInputStream(part
// .getInputStream());
// }
// All pictures change to png format
ext = "png"
// Insert picture info into database
picture.setBlogID(user.getId());
picture.setCreationTime(new Timestamp(System
.currentTimeMillis()));
picture.setFileEXName(ext);
picture.setTitle(fileName);
picture.create();
// Save png file to user directory, /users/userId/pictureId.png
FileOutputStream outFile = new FileOutputStream(
rootDirectory + File.separatorChar + user.getId() + File.separatorChar
+ picture.getId() + "." + ext);
int c;
while ((c = dataIn.read()) != -1) {
outFile.write(c);
}

outFile.close();
imageHasProcessed = true;
}
}
// 纯文本邮件, 带附件
else if (part.isMimeType("text/plain")) {
String body = part.getContent().toString();
String title = message.getSubject();

content.setBlogID(user.getId());
content.setCreationTime(new Timestamp(System.currentTimeMillis()));
content.setTitle(title);
content.setNote(body);
}

// 典型的 HTML 和 文本邮件可选形式, 进一步分析
if (part.getContent() instanceof Multipart) {
Multipart subPart = (Multipart) part.getContent();

for (int j = 0, m = subPart.getCount(); j < m; j++) {
Part mailText = subPart.getBodyPart(j);

if (mailText.isMimeType("text/plain")) {
String body = mailText.getContent().toString();
String title = message.getSubject();

content.setBlogID(user.getId());
content.setCreationTime(new Timestamp(System.currentTimeMillis()));
content.setTitle(title);
content.setNote(body);
break;
}
}
}

}// End of multipart parse

// 4. 创建博客记录
content.setPictureId(picture.getId());
if(content.create() > 0) {
// 更新用户产量
user.setPostTimes(user.getPostTimes() + 1);
user.update();
}
}
// 纯文本邮件, 无附件
else if (message.isMimeType("text/plain")) {
String body = message.getContent().toString();
String title = message.getSubject();

content.setBlogID(user.getId());
content.setCreationTime(new Timestamp(System.currentTimeMillis()));
content.setTitle(title);
content.setNote(body);

if(content.create() > 0) {
// 更新用户产量
user.setPostTimes(user.getPostTimes() + 1);
user.update();
}
}
}

// 测试, 尝试连接一次到邮件服务器
public static void main(String[] args) {
BlogMailHandler handler = new BlogMailHandler();

// TODO: debug, 请在 JSP 里面设置图片目录的根路径
handler.rootDirectory = "F:/Moblog/users/"
handler.doProcess();
}

/**
* @return Returns the rootDirectory.
*/
public String getRootDirectory() {
return rootDirectory;
}

/**
* @param rootDirectory
* The rootDirectory to set.
*/
public void setRootDirectory(String property1) {
this.rootDirectory = property1;
}

/** Make image transparent */
private void embossImage(BufferedImage srcImage, BufferedImage destImage) {
int width = srcImage.getWidth();
int height = srcImage.getHeight();

for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
int newColor = handlesinglepixel(j, i, srcImage.getRGB(j, i));
destImage.setRGB(j, i, newColor);
}
}
}

// Handle picture single pixel, change 0xff00ff color to transparent
private int handlesinglepixel(int x, int y, int pixel) {
int alpha = (pixel >> 24) & 0xff;
int red = (pixel >> 16) & 0xff;
int green = (pixel >> 8) & 0xff;
int blue = (pixel) & 0xff;
// Deal with the pixel as necessary...
// alpha 为 0 时完全透明, 为 255 时不透明
Color back = new Color(0xFF00FF);
// 将与背景色相同(此处PNG图片为紫色)的像素点的透明度设为透明
if (isDeltaInRange(back.getRed(), red, 2)
&& isDeltaInRange(back.getGreen(), green, 2)
&& isDeltaInRange(back.getBlue(), blue, 2)) {
// System.out.println("x=" + x + "y=" + y + " is transparent.");
alpha = 0;
}
// red = red / 2;
// //green = green / 2 + 68;
// blue = blue / 2;

return alpha << 24 | red << 16 | green << 8 | blue;
}

// 判断两个整数的差是否在一定范围之内
private boolean isDeltaInRange(int first, int second, int range) {
if (first - second <= range && second - first <= range)
return true;
return false;
}
}

  • # re: Java 中收取邮件并自动缩放图片的代码(原创)
    冷面阎罗
    Posted @ 2006-12-29 18:31
    不错!
    自己也写过java收发邮件的程序!  回复  
  • # re: Java 中收取邮件并自动缩放图片的代码(原创)
    托托姆
    Posted @ 2006-12-30 12:10
    不知道BeanSoft兄是不是看了我昨天的帖子有此感想。。。:)
    我测试了一下BeanSoft兄的zoomImage() 方法,如果按原代码执行,原本图片透明的部分将变成黑色。如果修改TYPE_INT_RGB为TYPE_INT_ARGB,则能避免这个问题。
posted @ 2006-12-30 13:27 Long Long Ago 阅读(577) | 评论 (0)编辑 收藏

写程序中遇到一个问题 如下:
mySoc = new Socket(svrAddress,5555);


myInput = new ObjectInputStream(mySoc.getInputStream());//有问题
myOutput = new ObjectOutputStream(mySoc.getOutputStream());//有问题
//myInput = new DataInputStream(mySoc.getInputStream());
//myOutput = new DataOutputStream(mySoc.getOutputStream()); 
注销的语句运行可以成功
但是未注销的那部分 运行时就卡在那里了
但是却没有抛出异常
请教原因是什么 有什么问题
该怎么解决呢?

找了好久终于再网上找到关于这个问题的说明了 因为问题比较特殊 所以贴出来希望对大家
有帮助

主机端先建立ObjectInputStream后建立ObjectOutputStream,则对应地客户端要先建立
ObjectOutputStream后建立ObjectInputStream,否则会造成两方互相等待数据而导致死
锁。

原因是建立ObjectInputStream对象是需要先接收一定的header数据,接收到这些数据之前
会处于阻塞状态。故而为了防止这种死锁状态,通讯两方的
ObjectInputStraem,ObjectOutputStream必须注意顺序对应使用。


目前相应的解决办法还没有找到 如果要解决 可以尝试重载对象输入输出流
posted @ 2006-12-30 12:46 Long Long Ago 阅读(939) | 评论 (1)编辑 收藏

       Tabbed Property是eclipse3.2中新加入一个view,可以使属性编辑器的功能近乎无限的扩大。这里说明一些Tabbed Property的使用方法。Tabbed Property中分成三个部分,Contributer,Tabs,Sections,一个Contributor包含若干个Tabs,一个Tabs又可以包含若干个sections。下面我们来分别进行描述。
      1。Contributor 这需要扩展org.eclipse.ui.views.properties.tabbed.PropertyContributor扩展点,定义时,最重要的是定义contributId,这个id必须是全局唯一的,这样在加载属性页时,才能找到这个我们定义的属性页,一般地,我们都将对应于这个属性页的workbenchpart的id作为本contributor的id,这样我们在代码中可以不硬编码本id字符串,而使用getSite().getId()就可以得到这个id了(当然,这样定义id不是必须的)。一个property view可以被多个workbench part共享,但 一个workbench part只能有一个property view,这个workbench part需要实现ITabbedPropertySheetPageContributor 接口,这个接口只有一个方法,要求返回本part对应的tabbed property Contributor id,一般只要return getSite().getId();
   contributor有如下几个attribute:
   1)typeMapper,这个类需要实现org.eclipse.ui.views.properties.tabbed.ITypeMapper,主要是实现类型的映射,因为我们选择的元素并不一定是实现IPropertySource的元素(即能够给property view提供内容的元素),比如在GEF中,我们选择的finger实际上是选择了对应的EditPart,而实际上实现了IPropertySource一般的是model部分的元素,所以这时候我们要将Editpart映射到对应的model元素。
   2)labelProvider,需要一个实现org.eclipse.jface.viewers.ILabelProvider的类,主要是在各个tabs的最上面显示文字和图片。
   3)propertyCategory,用于聚合多个tabs,注意至少要定义一个category,来聚合tabs,否则,可能会显示property失败。

   2。Tabs,这个需要扩展org.eclipse.ui.views.properties.tabbed.propertyTabs扩展点,其中contributorId就是与之相关联的Contributor的id,然后我们可以定义多个tab,这些tab的属性如下:
   1)label,用于显示在property view的tab bar上的字
   2)category,填入的就是在Contributor扩展点中定义的那些category,用于聚合tabs
   3)id,本tab的唯一标识
   4)afterTab,用于tab之间的排序,如果这是第一个tab,则没有afterTab,afterTab指的是在本tab之前的那个tab,并且afterTab描述的是在同一个category中的tabs,不同category之间的顺序是按照在contributor中定义category的顺序来定义的。
   5)indented,如果为ture,则各个tabs是有缩进的
   6)image,本tab的图片

   3。section ,需要扩展 org.eclipse.ui.views.properties.tabbed.PropertySections扩展点,它的contributionId就是本section所在的Contribution的id,针对每个tab,我们可以定义多个section,每个section的attribut描述如下:
   1)id,本secation的唯一标识
   2)tab,本section所属tab的标识
   3)class,实现了org.eclipse.ui.views.properties.tabbed.AbstractPropertySection抽象类的类,用于描述这个section的控件和布局。
   4)aftersection和上面的aftertab差不多,描述的是同一个tab中的section的顺序,注意afterserction描述的是本section之前的section的id
   5)filter:一个实现org.eclipse.jface.viewers.IFilter接口的过滤器,对选中元素进行过滤。
   6)enableFor:一个用于只是选择数目的值,必须要符合这个舒服才能使能这个section。如果不符合,则这个section就被过滤了,如果省略本值,则section的使能器就不会工作了。这是一个自然数,比如,当enableFor=1时,仅仅只有一个元素被选择的时候,本section才会被使能。

some notes:
    上面说过实现ITabbedPropertySheetPageContributor接口的workbench part除了要实现getContributeId方法外,还需要重载getAdapter方法,因为eclipse的默认加载的property veiw时原来的那个view,为了使tabbed property view能够加载,我们就需要重载getAdapter方法,返回一个TabbedPropertySheetPage对象。

    在实现section class的时候需要注意,createcontrol时首先应该先创建一个composite,一般是 Composite composite = getWidgetFactory().createFlatFormComposite(parent); 然后各个控件在这个composite上创建。


posted @ 2006-09-17 22:24 Long Long Ago 阅读(2865) | 评论 (1)编辑 收藏

安装subversion
基本命令:
  $ sudo apt-get install subversion
  $ sudo apt-get install libapache2-svn
可以安装的包:
 apache2
 apache2-common
 apache2-mpm-prefork
 apache2-utils
 libapache2-svn
 libapache2-mod-auth-pam
 libapache2-mod-auth-sys-group
 subversion
 subversion-tools


创建一个名为subversion的组:groupadd subversion
将自己(eg.:user)和www-data(apapch2帐号)用户添加入subversion组,可以编辑/etc/group文件,在最后找到subversion添加入帐号名(eg:user,www-data),看上去就像这样:subversion:x:1001:www-data,exp
然后是创建subversion库,并赋予subversion组中用户有读写subversion库的权限:
   $ sudo mkdir /home/svn  #创建svn库的父路径
   $ cd /home/svn
   $ sudo mkdir myproject  #创建本svn库的目录
   $ sudo svnadmin create /home/svn/myproject #使用svn命令,创建svn库
   $ sudo chown -R root:subversion myproject #更改本目录的组
   $ sudo chmod -R g+rws myproject #给本目录的组用户增加读写和递归增加新加目录的读写权限
注意上面提到的命令顺序,如果最后再执行创建库的命令(svnadmin create ....)则创建的文件没有获得组用户写的权限,这样在外部访问提交的时候会出错.
对于本机,可以直接使用file命令来访问:
  $ svn co(or checkout) file:///home/svn/myproject
#or
  $ svn co file://localhost/home/svn/myproject
注意:如果您并不确定主机的名称,您必须使用三个斜杠(///),而如果您指定了主机的名称,则您必须使用两个斜杠(//).
此时对svn库的权限是基于文件系统的,只要是subversion组中的用户都可以访问本svn库。

接下来,讲述如何使用apache服务器来提供对svn库的访问
编辑文件/etc/apache2/mods-available/dav_svn.conf
增加如下的内容:
  <Location /svn/myproject>
     DAV svn
     SVNPath /home/svn/myproject
     AuthType Basic
     AuthName "myproject subversion repository"
     AuthUserFile /etc/subversion/passwd
     
<LimitExcept GET PROPFIND OPTIONS REPORT>
        Require valid-user
     
</LimitExcept>
  
</Location>

apache会解析url中的/svn/myproject部分,来定位svn库,当收到此请求时,会查询svn库:/home/svn/myproject,这里的认证方式是basic,对于访问要求valid-user,帐号文件在/etc/subversion/passwd中。
注意重新设置后要重启apache2:sudo /etc/init.d/apache2 restart
编辑生成帐号文件: sudo htpasswd2 -c /etc/subversion/passwd user  #给user帐号创建口令
这时候可以通过浏览器来浏览svn库了
在我的设置中发现,apache2会自动绑定ipv6地址,可能会有些问题,可以强制apache绑定v4地址,在/etc/apache2/port.conf中改成:Listen [bindedip]:[port]的形式

通过https来访问svn库
首先生成一个 SSL 签名,使用命令

 # apache2-ssl-certificate

这里会有一系列关于你的个人隐私的问题,回答完了,自然的签名也就生成了,然
后我们就要在 apache2 里面打开 SSL 了,现在要做的是开启 ssl 模块

 # a2enmod ssl

然后,使用 apache2 的虚拟主机功能来添加 SSL 的支持,将

 /etc/apache2/sites-available/default

复制一份,叫

 /etc/apache2/sites-available/ssl

好啦

修改 default 文件的开头为

 NameVirtualHost *:80
 <VirtualHost *:80>

修改 ssl 文件的开头为

 NameVirtualHost *:443
 <VirtualHost *:443>

这里 443 是 SSL 的标准端口。

并在 ssl 文件中加入如下内容,在<VirtualHost></VirtualHost>内

 SSLEngine On
 SSLCertificateFile /etc/apache2/ssl/apache.pem

保存文件后,运行命令

   # a2ensite ssl

来激活这个虚拟主机

现在,修改文件

 /etc/apache2/ports.conf

加上一行

 Listen 443

好了,到此为止,SSL 服务器配置完成,重新启动 apache 吧。

 

一些问题:
可能出现 RA layer request failed svn: MKACTIVITY of 400 Bad Request 之类的错误,这可能是因为使用了代理的原因,代理不支持svn的扩展命令,see:http://subversion.tigris.org/faq.html#proxy
还有种原因,就是可能是你的客户端使用的是windowsxp,其他版本的windows我没试过,也是这样的错误,在linux下正常,解决方法不太清楚。
RA layer request failed svn: MKACTIVITY of 400 Bad Request,无论什么原因都可以用https代替http来暂时解决这样的问题。

参考:
http://fanqiang.chinaunix.net/app/web/2005-05-18/3257.shtml
http://wiki.ubuntu.org.cn/SubVersion?highlight=%28subversion%29

posted @ 2006-09-05 17:00 Long Long Ago 阅读(2956) | 评论 (0)编辑 收藏

在sources.list中添加如下几个源:
deb http://www.beerorkid.com/compiz/ dapper main
deb http://xgl.compiz.info/ dapper main
deb-src http://xgl.compiz.info/ dapper main
添加代理:
export http_proxy="http://xxx.xxx.xxx.xxx:xxxx"
获取pgp密钥:
wget http://www.beerorkid.com/compiz/quinn.key.asc -O - | sudo apt-key add - 

nivida的驱动:
sudo apt-get install nvidia-kernel-common nvidia-glx
编辑文件:/etc/X11/xorg.conf
在module部分中确定lode xgl,有如下代码:
Load "glx"
在devices部分修改除了Identifier行的其他各行,修改后如下:
Section "Device"
    Identifier- leave this line alone!
    Driver        "nvidia"
    BusID        "PCI:1:0:0"
    Option         "RenderAccel"         "true"
EndSection
在最下面添加Extensions部分,代码如下:
Section "Extensions"
          Option  "Composite" "Enable"
EndSection
下面是安装必要的库文件:
sudo apt-get install compiz xserver-xgl libgl1-mesa xserver-xorg libglitz-glx1 compiz-gnome
以上是引文http://www.ubuntuforums.org/showthread.php?t=131267 中的方法,此文所讲的后面是加载方法,我没有采用,用的是这里讲的方法:http://forum.ubuntu.org.cn/viewtopic.php?t=16777 不过这里讲的安装方法中少了一个库文件,呵呵
设置xgl启动入口:
新建一个xgl启动脚本/usr/bin/startxgl.sh,内容如下:
Xgl -fullscreen :1 -ac -accel glx:pbuffer -accel xv:pbuffer & sleep 2 && DISPLAY=:1
# Start GNOME
exec gnome-session 
使脚本可执行: sudo chmod 755 /usr/bin/startxgl.sh
新建一个compiz脚本/usr/bin/startcompiz,内容如下:
#!/bin/sh
killall gnome-window-decorator
wait
gnome-window-decorator & LD_PRELOAD=/usr/lib/fglrx/libGL.so.1.2.xlibmesa
compiz --replace gconf miniwin decoration transset wobbly fade minimize cube rotate zoom scale move resize place switcher trailfocus water & 
使得脚本可执行:sudo chmod 755 /usr/bin/startcompiz
在登陆管理器里建一个XGL会话: 建立一个文件/usr/share/xsessions/xgl.desktop ,内容如下:
[Desktop Entry]
Encoding=UTF-8
Name=XGl
Exec=/usr/bin/startxgl.sh
Icon=
Type=Application 

打开桌面菜单-〉系统-〉首选项-〉会话
在最右边的“启动程序”里添加 /usr/bin/startcompiz 这句话
最后不要忘了

sudo aptitude update
sudo aptitude upgrade
关闭所有程序
ctrl-alt-backspace启动X
登录时在会话中选择xgl
会提示是否为默认会话,建议选择仅本次
哦,差点忘了,怎么使用:
CTRL + ALT + Left/right arrow key. Switches to the new side of the cube for me.

CTRL + ALT + SHIFT + Left/Right arrow key- Takes the in focused app around cube.

CTRL + ALT + Left Click on Desktop - allows you to use the mouse to rotate cube.

F12 - uses the Expose like trick

Alt- Tab - switcher Vista-style
看起来有点晕,尤其是输入法的浮动窗体
posted @ 2006-08-31 13:03 Long Long Ago 阅读(904) | 评论 (3)编辑 收藏