2012年9月14日星期五

利用canvas进行 最大邻域法 图像放大

利用html5中的canvas可以进行在线图片处理,下面以放大图片为例来介绍基本方法。

图片放大可以用很多插值方法,比如在html中指定img元素的width属性时,浏览器会自动对其进行插值放大。但一些时候我们需要自定义的插值放大方法,比如下面的链接演示了雷达回波图片的放大,采用了基本的最大邻域法来进行放大,这时需要我们自己来写算法。

先看演示:雷达回波图采用最大邻域法来进行放大

下面是主要的两个处理函数:


//获取图片像素信息
var getImagePixelData = function(img){
var canvas = $('')[0];
canvas.width = img.width;
canvas.height = img.height;

var context = canvas.getContext('2d');//获取2D绘制上下文context 原生API

context.drawImage(img, 0, 0);//在context中绘制图片 原生API

return context.getImageData(0,0,img.width, img.height);//获取context中的像素数据 原生API
}

//利用包含图片像素数据的对象imageData 获取 放大后的对象newImageData
var scaleImage = function(imageData, newImageData){

var ow = imageData.width, oh = imageData.height;

var scalew = newImageData.width/ow,
scaleh = newImageData.height/oh;

var se1, se2, se3;

var pos = 0, pos2 = 0;

for(var y = 0; y < oh; y++){
for(var x = 0; x < ow; x++){

if(imageData.data[pos+3]){
for(var ny = Math.floor(y*scaleh); ny < Math.floor((y+1)*scaleh); ny++){
for(var nx = Math.floor(x*scalew); nx < Math.floor((x+1)*scalew); nx++){
pos2 = (ny*newImageData.height + nx)*4;
newImageData.data[pos2] = imageData.data[pos];
newImageData.data[pos2+1] = imageData.data[pos+1];
newImageData.data[pos2+2] = imageData.data[pos+2];
newImageData.data[pos2+3] = imageData.data[pos+3];
}
}

}
pos += 4;
}
}

return newImageData;
}

2012年9月12日星期三

SVN命令详解


  1. 将文件checkout到本地目录

    svn checkout path(path是服务器上的目录)
    例如:svn checkout svn://192.168.1.1/pro/domain
    简写:svn co


  2. 往版本库中添加新的文件

    svn add file (此时尚未提交到服务器版本库中)
    例如:svn add test.php(添加test.php)
    svn add *.php(添加当前目录下所有的php文件)


  3. 将改动的文件提交到版本库

    svn commit -m "LogMessage" [-N] [--no-unlock] PATH
    (如果选择了保持锁,就使用–no-unlock开关)
    例如:svn commit -m "add test file for my test" test.php
    简写:svn ci


  4. 加锁/解锁

    svn lock -m "LockMessage" [--force] PATH
    例如:svn lock -m "lock test file" test.php
    svn unlock PATH


  5. 更新到某个版本

    svn update -r m path
    例如: svn update如果后面没有目录,默认将当前目录以
    及子目录下的所有文件都更新到最新版本。
    svn update -r 200 test.php(将版本库中的文件test.php还原到版本200)
    svn update test.php(更新,于版本库同步。如果在提交的时候提示过期的话,
    是因为冲突,需要先update,修改文件,然后清除svn resolved,最后再提交commit)
    简写:svn up


  6. 查看文件或者目录状态

    svn status path...(目录下的文件和子目录的状态,正常状态不显示)
    【?:不在svn的控制中;M:内容被修改;C:发生冲突;
    A:预定加入到版本库;K:被锁定】
    2)svn status -v path(显示文件和子目录状态)
    第一列保持相同,第二列显示工作版本号,第三和第四列显示
    最后一次修改的版本号和修改人。
    注:svn status、svn diff和 svn revert这三条命令在
    没有网络的情况下也可以执行的,
    原因是svn在本地的.svn中保留了本地版本的原始拷贝。
    简写:svn st


  7. 删除文件

    svn delete path -m "delete test fle"
    例如:svn delete svn://192.168.1.1/pro/test.php -m "delete file"
    或者直接svn delete test.php 然后再svn ci -m "delete test file"(推荐)
    简写:svn (del, remove, rm)


  8. 移动文件或者目录或文件更名

    svn move -m "move test fle"
    例如:svn move -m "move test file" a.php b.php
    svn move -m "move test file" address admin/address
    把当前目录下得address目录移动到当前目录下的admin/address下


  9. 查看日志

    svn log path
    例如:svn log test.php 显示这个文件的所有修改记录,及其版本号的变化


  10. 查看文件详细信息

    svn info path
    例如:svn info test.php


  11. 比较差异

    svn diff path(将修改的文件与基础版本比较)
    例如:svn diff test.php
    svn diff -r m:n path(对版本m和版本n比较差异)
    例如:svn diff -r 200:201 test.php
    简写:svn di


  12. 将两个版本之间的差异合并到当前文件

    svn merge -r m:n path
    例如:svn merge -r 200:205 test.php(将版本200与205之间的差异
    合并到当前文件,但是一般都会产生冲突,需要处理一下)


  13. SVN 帮助

    svn help
    svn help ci


  14. 恢复本地修改

    svn revert: 恢复原始未改变的工作副本文件 (恢复大部份的本地修改)。revert:
    用法: revert PATH…
    注意: 本子命令不会存取网络,并且会解除冲突的状况。但是它不会恢复
    被删除的目录


  15. 解决冲突

    svn resolved: 移除工作副本的目录或文件的“冲突”状态。
    用法: resolved PATH…
    注意: 本子命令不会依语法来解决冲突或是移除冲突标记;它只是移除冲突的
    相关文件,然后让 PATH 可以再次提交。


  16. 创建纳入版本控制下的新目录

    svn mkdir: 创建纳入版本控制下的新目录。
    用法: 1、mkdir PATH…
    2、mkdir URL…
    创建版本控制的目录。
    1、每一个以工作副本 PATH 指定的目录,都会创建在本地端,并且加入新增
    调度,以待下一次的提交。
    2、每个以URL指定的目录,都会透过立即提交于仓库中创建。
    在这两个情况下,所有的中间目录都必须事先存在。


  17. 版本库下的文件和目录列表

    svn list path
    显示path目录下的所有属于版本库的文件和目录
    简写:svn ls


  18. 代码库URL变更

    svn switch (sw): 更新工作副本至不同的URL。
    用法: 1、switch URL [PATH]
    2、switch –relocate FROM TO [PATH...]
    1、更新你的工作副本,映射到一个新的URL,其行为跟“svn update”很像,也会将
    服务器上文件与本地文件合并。这是将工作副本对应到同一仓库中某个分支或者标记的
    方法。
    2、改写工作副本的URL元数据,以反映单纯的URL上的改变。当仓库的根URL变动
    (比如方案名或是主机名称变动),但是工作副本仍旧对映到同一仓库的同一目录时使用
    这个命令更新工作副本与仓库的对应关系。


  19. 输出指定文件或URL的内容。

    svn cat 目标[@版本]…如果指定了版本,将从指定的版本开始查找。
    svn cat -r PREV filename > filename
    (PREV 是上一版本,也可以写具体版本号,这样输出结果是可以提交的)




