存档在 2013年3月

zigbee远程控制与网络拓扑结构显示项目1

2013年3月8日

项目截图




lighthttp

2013年3月6日

lighthttp:轻量级的http服务器代码,看透这个很容易明白linux 多进程处理,通信模块如何设计,

C++ Sockets Library

2013年3月6日

官方网站http://www.alhem.net/Sockets/
This is a GPL licensed C++ class library wrapping the berkeley sockets C API, and therefore works on most unixes and also win32. The library is in use in a number of real world applications, both commercial and open source.

Features include, but are not limited to, SSL support, IPv6 support, tcp and udp sockets, sctp sockets, http protocol, highly customizable error handling. Testing has been done on Linux and Windows 2000, and to some part on Solaris and Mac OS X.

The source code is released under the terms of the GNU GPL, but is also available under an alternative license.

Protocol Buffers介绍

2013年3月6日

  今天来介绍一下“Protocol Buffers”(以下简称protobuf)这个玩意儿。本来俺在构思“生产者/消费者模式 ”系列的下一个帖子:关于生产者和消费者之间的数据传输格式。由于里面扯到了protobuf,想想干脆单独开一个帖子算了。

  ★protobuf是啥玩意儿?
  为了照顾从没听说过的同学,照例先来扫盲一把。
  首先,protobuf是一个开源项目(官方站点在“这里 ”),而且是后台很硬的开源项目。网上现有的大部分(至少80%)开源项目,要么是某人单干、要么是几个闲杂人等合伙搞。而protobuf则不然,它是鼎鼎大名的Google公司开发出来,并且在Google内部久经考验的一个东东。由此可见,它的作者绝非一般闲杂人等可比。
  那这个听起来牛X的东东到底有啥用处捏?简单地说,这个东东干的事儿其实和XML差不多,也就是把某种数据结构的信息,以某种格式保存起来。主要用于数据存储、传输协议格式等 场合。有同学可能心理犯嘀咕了:放着好好的XML不用,干嘛重新发明轮子啊?!先别急,后面俺自然会有说道。
  话说到了去年(大约是08年7 月),Google突然大发慈悲,把这个好东西贡献给了开源社区。这下,像俺这种喜欢捡现成的家伙可就有福啦!貌似喜欢捡现成的家伙还蛮多滴,再加上Google的号召力,开源后不到一年,protobuf的人气就已经很旺了。所以俺为了与时俱进,就单独开个帖子来忽悠一把。

  ★protobuf有啥特色?
  扫盲完了之后,就该聊一下技术方面的话题了。由于这玩意儿发布的时间较短(未满周岁),所以俺接触的时间也不长。今天在此是先学现卖,列位看官多多包涵 :-)

  ◇性能好/效率高
  现在,俺就来说说Google公司为啥放着好端端的XML不用,非要另起炉灶,重新造轮子。一个根本的原因是XML性能不够好。
  先说时间开销:XML格式化(序列化)的开销倒还好;但是XML解析(反序列化)的开销就不敢恭维啦。俺之前经常碰到一些时间性能很敏感的场合,由于不堪忍受XML解析的速度,弃之如敝履。
  再来看空间开销:熟悉XML语法的同学应该知道,XML格式为了有较好的可读性,引入了一些冗余的文本信息。所以空间开销也不是太好(不过这点缺点,俺不常碰到)。
   由于Google公司赖以吹嘘的就是它的海量数据和海量处理能力。对于几十万、上百万机器的集群,动不动就是PB级的数据量,哪怕性能稍微提高0.1%也是相当可观滴。所以Google自然无法容忍XML在性能上的明显缺点。再加上Google从来就不缺造轮子的牛人,所以protobuf也就应运而生了。
  Google对于性能的偏执,那可是出了名的。所以,俺对于Google搞出来protobuf是非常滴放心,性能上不敢说是最好,但肯定不会太差。

  ◇代码生成机制
  除了性能好,代码生成机制是主要吸引俺的地方。为了说明这个代码生成机制,俺举个例子。
  比如有个电子商务的系统(假设用C++实现),其中的模块A需要发送大量的订单信息给模块B,通讯的方式使用socket。
假设订单包括如下属性:
--------------------------------
  时间:time(用整数表示)
  客户id:userid(用整数表示)
  交易金额:price(用浮点数表示)
  交易的描述:desc(用字符串表示)