[原文链接]:Linux学习篇-SVN命令详解

2012年9月11日星期二

使用 HTML5 WebSocket 构建实时 Web 应用


作为下一代的 Web 标准,HTML5 拥有许多引人注目的新特性,如 Canvas、本地存储、多媒体编程接口、WebSocket 等等。这其中有“Web 的 TCP ”之称的 WebSocket 格外吸引开发人员的注意。WebSocket 的出现使得浏览器提供对 Socket 的支持成为可能,从而在浏览器和服务器之间提供了一个基于 TCP 连接的双向通道。Web 开发人员可以非常方便地使用 WebSocket 构建实时 web 应用,开发人员的手中从此又多了一柄神兵利器。本文首先介绍 HTML5 WebSocket 的基本概念以及这个规范试图解决的问题,然后介绍 WebSocket 的基本原理和编程接口。接下来会通过一个简单案例来示范怎样实现一个 WebSocket 应用,并且展示 WebSocket 如何在功能强大和编程简单易用上达到的完美统一。最后介绍了目前主流浏览器对 WebSocket 支持的状况、局限性以及未来的展望。

实时 Web 应用的窘境



Web 应用的信息交互过程通常是客户端通过浏览器发出一个请求,服务器端接收和审核完请求后进行处理并返回结果给客户端,然后客户端浏览器将信息呈现出来,这种机制对于信息变化不是特别频繁的应用尚能相安无事,但是对于那些实时要求比较高的应用来说,比如说在线游戏、在线证券、设备监控、新闻在线播报、RSS 订阅推送等等,当客户端浏览器准备呈现这些信息的时候,这些信息在服务器端可能已经过时了。所以保持客户端和服务器端的信息同步是实时 Web 应用的关键要素,对 Web 开发人员来说也是一个难题。在 WebSocket 规范出来之前,开发人员想实现这些实时的 Web 应用,不得不采用一些折衷的方案,其中最常用的就是轮询 (Polling) 和 Comet 技术,而 Comet 技术实际上是轮询技术的改进,又可细分为两种实现方式,一种是长轮询机制,一种称为流技术。下面我们简单介绍一下这几种技术:

轮询:



这是最早的一种实现实时 Web 应用的方案。客户端以一定的时间间隔向服务端发出请求,以频繁请求的方式来保持客户端和服务器端的同步。这种同步方案的最大问题是,当客户端以固定频率向服务器发起请求的时候,服务器端的数据可能并没有更新,这样会带来很多无谓的网络传输,所以这是一种非常低效的实时方案。

长轮询:



长轮询是对定时轮询的改进和提高,目地是为了降低无效的网络传输。当服务器端没有数据更新的时候,连接会保持一段时间周期直到数据或状态改变或者时间过期,通过这种机制来减少无效的客户端和服务器间的交互。当然,如果服务端的数据变更非常频繁的话,这种机制和定时轮询比较起来没有本质上的性能的提高。

流:



流技术方案通常就是在客户端的页面使用一个隐藏的窗口向服务端发出一个长连接的请求。服务器端接到这个请求后作出回应并不断更新连接状态以保证客户端和服务器端的连接不过期。通过这种机制可以将服务器端的信息源源不断地推向客户端。这种机制在用户体验上有一点问题,需要针对不同的浏览器设计不同的方案来改进用户体验,同时这种机制在并发比较大的情况下,对服务器端的资源是一个极大的考验。

综合这几种方案,您会发现这些目前我们所使用的所谓的实时技术并不是真正的实时技术,它们只是在用 Ajax 方式来模拟实时的效果,在每次客户端和服务器端交互的时候都是一次 HTTP 的请求和应答的过程,而每一次的 HTTP 请求和应答都带有完整的 HTTP 头信息,这就增加了每次传输的数据量,而且这些方案中客户端和服务器端的编程实现都比较复杂,在实际的应用中,为了模拟比较真实的实时效果,开发人员往往需要构造两个 HTTP 连接来模拟客户端和服务器之间的双向通讯,一个连接用来处理客户端到服务器端的数据传输,一个连接用来处理服务器端到客户端的数据传输,这不可避免地增加了编程实现的复杂度,也增加了服务器端的负载,制约了应用系统的扩展性。

WebSocket 的拯救



HTML5 WebSocket 设计出来的目的就是要取代轮询和 Comet 技术,使客户端浏览器具备像 C/S 架构下桌面系统的实时通讯能力。 浏览器通过 JavaScript 向服务器发出建立 WebSocket 连接的请求,连接建立以后,客户端和服务器端就可以通过 TCP 连接直接交换数据。因为 WebSocket 连接本质上就是一个 TCP 连接,所以在数据传输的稳定性和数据传输量的大小方面,和轮询以及 Comet 技术比较,具有很大的性能优势。Websocket.org 网站对传统的轮询方式和 WebSocket 调用方式作了一个详细的测试和比较,将一个简单的 Web 应用分别用轮询方式和 WebSocket 方式来实现,在这里引用一下他们的测试结果图:

[caption id="" align="alignnone" width="504"]图 1. 轮询和 WebSocket 实现方式的网络负载对比图图 1. 轮询和 WebSocket 实现方式的网络负载对比图[/caption]

通过这张图可以清楚的看出,在流量和负载增大的情况下,WebSocket 方案相比传统的 Ajax 轮询方案有极大的性能优势。这也是为什么我们认为 WebSocket 是未来实时 Web 应用的首选方案的原因。


WebSocket 规范



WebSocket 协议本质上是一个基于 TCP 的协议。为了建立一个 WebSocket 连接,客户端浏览器首先要向服务器发起一个 HTTP 请求,这个请求和通常的 HTTP 请求不同,包含了一些附加头信息,其中附加头信息”Upgrade: WebSocket”表明这是一个申请协议升级的 HTTP 请求,服务器端解析这些附加的头信息然后产生应答信息返回给客户端,客户端和服务器端的 WebSocket 连接就建立起来了,双方就可以通过这个连接通道自由的传递信息,并且这个连接会持续存在直到客户端或者服务器端的某一方主动的关闭连接。

下面我们来详细介绍一下 WebSocket 规范,由于这个规范目前还是处于草案阶段,版本的变化比较快,我们选择 draft-hixie-thewebsocketprotocol-76版本来描述 WebSocket 协议。因为这个版本目前在一些主流的浏览器上比如 Chrome,、FireFox、Opera 上都得到比较好的支持,您如果参照的是新一些的版本话,内容可能会略有差别。

一个典型的 WebSocket 发起请求和得到响应的例子看起来如下:

清单 1. WebSocket 握手协议

客户端到服务端:
GET /demo HTTP/1.1
Host: example.com
Connection: Upgrade
Sec-WebSocket-Key2: 12998 5 Y3 1 .P00
Upgrade: WebSocket
Sec-WebSocket-Key1: 4@1 46546xW%0l 1 5
Origin: http://example.com
[8-byte security key]

服务端到客户端:
HTTP/1.1 101 WebSocket Protocol Handshake
Upgrade: WebSocket
Connection: Upgrade
WebSocket-Origin: http://example.com
WebSocket-Location: ws://example.com/demo
[16-byte hash response]


这些请求和通常的 HTTP 请求很相似,但是其中有些内容是和 WebSocket 协议密切相关的。我们需要简单介绍一下这些请求和应答信息,”Upgrade:WebSocket”表示这是一个特殊的 HTTP 请求,请求的目的就是要将客户端和服务器端的通讯协议从 HTTP 协议升级到 WebSocket 协议。从客户端到服务器端请求的信息里包含有”Sec-WebSocket-Key1”、“Sec-WebSocket-Key2”和”[8-byte securitykey]”这样的头信息。这是客户端浏览器需要向服务器端提供的握手信息,服务器端解析这些头信息,并在握手的过程中依据这些信息生成一个 16 位的安全密钥并返回给客户端,以表明服务器端获取了客户端的请求,同意创建 WebSocket 连接。一旦连接建立,客户端和服务器端就可以通过这个通道双向传输数据了。

在实际的开发过程中,为了使用 WebSocket 接口构建 Web 应用,我们首先需要构建一个实现了 WebSocket 规范的服务器,服务器端的实现不受平台和开发语言的限制,只需要遵从 WebSocket 规范即可,目前已经出现了一些比较成熟的 WebSocket 服务器端实现,比如:


  • Kaazing WebSocket Gateway — 一个 Java 实现的 WebSocket Server

  • mod_pywebsocket — 一个 Python 实现的 WebSocket Server

  • Netty —一个 Java 实现的网络框架其中包括了对 WebSocket 的支持

  • node.js —一个 Server 端的 JavaScript 框架提供了对 WebSocket 的支持




如果以上的 WebSocket 服务端实现还不能满足您的业务需求的话,开发人员完全可以根据 WebSocket 规范自己实现一个服务器。在“WebSocket 实战”这一节,我们将使用 Microsoft .NET 平台上的 C# 语言来打造一个简单的 WebSocket 服务器,继而构建一个简单的实时聊天系统。

WebSocket JavaScript 接口



上一节介绍了 WebSocket 规范,其中主要介绍了 WebSocket 的握手协议。握手协议通常是我们在构建 WebSocket 服务器端的实现和提供浏览器的 WebSocket 支持时需要考虑的问题,而针对 Web 开发人员的 WebSocket JavaScript 客户端接口是非常简单的,以下是 WebSocket JavaScript 接口的定义:

清单 2. WebSocket JavaScript 定义

[Constructor(in DOMString url, in optional DOMString protocol)]
interface WebSocket {
readonly attribute DOMString URL;
// ready state
const unsigned short CONNECTING = 0;
const unsigned short OPEN = 1;
const unsigned short CLOSED = 2;
readonly attribute unsigned short readyState;
readonly attribute unsigned long bufferedAmount;
//networking
attribute Function onopen;
attribute Function onmessage;
attribute Function onclose;
boolean send(in DOMString data);
void close();
};
WebSocket implements EventTarget;


其中 URL 属性代表 WebSocket 服务器的网络地址,协议通常是”ws”,send 方法就是发送数据到服务器端,close 方法就是关闭连接。除了这些方法,还有一些很重要的事件:onopen,onmessage,onerror 以及 onclose。我们借用 Nettuts 网站上的一张图来形象的展示一下 WebSocket 接口:
[caption id="" align="alignnone" width="443"]图 2. WebSocket JavaScript 接口图 2. WebSocket JavaScript 接口[/caption]

下面是一段简单的 JavaScript 代码展示了怎样建立 WebSocket 连接和获取数据:

清单 3. 建立 WebSocket 连接的实例 JavaScript 代码
 var  wsServer = 'ws://localhost:8888/Demo'; 
var websocket = new WebSocket(wsServer);
websocket.onopen = function (evt) { onOpen(evt) };
websocket.onclose = function (evt) { onClose(evt) };
websocket.onmessage = function (evt) { onMessage(evt) };
websocket.onerror = function (evt) { onError(evt) };
function onOpen(evt) {
console.log("Connected to WebSocket server.");
}
function onClose(evt) {
console.log("Disconnected");
}
function onMessage(evt) {
console.log('Retrieved data from server: ' + evt.data);
}
function onError(evt) {
console.log('Error occured: ' + evt.data);
}


浏览器支持



下面是主流浏览器对 HTML5 WebSocket 的支持情况:
浏览器支持情况
ChromeSupported in version 4+
FirefoxSupported in version 4+
Internet ExplorerSupported in version 10+
OperaSupported in version 10+
SafariSupported in version 5+


WebSocket 实战



这一节里我们用一个案例来演示怎么使用 WebSocket 构建一个实时的 Web 应用。这是一个简单的实时多人聊天系统,包括客户端和服务端的实现。客户端通过浏览器向聊天服务器发起请求,服务器端解析客户端发出的握手请求并产生应答信息返回给客户端,从而在客户端和服务器之间建立连接通道。服务器支持广播功能,每个聊天用户发送的信息会实时的发送给所有的用户,当用户退出聊天室时,服务器端需要清理相应用户的连接信息,避免资源的泄漏。以下我们分别从服务器端和客户端来演示这个 Web 聊天系统的实现,在实现方式上我们采用了 C# 语言来实现 WebSocket 服务器,而客户端是一个运行在浏览器里的 HTML 文件。

WebSocket 服务器端实现



这个聊天服务器的实现和基于套接字的网络应用程序非常类似,首先是服务器端要启动一个套接字监听来自客户端的连接请求,关键的区别在于 WebSocket 服务器需要解析客户端的 WebSocket 握手信息,并根据 WebSocket 规范的要求产生相应的应答信息。一旦 WebSocket 连接通道建立以后,客户端和服务器端的交互就和普通的套接字网络应用程序是一样的了。所以在下面的关于 WebSocket 服务器端实现的描述中,我们主要阐述 WebSocket 服务器怎样处理 WebSocket 握手信息,至于 WebSocket 监听端口的建立,套接字信息流的读取和写入,都是一些常用的套接字编程的方式,我们就不多做解释了,您可以自行参阅本文的附件源代码文件。

在描述 WebSocket 规范时提到,一个典型的 WebSocket Upgrade 信息如下所示:
GET /demo HTTP/1.1 
Host: example.com
Connection: Upgrade
Sec-WebSocket-Key2: 12998 5 Y3 1 .P00
Upgrade: WebSocket
Sec-WebSocket-Key1: 4@1 46546xW%0l 1 5
Origin: http://example.com
[8-byte security key]