--------------------------------
  如果使用protobuf实现,首先要写一个proto文件(不妨叫Order.proto),在该文件中添加一个名为”Order”的message结构,用来描述通讯协议中的结构化数据。该文件的内容大致如下:

--------------------------------
message Order
{
required int32 time = 1;
required int32 userid = 2;
required float price = 3;
optional string desc = 4;
}
--------------------------------

  然后,使用protobuf内置的编译器编译 该proto。由于本例子的模块是C++,你可以通过protobuf编译器的命令行参数(看“这里 ”),指定它生成C++语言的“订单包装类”。(一般来说,一个message结构会生成一个包装类)
  然后你使用类似下面的代码来序列化/解析该订单包装类:

--------------------------------
// 发送方
Order order;
order.set_time(XXXX);
order.set_userid(123);
order.set_price(100.0f);
order.set_desc(“a test order”);

string sOrder;
order.SerailzeToString(&sOrder);
// 然后调用某种socket的通讯库把序列化之后的字符串发送出去
// ……

--------------------------------
// 接收方
string sOrder;
// 先通过网络通讯库接收到数据,存放到某字符串sOrder
// ……

Order order;
if(order.ParseFromString(sOrder)) // 解析该字符串
{
cout << "userid:" << order.userid() << endl << "desc:" << order.desc() << endl; } else { cerr << "parse error!" << endl; } --------------------------------   有了这种代码生成机制,开发人员再也不用吭哧吭哧地编写那些协议解析的代码了(干这种活是典型的吃力不讨好)。   万一将来需求发生变更,要求给订单再增加一个“状态”的属性,那只需要在Order.proto文件中增加一行代码。对于发送方(模块A),只要增加一行设置状态的代码;对于接收方(模块B)只要增加一行读取状态的代码。哇塞,简直太轻松了!   另外,如果通讯双方使用不同的编程语言来实现,使用这种机制可以有效确保两边的模块对于协议的处理是一致的。   顺便跑题一下。   从某种意义上讲,可以把proto文件看成是描述通讯协议的规格说明书(或者叫接口规范)。这种伎俩其实老早就有了,搞过微软的COM编程或者接触过CORBA的同学,应该都能从中看到IDL(详细解释看“这里 ”)的影子。它们的思想是相通滴。   ◇支持“向后兼容”和“向前兼容”   还是拿刚才的例子来说事儿。为了叙述方便,俺把增加了“状态”属性的订单协议成为“新版本”;之前的叫“老版本”。   所谓的“向后兼容”(backward compatible),就是说,当模块B升级了之后,它能够正确识别模块A发出的老版本的协议。由于老版本没有“状态”这个属性,在扩充协议时,可以考虑把“状态”属性设置成非必填 的,或者给“状态”属性设置一个缺省值(如何设置缺省值,参见“这里 ”)。   所谓的“向前兼容”(forward compatible),就是说,当模块A升级了之后,模块B能够正常识别模块A发出的新版本的协议。这时候,新增加的“状态”属性会被忽略。   “向后兼容”和“向前兼容”有啥用捏?俺举个例子:当你维护一个很庞大的分布式系统时,由于你无法同时 升级所有 模块,为了保证在升级过程中,整个系统能够尽可能不受影响,就需要尽量保证通讯协议的“向后兼容”或“向前兼容”。   ◇支持多种编程语言   俺开博以来点评的几个开源项目(比如“Sqlite ”、“cURL ”),都是支持很多种 编程语言滴,这次的protobuf也不例外。在Google官方发布的源代码中包含了C++、Java、Python三种语言(正好也是俺最常用的三种,真爽)。如果你平时用的就是这三种语言之一,那就好办了。   假如你想把protobuf用于其它语言,咋办捏?由于Google一呼百应的号召力,开源社区对protobuf响应踊跃,近期冒出很多其它编程语言 的版本(比如ActionScript、C#、Lisp、Erlang、Perl、PHP、Ruby等),有些语言还同时搞出了多个开源的项目。具体细节可以参见“这里 ”。   不过俺有义务提醒一下在座的各位同学。如果你考虑把protobuf用于上述这些语言,一定认真评估对应的开源库。因为这些开源库不是Google官方提供的、而且出来的时间还不长。所以,它们的质量、性能等方面可能还有欠缺。   ★protobuf有啥缺陷?   前几天刚刚在“光环效应 ”的帖子里强调了“要同时评估优点和缺点”。所以俺最后再来批判一下这玩意儿的缺点。   ◇应用不够广   由于protobuf刚公布没多久,相比XML而言,protobuf还属于初出茅庐。因此,在知名度、应用广度等方面都远不如XML。由于这个原因,假如你设计的系统需要提供若干对外的接口给第三方系统调用,俺奉劝你暂时不要考虑protobuf格式。   ◇二进制格式导致可读性差   为了提高性能,protobuf采用了二进制格式进行编码。这直接导致了可读性差的问题(严格地说,是没有可读性)。虽然protobuf提供了TextFormat这个工具类(文档在“这里 ”),但终究无法彻底解决此问题。    可读性差的危害,俺再来举个例子。比如通讯双方如果出现问题,极易导致扯皮(都不承认自己有问题,都说是对方的错)。俺对付扯皮的一个简单方法就是直接抓包并dump成log,能比较容易地看出错误在哪一方。但是protobuf的二进制格式,导致你抓包并直接dump出来的log难以看懂。   ◇缺乏自描述   一般来说,XML是自描述的,而protobuf格式则不是。给你一段二进制格式的协议内容,如果不配合相应的proto文件,那简直就像天书一般。   由于“缺乏自描述”,再加上“二进制格式导致可读性差”。所以在配置文件方面,protobuf是肯定无法取代XML的地位滴。   ★为什么俺会用上protobuf?   俺自从前段时间接触了protobuf之后,就着手把俺负责的产品中的部分 数据传输协议替换成protobuf。可能有同学会问,和protobuf类似的东东也有不少,为啥独独相中protobuf捏?由于今天写的篇幅已经蛮长了,俺卖个关子,把这个话题留到“生产者/消费者模式[5]:如何选择传输协议及格式?”。俺会在后续的这个帖子里对比各种五花八门的协议格式,并谈谈俺的浅见。

ZeroMQ简介

2013年3月6日

官方网站:http://www.zeromq.org/
★ZMQ是啥玩意儿?
  通俗地说,ZMQ是一个开源的、跨语言的、非常简洁的、非常高性能、非常灵活的网络通讯库。
  它的官方网站在”这里”,维基百科的介绍在”这里”(暂时没有中文的维基词条)。
  这玩意儿推出的时间不长,貌似09年下半年才推出1.0.1版本。俺去年开始接触它,感觉实在不错,今年就已经用于公司的产品中。最近一段时间,对 ZMQ 的好评日渐增多,所以俺也来赶赶潮流,在俺博客里忽悠一下。
  接下来,就针对ZMQ的几大特点,分别聊一聊。

★简单
  ZMQ的首要特点,就是简单(从它的名字也能感觉得到)。

◇封装导致的简单
  相比原始的 socket API,ZMQ 封装掉了很多东西,免去了开发人员的很多麻烦。
  比如,传统的 TCP 是基于字节流进行收发,因此程序猿常常要自己去处理数据块与数据块之间的边界(断界处理);与之相对,ZMQ 是以消息为单位进行收发,它确保你每次发出/收到的,都是一个消息块。这样一来,就省却了不少代码量。
  比如,基于 socket API 进行 TCP 通讯,你需要自己处理很多网络异常(比如连接异常中断以及重连),即使有经验的程序员,也未必能写得严密。而在 ZMQ 中,这些琐事统统不用程序猿操心。
  再比如,用传统的 socket API,当你想提高通讯性能,往往要搞些异步(非阻塞)、缓冲区、多线程之类的把戏。而这些东西,ZMQ 也帮你封装掉了。
  总而言之,ZMQ 对很多底层细节的封装,让你的网络程序代码变得简单,写起来又快又轻松。

◇API的简单
  ZMQ 的 API 接口很少,而且在风格上非常类似于 BSD Socket。如果你曾经用 socket API 写过程序,那要上手 ZMQ 是非常容易的。如果你是 Java 程序猿,搞过 JMS API(比如 ActiveMQ),那你会发觉两者的 API 简直是天壤之别。顺便抱怨一下:Java 的 JMS API,那可真是复杂啊!

◇具体的示例
  为了增加说服力,下面给出 Python 语言实现的 Echo Server 代码(所谓的Echo Server,是一种最简单的服务端程序。它把收到的信息原样回送给客户端程序)。

#服务端程序
import zmq
context = zmq.Context()
socket = context.socket(zmq.REP)
socket.bind(“tcp://127.0.0.1:1234”)

while True :
msg = socket.recv()
socket.send(msg)

#客户端端程序
import zmq
context = zmq.Context()
socket = context.socket(zmq.REQ)
socket.connect(“tcp://127.0.0.1:1234”)
msg_send = “xxx”socket.send(msg_send)
print “Send:”, msg_send
msg_recv = socket.recv()
print “Receive:”, msg_recv

  从上述示例代码,大伙儿应该能感觉到:ZMQ 的使用,是非常简单的。

★灵活
  所谓的灵活性,主要指如下2方面。

◇适用于多种通讯环境
  ZMQ 可以灵活地支持多种通讯环境(进程内,主机内跨进程、跨主机)。ZMQ 的 API 设计得很好,以至于你的代码只要做很小的改动(甚至不改动),就可以适用于不同的通讯环境。
  在刚才的例子里,有这样的语句 socket.connect(“tcp://127.0.0.1:1234”)。其中的 “tcp://127.0.0.1:1234” 是表示通讯对端的地址串。ZMQ 约定地址串使用如下格式:transport://endpoint 。地址串前面的 transport 表示通讯的类型,目前支持 inproc(进程内),ipc(主机内跨进程),tcp(跨主机),pgm(跨主机,支持多播)共4种方式。
  对程序猿来说,如果你把通讯的地址串保存到配置文件中,就完全可以用一套代码来搞定多种通讯方式,非常爽!

◇支持多种通讯模式
  ZMQ将常见的通讯场景进行了归纳,总结了如下几种不同的模式。
PUB and SUB
REQ and REP
REQ and ROUTER
DEALER and REP
DEALER and ROUTER
DEALER and DEALER
ROUTER and ROUTER
PUSH and PULL
PAIR and PAIR
  限于篇幅,俺就不深入介绍每种模式了,有兴趣的同学请看官方文档(在”这里”)。

★跨语言
  为啥俺要强调跨语言的特色捏?通常来说,用得着网络通讯库的软件系统,某种程度上都算是分布式系统。如果开发的分布式系统比较复杂,要想用一种编程语言通吃,难度较大。因此,在稍微复杂的分布式系统中,采用多种编程语言是常有的事儿(至少俺的经历是如此)。所以,ZMQ 的这个跨语言特色就显得非常重要了。
  在官方网站的文档中,给出了如下许多编程语言的示例(链接在”这里”)。为避免引发编程语言的名次之争,以下按照字母序排列。
Ada, Basic, C#, C, C++, Common Lisp, Erlang, Go, Haskell, Haxe, Java, JavaScript(Node.js), Lua, Objective-C, PHP, Perl, Python, Racket, Ruby, Scala
  这个语言清单太全了,居然有2个语言,俺都没听说过。可以不夸张地说——常用的编程语言,都可以找到相应的 ZMQ 封装库。

★高性能
  说到性能,这可是 ZMQ 吹嘘的主要亮点。首先,ZMQ 是用 C/C++ 开发的(C/C++ 的性能,那可是公认滴);其次,ZMQ 本身的协议格式定义得很简洁(相对来说,JMS 规范中的协议格式就复杂多了)。所以,它的性能远远高于其它的消息队列软件。甚至可以说,用 ZMQ 的性能,跟用传统 socket API 的性能,是不相上下滴。
  为了让大伙儿有一个感性的认识,俺特地找来了消息队列软件的性能测评。这是某老外写的一篇帖子(在”这里”),不懂洋文的同学可以看”这里”。连帖子都懒得看的同学,可以直接看下图。

  从图中可以明显看出,ZMQ 相比其它几款MQ,简直是鹤立鸡群啊!性能根本不在一个档次嘛。

★总结
  总体而言,ZMQ 是非常值得大伙儿去尝试的一个网络通讯库。即使工作中用不到,业余时间玩玩也是不错滴。

libcurl

2013年3月6日

今天来点评一下cURL ,这是一个老资格的开源项目,使用它可以基于多种应用层网络协议进行数据传输(包括上传和下载)。它的特点是:支持的协议多、跨平台、支持多种编程语言接口。后面我会针对这些特点作一些简单的介绍。
  cURL项目实际上包含两个部分:命令行工具和编程用的库(libcurl )。两者支持的功能基本相同。由于开发人员更多地是和libcurl打交道,所以后面我会主要介绍libcurl。

  ★支持多种应用层协议
  多种网络协议支持是cURL的主要卖点。截至到目前的7.19.4版本,它支持的网络协议有:FTP、FTPS、HTTP、HTTPS、SCP (secure copy)、SFTP (SSH FTP)、TFTP (trivial FTP)、TELNET、DICT 、LDAP 、LDAPS和FILE ,够全的吧?
  ◇HTTP
  HTTP估计是最常用的一种协议,我简单说一下cURL对HTTP支持的程度。
  对于协议版本,cURL支持HTTP 1.0和HTTP 1.1。
  对于请求方式:cURL支持GET、POST、PUT、File Upload POST。
  对于代理(Proxy)类型:包括HTTP Proxy、SOCKS4 Proxy、SOCKS5 Proxy。
  另外,还可以设定HTTP认证的用户名口令,cookies,referer URL等许多杂七杂八的东东。
  ◇SSL加密
  假如你要支持某些依赖SSL /TLS 的协议(比如HTTPS、FTPS),则需要用到OpenSSL 库。在cURL的下载页面 上标注有SSL标志的压缩包,都内置了OpenSSL 的动态库。另外,在cURL配置SSL证书的相关说明,可以参见”这里 “。

  ★跨平台
  cURL支持的平台是相当多的。即使是一些冷门的操作系统(比如DOS、OS/2),它也支持得很好。
  另外,cURL官方网站的下载页面 提供了基于不同平台的、编译好的、二进制文件供大伙儿直接使用。对于Linux,它还根据不同厂商、不同发行版本,分别提供二进制文件,考虑相当周到。相比某些开源项目只提供源代码(使用者需要自己动手编译),cURL算是很方便的一个。

  ★多种编程语言支持
  和上次点评的SQLite 一样,libcurl也支持多种编程语言的绑定,而且cURL整合的编程语言比SQLite 还要多。下面列了一些比较常见的编程语言和平台提供的cURL接口。
  ◇C/C++
  cURL本身是C写的,因此C和C++可以直接调用它的C接口API。在cURL的源码包中带有很多C的示例,大伙儿可以依样画葫芦。
  喜欢OO的同学,可以使用cURLpp 提供的C++包装类。这玩意儿使用MIT许可协议。
  ◇Java
  cURL和Java的整合通过JNI实现。可以在”这里 “下载压缩包,然后自己编译出相关的动态库和class文件。懒惰的同学可以到”这里 “捡现成。
  ◇Python
  pycurl 是cURL的Python包装库。如果你觉得Python内置的urllib 功能不够,可以考虑用它。这玩意儿使用双重许可协议:LGPL和MIT/X。
  ◇dotNET
  cURL和dotNET的绑定libcurl.NET 。这玩意儿只支持Win32操作系统。不过不要紧,对于非Windows系统,可以使用cURL的Mono 绑定libcurl.mono 。
  ◇Visual Basic
  cURL和VB的绑定libcurl.vb 。这个项目和上述的libcurl.NET 都是由同一个作者维护的,也都使用MIT许可协议。
  ◇PHP
  PHP要支持cURL相对简单多了。在PHP官方网站 上有相关的安装/配置说明。
  ◇Ruby
  cURL的Ruby的绑定Curb 。这玩意儿使用Ruby许可协议。
  ◇Perl
  cURL和Perl的绑定WWW::Curl::Easy 。这玩意儿使用MPL或MIT/X许可协议。

  ★应用场景举例
  前面说了很多cURL的特点,下面来随手举几个应用的例子。
  ◇传输文件
  如果你需要在程序中进行文件的上传、下载,使用libcurl会非常方便。由于它支持的协议很多。一旦将来你的应用程序发生需求变更,改用其它协议,你的代码也不用大改。
  ◇调用Web接口
  随着SOA风格的流行,很多比较复杂的系统都会提供很多Web API接口。如果你要在程序中调用Web API接口,可以考虑使用libcurl来实现。
  ◇Web测试
  还记得之前善用自动化 的帖子里提到自动测试的好处吗?由于cURL对HTTP的支持很全。在HTTP协议方面,浏览器能干的活它基本上也能干。再加上它可以和很多脚本语言绑定(除了前面提到的,还可以支持Lua、Tcl、Lisp等脚本)。所以你可以用脚本语言+cURL的方式,来进行某些自动化的Web测试。
  比如测试某Web站点的安全性(是否有SQL注入、XSS跨站脚本等安全漏洞)或者测试某Web接口是否符合文档的约定或者测试某些Web接口的性能或者……