其中 Sec-WebSocket-Key1,Sec-WebSocket-Key2 和 [8-byte security key] 这几个头信息是 WebSocket 服务器用来生成应答信息的来源,依据 draft-hixie-thewebsocketprotocol-76 草案的定义,WebSocket 服务器基于以下的算法来产生正确的应答信息:


  1. 逐个字符读取 Sec-WebSocket-Key1 头信息中的值,将数值型字符连接到一起放到一个临时字符串里,同时统计所有空格的数量;

  2. 将在第 1 步里生成的数字字符串转换成一个整型数字,然后除以第 1 步里统计出来的空格数量,将得到的浮点数转换成整数型;

  3. 将第 2 步里生成的整型值转换为符合网络传输的网络字节数组;

  4. 对 Sec-WebSocket-Key2 头信息同样进行第 1 到第 3 步的操作,得到另外一个网络字节数组;

  5. 将 [8-byte security key] 和在第 3,第 4 步里生成的网络字节数组合并成一个 16 字节的数组;

  6. 对第 5 步生成的字节数组使用 MD5 算法生成一个哈希值,这个哈希值就作为安全密钥返回给客户端,以表明服务器端获取了客户端的请求,同意创建 WebSocket 连接




至此,客户端和服务器的 WebSocket 握手就完成了,WebSocket 通道也建立起来了。下面首先介绍一下服务器端实现是如何根据用户传递的握手信息来生成网络字节数组的。.NET 平台提供了很方便的对字符串,数值以及数组操作的函数,所以生成字节数组的方法还是非常简单明了的,代码如下:

清单 4. 生成网络字节数组的代码

private byte[]   BuildServerPartialKey(string clientKey)
{
string partialServerKey = "";
byte[] currentKey;
int spacesNum = 0;
char[] keyChars = clientKey.ToCharArray();
foreach (char currentChar in keyChars)
{
if (char.IsDigit(currentChar)) partialServerKey += currentChar;
if (char.IsWhiteSpace(currentChar)) spacesNum++;
}
try
{
currentKey = BitConverter.GetBytes((int)(Int64.Parse(partialServerKey)
/ spacesNum));
if (BitConverter.IsLittleEndian) Array.Reverse(currentKey);
}
catch
{
if (currentKey!= null) Array.Clear(currentKey, 0, currentKey.Length);
}
return currentKey;
}

得到网络字节数组以后,服务器端生成 16 位安全密钥的方法如下所示:

清单 5. 生成 16 位安全密钥的代码

private byte[] BuildCompleteServerKey(byte[] serverKey1, byte[] serverKey2,
byte[] last8Bytes)
{
byte[] concatenatedKeys = new byte[16];
Array.Copy(serverKey1, 0, concatenatedKeys, 0, 4);
Array.Copy(serverKey2, 0, concatenatedKeys, 4, 4);
Array.Copy(last8Bytes, 0, concatenatedKeys, 8, 8);
System.Security.Cryptography.MD5 MD5Service =
System.Security.Cryptography.MD5.Create();
return MD5Service.ComputeHash(concatenatedKeys);
}

整个实现是非常简单明了的,就是将生成的网络字节数组和客户端提交的头信息里的 [8-byte security key] 合并成一个 16 位字节数组并用 MD5 算法加密,然后将生成的安全密钥作为应答信息返回给客户端,双方的 WebSocekt 连接通道就建立起来了。实现了 WebSocket 握手信息的处理逻辑,一个具有基本功能的 WebSocket 服务器就完成了。整个 WebSocket 服务器由两个核心类构成,一个是 WebSocketServer,另外一个是 SocketConnection,出于篇幅的考虑,我们不介绍每个类的属性和方法了,文章的附件会给出详细的源代码,有兴趣的读者可以参考。

服务器刚启动时的画面如下:

[caption id="" align="alignnone" width="437"]图 3. WebSocket 服务器刚启动的画面图 3. WebSocket 服务器刚启动的画面[/caption]

客户端可以依据这个信息填写聊天服务器的连接地址,当有客户端连接到聊天服务器上时,服务器会打印出客户端和服务器的握手信息,每个客户的聊天信息也会显示在服务器的界面上,运行中的聊天服务器的界面如下:
[caption id="" align="alignnone" width="408"]图 4. 有客户端连接到 WebSocket 服务器的图 4. 有客户端连接到 WebSocket 服务器的[/caption]

以上我们简单描述了实现一个 WebSocket 服务器的最基本的要素,下一节我们会描述客户端的实现。

客户端实现



客户端的实现相对于服务器端的实现来说要简单得多了,我们只需要发挥想象去设计 HTML 用户界面,然后呼叫 WebSocket JavaScript 接口来和 WebSocket 服务器端来交互就可以了。当然别忘了使用一个支持 HTML5 和 WebSocket 的浏览器,在笔者写这篇文章的时候使用的浏览器是 Firefox。客户端的页面结构是非常简洁的,初始运行界面如下:
[caption id="" align="alignnone" width="353"]图 5. 聊天室客户端初始页面图 5. 聊天室客户端初始页面[/caption]

当页面初次加载的时候,首先会检测当前的浏览器是否支持 WebSocket 并给出相应的提示信息。用户按下连接按钮时,页面会初始化一个到聊天服务器的 WebSocekt 连接,初始化成功以后,页面会加载对应的 WebSocket 事件处理函数,客户端 JavaScript 代码如下所示:

清单 6. 初始化客户端 WebSocket 对象的代码
 function ToggleConnectionClicked() {
if (SocketCreated && (ws.readyState == 0 || ws.readyState == 1)) {
ws.close();
} else {
Log("准备连接到聊天服务器 ...");
try {
ws =
new WebSocket("ws://" + document.getElementById("Connection").value);
SocketCreated = true;
} catch (ex) {
Log(ex, "ERROR");
return;
}
document.getElementById("ToggleConnection").innerHTML = "断开";
ws.onopen = WSonOpen;
ws.onmessage = WSonMessage;
ws.onclose = WSonClose;
ws.onerror = WSonError;
}
};

function WSonOpen() {
Log("连接已经建立。", "OK");
$("#SendDataContainer").show("slow");
};

function WSonMessage(event) {
Log(event.data);
};

function WSonClose() {
Log("连接关闭。", "ERROR");
document.getElementById("ToggleConnection").innerHTML = "连接";
$("#SendDataContainer").hide("slow");
};


function WSonError() {
Log("WebSocket错误。", "ERROR");
};


当用户按下发送按钮,客户端会调用WebSocket对象向服务器发送信息,并且这个消息会广播给所有的用户,实现代码如下所示:

function SendDataClicked()
{
if (document.getElementById("DataToSend").value != "") {
ws.send(document.getElementById("txtName").value + "说 :\"" +
document.getElementById("DataToSend").value + "\"");
document.getElementById("DataToSend").value = "";
}
};


如果有多个用户登录到聊天服务器,客户端页面的运行效果如下所示:[caption id="" align="alignnone" width="415"]图 6. 聊天客户端运行页面图 6. 聊天客户端运行页面[/caption]


至此我们已经完成了一个完整的 WebSocket 客户端实现,用户可以体验一下这个聊天室的简单和快捷,完全不用考虑页面的刷新和繁琐的 Ajax 调用,享受桌面程序的用户体验。WebSocket 的强大和易用可见一斑,您完全可以在这个基础上加入更多的功能,设计更加漂亮的用户界面,切身体验 WebSocket 的震撼力。完整的客户端代码请参阅附件提供的源代码。

WebSocket 的局限性



WebSocket 的优点已经列举得很多了,但是作为一个正在演变中的 Web 规范,我们也要看到目前用 Websocket 构建应用程序的一些风险。首先,WebSocket 规范目前还处于草案阶段,也就是它的规范和 API 还是有变动的可能,另外的一个风险就是微软的 IE 作为占市场份额最大的浏览器,和其他的主流浏览器相比,对 HTML5 的支持是比较差的,这是我们在构建企业级的 Web 应用的时候必须要考虑的一个问题。

总结



本文介绍了 HTML5 WebSocket 的横空出世以及它尝试解决的的问题,然后介绍了 WebSocket 规范和 WebSocket 接口,以及和传统的实时技术相比在性能上的优势,并且演示了怎样使用 WebSocket 构建一个实时的 Web 应用,最后我们介绍了当前的主流浏览器对 HTML5 的支持情况和 WebSocket 的局限性。不过,我们应该看到,尽管 HTML5 WebSocket 目前还有一些局限性,但是已经是大势所趋,微软也明确表达了未来对 HTML5 的支持,而且这些支持我们可以在 Windows 8 和 IE10 里看到,我们也在各种移动设备,平板电脑上看到了 HTML5 和 WebSocket 的身影。WebSocket 将会成为未来开发实时 Web 应用的生力军应该是毫无悬念的了,作为 Web 开发人员,关注 HTML5,关注 WebSocket 也应该提上日程了,否则我们在新一轮的软件革新的浪潮中只能做壁上观了。

示例代码下载: source.zip

参考资料

学习


讨论

  • 加入 developerWorks 中文社区。查看开发人员推动的博客、论坛、组和维基,并与其他 developerWorks 用户交流。




[原文链接]: 使用 HTML5 WebSocket 构建实时 Web 应用

2012年4月9日星期一

Javascript 中类的实现方式之一

世上本没有路,走的人多了……
Javascript中本没有类,但可以用函数来实现类。

先来看简单的矩形类Rect的实现:

function Rect(w,h){
this.width = w;
this.height = h;
}

var rect1 = new Rect(10,20);
window.alert('rect1.width = '+rect1.width);

上面实现了Rect类,这个类有两个公开数据成员width和height。定义一个对象(或者叫类的一个实例)的方法是在函数前加上new关键字。
下面来看怎么定义私有数据成员:

function Rect(w,h){
var width = w, height = h;

this.getWidth = function(){
return width;
}
this.getHeight = function(){
return height;
}
}

var rect1 = new Rect(10,20);
window.alert('rect1.width = '+rect1.getWidth());

上面将width和height定义为私有数据成员,并定义了两个公开成员函数getWidth和getHeight。
可见,定义私有数据成员或者私有成员函数的方法实际上是将他们定义为函数内部变量。

另外需要特别说明的是:一般情况下,在函数Rect内部,私有成员函数只可以访问私有成员(私有数据成员或者私有成员函数),而公开成员函数可以访问所有成员,这一点和我们熟悉的面向对象方法不太一样。

2012年4月1日星期日

理解和应用canvas变换

默认的canvas画布以左上角为原点,向右为x轴增长方向,向下为y轴增长方向。默认以像素为单位。

很多情况下使用默认绘图环境极为不便,默认坐标系与我们上学时经常用到的直角坐标系不太一致。

幸运的是我们可以利用为canvas提供的变换函数来进行坐标转换,比如将原点移动到画布中心,转换Y轴方向等。
当调用一个变换函数时,接下来的绘图将自动变换。所以变换前应该先将当前环境保存下来,执行完绘图时恢复之前的环境。
save()和restore()两个函数用来保存和恢复环境。

下面说一下常用的变换函数:

1. 平移函数 translate(dx, dy)
调用此函数后接下来的绘图x值自动增加dx,y值自动增加dy。效果就是平移一个绘图,也就是所有点都进行了平移。这也是改变原点的办法。

2. 旋转函数 rotate(angle)
调用此函数后接下来的绘图将沿原点顺时针方向旋转angle弧度

3. 缩放函数 scale(a, b)
调用此函数后x坐标值将缩放a倍,y坐标将缩放b倍。效果是整个绘图放大或缩小了。
a和b的值可以为负值,比如 scale(1, -1)的效果是转换y轴的方向(向下变为向上)。

还有两个矩阵变换函数transform / setTransform。它们稍微复杂些,先不介绍。

上面那些变换函数可以一起使用,但你心中要明白每一步操作有什么影响。

回到开头的问题,比如我们想将原点移动到(300,400)这个点,然后使y轴方向向上,可以这么做:

dc.translate(300, 400);
dc.scale(1,-1);

接下来的绘图就是你熟悉的在直角坐标系下的绘图了。

当绘制正弦函数sin(x)时,由于y值最大为1,x的范围一般为2*π ,这时可以利用缩放函数scale对x轴和y轴进行放大。
又比如我们的数据值是在(1000,3000)附近,这时可以在设置好坐标后对坐标进行平移,使(1000,3000)这个点在画布中央。

dc.translate(300, 400);
dc.scale(1,-1);
dc.translate(-1000, -3000);



相关链接:1.canvas基础教程

2012年3月26日星期一

windows中的多线程处理

(一)建立新线程的函数


1.Win32 API函数 CreateThread
HANDLE WINAPI CreateThread(
__in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes,
__in SIZE_T dwStackSize,
__in LPTHREAD_START_ROUTINE lpStartAddress,
__in_opt LPVOID lpParameter,
__in DWORD dwCreationFlags,
__out_opt LPDWORD lpThreadId
);


2.也可以用C运行库的函数 _beginthread 来创建新线程
uintptr_t _beginthread( 
void( *start_address )( void * ),
unsigned stack_size,
void *arglist
);


(二)线程间通讯方法:


1.消息通讯
可以在线程中用SendMessage发送自定义消息到指定窗口
LRESULT WINAPI SendMessage(
__in HWND hWnd,
__in UINT Msg,
__in WPARAM wParam,
__in LPARAM lParam
);


2.创建线程时可以传递一个结构体的指针
3.利用全局变量来通讯

对Window程序消息循环的理解



  • 1.每个(创建窗口的)线程有一个消息队列,程序用下面的循环接收、转换消息,并将消息分发到合适的窗口处理程序:



    MSG msg = { };
    while (GetMessage(&msg, NULL, 0, 0))
    {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
    }



  • 2.windows系统负责把消息放入每个线程的消息队列或者直接调用合适的窗口处理程序




  • 3.Post消息和Send消息的区别



    a. Post一个消息(可以调用API函数PostMessage)表示消息将经过消息队列,通过消息循环来分发消息
    b. Send一个消息(可以调用API函数SendMessage)表示消息会跳过消息队列,操作系统直接调用窗口处理程序



  • 4.可以在任意线程中发送自定义的消息(消息值>= WM_USER)到指定的窗口




  • 5.消息队列


    操作系统维护一个系统消息队列并为每一个GUI线程维护一个线程消息队列。为了避免为非GUI线程创建消息队列的开销,所有的线程在最初创建时没有消息队列。仅当线程第一次调用特定的用户函数时系统才创建一个线程消息队列;没有任何GUI函数的调用会导致消息队列的创建。


  • 6.队列消息和非队列消息


    a. 队列消息

    移动或点击鼠标、敲击键盘等大部分消息为队列消息。
    仅当消息队列中没有其它消息时,WM_PAINTWM_TIMERWM_QUIT消息才被发送到窗口处理程序。
    其它的消息按先进先出的原则被发送到窗口处理程序
    用户可以用PostMessage等函数来发送一个队列消息

    b. 非队列消息

    比如当窗口转为active状态时,或者用户调用某些系统函数(如SetWindowPos)时,消息将跳过消息队列,直接发往窗口处理程序。
    用户可以调用SendMessage等函数来发送一个非队列消息


2012年3月24日星期六

C++ 字符串字面值的使用

以下语句将输出helloworld

//code1
char *a = "helloworld";

std::cout<

上面的a实际上是指向字符串字面值常量的起始地址,如果对a指向的字符进行赋值将会出现错误(比如 *(a+1) = 'E' 将出错)。
实际上应该像下面这样定义指针a(上面那样定义也能编译通过,但不推荐):

//code2
const char *a = "helloworld";


也可以用字符串字面值初始化字符数组,这样可以将字符数组当成字符串来使用:

//code3
char b[] = "helloworld";

std::cout<

注意上面是用字符串字面值来初始化字符数组b,数组b的长度为11,最后一位b[10] = '\0'. 可以直接在cout语句中输出b所存储的字符串的值,和code1类似。
但是请注意b为字符数组,数组b中的字符是可以改变的

//code4
char b[] = "helloworld";

b[0] = 'H';
std::cout<

上面代码将输出Helloworld,第一个字符由小写h修改为大写H。

稍微深入一点来说:
每个程序在内存中都有一块区域来存放常量、字符串字面值常量等。当用字符指针的方式来定义一个字符串时,实际上是将指针指向了字符串字面值常量的起始地址。
当用字符数组的方式来定义字符串时,程序首先开辟一段新的内存,然后将字符串字面值常量复制到这个位置。

可以用下面的程序来验证这点

#include

int main()
{
const char* p1 = "helloworld";
char array1[] = "helloworld";
const char* p2 = "helloworld";

std::cout<<" p1's address: "< <<"\n array1's address:"< <<"\n p2's address: "<
}


上面程序输出为:

p1's address: 13072640
array1's address:1899864
p2's address: 13072640


可以看到p1和p2指向相同的地址,而array1则为新的地址

2012年3月13日星期二

Boost中的Regex类库将部分中文识别为空白字符的问题原因及解决办法

问题描述:
使用字符类\s匹配一个字符串中的空白字符时,部分中文字符也将被识别为空白。

原因:
查看Boost的Regex源代码可以发现,Regex使用cpp_regex_traits类(在文件 boost/regex/v4/cpp_regex_traits.hpp 中定义)来处理字符类


// class cpp_regex_traits_base:
// acts as a container for locale and the facets we are using.
//
template
struct cpp_regex_traits_base
{
cpp_regex_traits_base(const std::locale& l)
{ imbue(l); }
std::locale imbue(const std::locale& l);

std::locale m_locale;
std::ctype const* m_pctype; //这个类用来识别空白符
//……
}


可以看到Regex使用std::ctype类来识别空白符等
ctype类有一个is成员函数用来判断某个字符是否属于空白字符或数字字符等,regex正是使用了这个成员函数。
查看这个成员函数的介绍可知字符类是在cctype头文件中定义的
cctype头文件定义了一系列函数来分类和转换单个字符,函数isspace来判断一个字符是否为空白字符
再来看函数isspace的说明,可知在C++中,指定了locale的此函数的模板版本存在于头文件中.
也就是说指定不同的locale,此函数的行为将不同。而默认的locale为"C" locale集。这就是问题的根源。

解决办法:1.使用[ \t\r\n]等特定的字符来代替\s
2.设置合适的locale并使用宽字符集和Regex的宽字符版本wregex

2012年3月7日星期三

Windows下编译和安装Boost库的指定模块

当然你可以使用www.boostpro.com提供的安装工具来安装,免去以下的步骤。但是那会安装许多不必要的文件,占用大量磁盘空间,有可能使你更迷惑。可参见这篇文章

如果你想了解如何一步步编译安装Boost库,请接着往下读。

一、到官网(www.boost.org)下载最新版Boost并解压到任意目录
先来看一下解压后的目录结构,以$BOOST_ROOT代表你Boost所在的目录

$BOOST_ROOT\ .................boost 根目录
index.htm .........A copy of www.boost.org starts here
boost\ .........................所有的Boost头文件
libs\ ............各种库的测试用例,文档说明。可以在这个文件夹中查看各种库怎么使用
index.html ........库文档从这里开始
algorithm\
any\
array\
…更多的库…
status\ .........................Boost范围测试套件
tools\ ...........一些有用工具, 比如 Boost.Build, quickbook, bcp
more\ ..........................政策等其它文档.
doc\ ...............所有Boost库文档的子集


二、编译需要的库
首先要说明的是大多数Boost库现在已经可以使用了,直接包含boost目录下相应的头文件即可,并不需要编译安装。

以下是必须编译安装的Boost库:


以下库拥有可选的编译组件(也就是说如果你要使用它们中的某些功能,则需要单独编译安装)

  • Boost.DateTime 当你使用它的 to_string/from_string 或者序列化特性, 或者目标平台为 Visual C++ 6.x 或 Borland.

  • Boost.Graph 仅仅当你想 处理 GraphViz 文件.

  • Boost.Math 含有TR1 和 C99 cmath 函数二进制组件.

  • Boost.Random 当你使用 random_device.

  • Boost.Test 可以 “header-only” 或 “separately compiled”
    模式使用, 尽管 对于郑重使用时推荐单独编译.



如果要编译上面的库,首先打开命令行,转到$BOOST_ROOT目录并运行目录下的bootstrap.bat脚本,这个脚本为Boost.Build系统的运行准备环境。

bootstrap.bat

可以看到现在$BOOST_ROOT目录下有b2.exe和bjam.exe两个可执行文件。这两个文件是一样的,只是名字不同,它们可以调用Boost.Build系统来创建上面那些需要编译安装的库。

比如我们想单独编译安装regex库,运行下面的命令即可

bjam stage --with-regex link=static threading=multi runtime-link=static

下面介绍相关参数的含义:
stage 仅创建和安装库文件(不创建头文件),可以用 --stagedir= 选项指定库的安装位置,默认安装在当前目录下的stage文件夹内。
--with- 创建和安装指定的库,如果使用了这个选项,则仅仅指定的库被创建,其它库不被创建。如果不指定这个选项,默认创建所有需要编译安装的库。
link=static指定生成静态regex库
threading=multi指定生成多线程库
runtime-link=static指定链接静态C和C++ 运行库

其他选项可参见这篇文章

三、配置开发环境
打开Visual Studio,在 工具->选项->项目和解决方案->VC++目录 条目下配置Boost包含文件和库文件目录,这样以后就不用每次都要配置Boost目录。

C++程序配置类库

一、INI文件读取库


除了使用Windows提供的API函数(见之前文章介绍)和BoostQTwxWidgets等类库提供的配置函数外,还可以使用下面的类库:

1.SimpleIni(code.jellycan.com/simpleini/)


一个跨平台的类库,提供简单的API读写INI文件,支持ASCII, MBCS and Unicode文件格式。开源,使用MIT协议。
使用方法:

CSimpleIniA ini;
ini.SetUnicode();
ini.LoadFile("myfile.ini");
const char * pVal = ini.GetValue("section", "key", "default");
ini.SetValue("section", "key", "newvalue");


2.RudeConfig(rudeserver.com/config/index.html)



// Create config object
//
Config config;


// load a configuration/.ini file
//
config.load("myfile.ini");


// read information
//
config.setSection("General Info");
double cost = config.getDoubleValue("Cost");
const char *company = config.getStringValue("Company Name");


// create information
//
config.setSection("new section");
config.setStringValue("animal type", "giraffe");
config.setBoolValue("mammal", true);

// save changes
//
config.save();


3.minIni(code.google.com/p/minini/)


minIni是一个在嵌入式系统中使用的读取INI文件库。minIni占用极少资源,可以为各种文件IO库配置。

二、XML文件读取库


1.TinyXML-2(grinninglizard.com/tinyxml2docs)


如名字所示,是简单的XML文件读取库,并且是第二版。仅包含一个.h文件和一个.cpp文件,直接和自己的源代码一起编译即可使用。


三、序列化文件(类似JSON格式)读取库


1.libconfig(hyperrealm.com/libconfig/)


Libconfig是一个简单的用来处理结构化配置文件的库,像这样的文件:test.cfg。这种文件格式比XML更紧凑易读。不像XML,它是可识别类型的,所以在程序代码中不必做字符串解析。

int var1;
double var2;
const char *var3;

if(config.lookupValue("values.var1", var1)
&& config.lookupValue("values.var2", var2)
&& config.lookupValue("values.var3", var3))
{
// use var1, var2, var3
}
else
{
// error handling here
}

2012年2月21日星期二

科学家部署激光器、GPS技术来改善降雪测量

通过配备专门的激光和GPS技术,国家大气研究中心(NCAR)的科学家们正在与同事一道解决一个关键的冬季天气奥秘:如何准确地测量地面上的降雪量。

运输船员,水资源管理者和其他那些重大安全决策者需要对于积雪深度如何在大范围内分布的精确测量。 但是当积雪量在单一区域或街道变化时,传统的测量设备,如雪压表或尺度杆往往不能够捕获 降雪总量。

[caption id="attachment_1136" align="alignleft" width="375" caption="Ethan Gutmann examines a laser instrument for measuring snow. "]Ethan Gutmann examines a laser instrument for measuring snow. [/caption]
现在,科学家们发现,使用光脉冲、卫星信号和其他技术的原型装置提供了几乎实时测量大面积降雪量的可能性。 随着时间的推移,这种装置甚至可能提供全球积雪深度的图像。

“我们已经准确测量降雨几个世纪了,但降雪量更难测量,因为它受风吹日晒和其他因素的影响”,NCAR的科学家伊桑•葛特曼(Ethan Gutmann),“看起来新技术将最终使我们能够精确地说出多少雪降在地面上。”

NCAR正与几个合作组织,包括美国国家海洋和大气管理局(NOAA)和科罗拉多大学博尔德大学(University of Colorado Boulder)进行这项研究。 这项工作得到NCAR的赞助商--国家科学基金会的支持。

不确定的深度


在大风暴期间应急管理人员依靠降雪观测来调集雪犁,或决定是否关闭高速和机场。 他们还使用雪总量来决定一个地区是否达到灾难援助的资格。 在山区,官员需要准确的积雪深度报告来评估雪崩或洪水的威胁,并预估从春季和夏季径流可用的水总量。

更精确的测量,还可以帮助气象学家和水文学家更好地了解雪物理和水文过程。

但传统的测雪方法可能极大低估或高估雪总量​​,特别是在恶劣的条件下。 在大风暴中雪仪表可能会缺测几乎三分之一的雪,即使当他们被专门设计的围栏保护起来以减少风的影响。 雪探头或雪尺可以在有限的区域内揭示积雪深度。 但是,这些工具需要不同地点的很多人工测量,这种方法在大雪期间可能无法得到雪总量。

[caption id="attachment_1137" align="alignleft" width="450" caption="Snow dunes. The three-dimensional features of a snow field above treeline is revealed by laser measurements. The laser, installed by NCAR at a test site in the Rocky Mountains, measures snow at more than 1,000 points across an area almost the size of a football field. "]Snow dunes[/caption]
气象专家有时也会监测收集在平坦、白色的被称为雪板的木板上的降雪量,但是这是一个时间密集型的方法,需要人每隔几个小时来检查雪板并清除他们。 两个全国最大的志愿者努力--全国天气服务合作观察员计划和社会协作雨、冰雹和雪网(CoCoRaHS)--每一个涉及全国数千名使用雪板的参与者,但他们的报告通常是仅仅一天提出一次。

最近,超声波装置已部署在一些世界上最寒冷的地区。 就像雷达,这些设备测量携带能量的超声波脉冲抵达雪面并返回到发射器所需的时间长度。 然而,信号可能受不断变化的大气条件(包括温度、湿度和风)的影响。

测试新方法


NCAR正在发展的专门的激光仪器可以纠正这些问题。 一旦设置在一个位置,它们就可以自动测量大面积的积雪深度。不同于超声仪器,激光依靠不受大气条件影响的光脉冲。

[caption id="attachment_1138" align="alignleft" width="350" caption="Ethan Gutmann walks past a snow gauge at a research site in Colorado."]snow gauge[/caption]
古特曼新的测试表明,安装在博尔德以西的落基山脉高于树线以上的激光仪器,可以测量10英尺以上的积雪,和测量半英寸的精度相当或更好。 这种仪器,在一个多小时里,在几乎是一个足球场大小的区域内测量超过1000个点的积雪,产生一个积雪深度及其变化的三维图像。

古特曼的下一步--如果他能获得急需的资金--将是建立和测试可以测量几平方英里的雪的激光仪器。 测量如此大的区域需要一个能够以每秒获取超过12,000个测量的新工具。

“如果我们成功了,一下子这些类型的工具将能够显示横跨整个流域的降雪的图像”他说。

然而,激光器的一个限制是光脉冲不能穿透物体,如树木和建筑物。这可能需要低成本的激光装置网络的发展,它们将记录每一个封闭区域内的积雪深度。另外,未来配备了这种激光器的卫星可能拥有从上面观测整个世界降雪的能力。

古特曼和Kristine拉尔森--在美国科罗拉多州大学的同事--也正在探索如何使用GPS传感器来进行降雪测量。 GPS传感器记录了两个卫星的信号,直接达到雪表面和地面反射的信号。 当地面上有雪,GPS信号用(相比地面是裸露的土壤)不同的频率反射雪,使科学家能够确定在地面之上是多高的雪面。

这些传感单元可能是一个经济有效的测量雪总量的方法,因为气象学家可以利用现有的地面GPS接收机的全球网络。 然而,研究人员正在充分了解雪的密度和其表面粗糙度如何改变GPS信号。

“我们希望建立一个高科技工具,使官员不断监测积雪深度,即使在强烈的风暴期间,”拉森说。 “虽然我们仍然需要做很多工作,该技术非常有前途。”

“我认为这项技术具有很大的可能让应急管理人员和其他决策者受益,就想我们的天气预报”,古特曼说。

原文链接:scientists-deploy-lasers-gps-technology-improve-snow-measurements

2012年2月13日星期一

PHP代码获取当前系统的CPU和内存的空闲百分比

设想我们有一个php页面A比较耗资源,因此在每次执行页面A中的代码前需要检测一下系统目前CPU和内存的空闲百分比。
我们可以利用下面几个函数来解决这个问题


//获取cpu的空闲百分比
function get_cpufree(){
$cmd = "top -n 1 -b -d 0.1 | grep 'Cpu'";//调用top命令和grep命令
$lastline = exec($cmd,$output);

preg_match('/(\S+)%id/',$lastline, $matches);//正则表达式获取cpu空闲百分比
$cpufree = $matches[1];
return $cpufree;
}
//获取内存空闲百分比
function get_memfree(){
$cmd = 'free -m';//调用free命令
$lastline = exec($cmd,$output);

preg_match('/Mem:\s+(\d+)/',$output[1], $matches);
$memtotal = $matches[1];
preg_match('/(\d+)$/',$output[2], $matches);
$memfree = $matches[1]*100.0/$memtotal;

return $memfree;
}

//获取某个程序当前的进程数
function get_proc_count($name){
$cmd = "ps -e";//调用ps命令
$output = shell_exec($cmd);

$result = substr_count($output, ' '.$name);
return $result;
}


比如当CPU空闲率小于30%时我们延迟页面A执行:

$cpufree = get_cpufree();

while( $cpufree<30 ){
// wait for 0.1 seconds
usleep(0.1*1000000);
$cpufree = get_cpufree();
};

2012年2月9日星期四

在线绘制近10年全球天气形势图(chatianqi.org)

天气预报等气象行业经常要与天气图打交道,查看并分析500hPa、700hPa等不同层次的高度场、温度场、湿度场和风场等要素,了解对流层的结构,从而制作天气预报。而每次遇到暴雨强对流等灾害性天气,更要认真分析实时的天气图,之后还要总结各种灾害性天气出现时对应的典型天气形势。只有对大量灾害性天气的形势图进行总结分析,才能得出规律,以便更好进行下一次预报。

而每次写技术总结,学术交流文章,写论文都要再次回顾一些天气个例,利用绘图工具对典型的天气形势进行绘图。很多人会对那些复杂的绘图命令望而生畏,即使对一些绘图工具有一些了解,绘制专业并且漂亮的天气形势图也需要花费大量的时间。而且你需要重新整理各种历史资料,提取需要的数据等,而这种资料的整理也需要花费大量时间。

现在给大家推荐一个网站,可以在线绘制近10年全球天气形势图,让大家省却以上烦恼并将为大家节省大量宝贵的时间。
网址为:chatianqi.org 十分好记,就是"查天气"的拼音

无论你在何地,使用任何设备(单位电脑、平板电脑等),只要能连接互联网,就可以打开这个网站查看任一日期的天气图,并可以对比多个层次、多个时次的天气图。
快来体验一下吧!

2012年1月2日星期一

推荐一些适用于安卓(Android)平板的应用




  • 1.百度输入法Pad版 (官方网站)


    个人觉得很好用

  • 2.安卓市场平板客户端 (官方网站)


    这里可以搜索并下载各种应用与游戏,资源比较丰富

  • 3.MoboPlayer (官方网站)


    比较好用的视频播放器软件,支持各种视频格式

  • 4.Evernote (官方网站)


    强大的笔记本软件,可在各种平台间同步笔记。
    国内类似的应用为有道云笔记,但功能差远了,在wifi代理情况下不能同步。

  • 5.鲜果联播Android HD版 (官方网站)


    杂志般的阅读体验,可订阅各种内容。这个可以取代RSS阅读器了。

  • 6.Google Maps (官方网站)


    google 官方的地图应用,强大易用不必多说。另外Google Sky Map还可以查看星空。

  • 7.Google Music (官方网站)


    Google的另一个应用,这个一定要安装,超炫的音乐播放器。有人说你装了这个软件就再也不想装其他的音乐播放器了

  • 8.豆瓣FM (官方网站)


    豆瓣电台,自动推荐歌曲

  • 9.云中书城 (官方网站)


    盛大出品的阅读应用,可以下载各种电子书

  • 10.Twitter (官方网站)


    这个不解释

  • 11.豌豆荚 (官方网站)


    在Windows下安装豌豆荚程序,可以向android平板里面安装应用,并且可以截取平板上面的屏幕图像

  • 12.豆果美食 Android Pad版 (官方网站)


    发现的唯一针对Android平板进行优化设计的食谱应用,内容很丰富,拿着平板可以做菜了

  • 13.Adobe Reader (官方网站)


    官方的PDF阅读器,很流畅,支持文本重排,阅读体验非常好

  • 14.开卷有益 (官方网站)


    支持TXT EPUB UMD CHM HTML JPG PNG GIF CBZ格式电子书 漫画和图片。