[Comet]用Mochiweb打造百万级Comet应用,第一部分

mikel阅读(819)

原文:A Million-user Comet Application with Mochiweb, Part 1

参考资料:Comet–基于 HTTP 长连接、无须在浏览器端安装插件的“服务器推”技术为“Comet”

               MochiWeb–建立轻量级HTTP服务器的Erlang库

 

      在这个系列中,我将详述我所发现的mochiweb是怎样支持那么巨大的网络连接的,为大家展示怎样用mochiweb构建一个comet应用,这个应用 中每个mochiweb连接都被注册到负责为不同用户派送消息的路由器上。最后我们建立一个能够承受百万并发连接的可工作的应用,更重要的我们将知道这样 的应用需要多少内存才能使它跑起来。

 

本部分内容如下:

  • 建立一个基本的comet应用, 它每10秒钟给客户端发送一条消息
  • 调整linux内核参数,使它能够处理大量的TCP连接
  • 写一个能够建立大量网络连接的压力测试工具 (也就是 C10k测试)
  • 检查每个连接到底需要多少内存.

本系列续作将包括怎样建立一个真正的信息路由系统,降低内存使用的技巧,100K和1m并发连接的测试。

基础是你需要知道一些linux命令行操作和一点Erlang知识,否则看不懂别怪我呀,呵呵

写一个Mochiweb测试程序

概括如下:

  1. 安装编译Mochiweb
  2. 运行: /your-mochiweb-path/scripts/new_mochiweb.erl mochiconntest
  3. cd mochiconntest 之后编辑 src/mochiconntest_web.erl

这部分代码(mochiconntest_web.erl)只是接收连接并且每十秒用块传输方式给客户端发送一个初始的欢迎信息。

 

mochiconntest_web.erl

  1. module ( mochiconntest_web) .
  2. export ( [ start/1 , stop/0 , loop/2 ] ) .
  3. %% 外部API
  4. start( Options ) ->
  5.     { DocRoot , Options1 } = get_option( docroot, Options ) ,
  6.     Loop = fun ( Req ) ->
  7.                    ?MODULE :loop ( Req , DocRoot )
  8.            end ,
  9.     % 设置最大连接数为一百万,缺省2048
  10.     mochiweb_http:start ( [ { max, 1000000 } , { name, ?MODULE } , { loop, Loop } | Options1 ] ) .
  11.  
  12. stop( ) ->
  13.     mochiweb_http :stop ( ?MODULE ) .
  14.  
  15. loop( Req , DocRoot ) ->
  16.     "/" ++ Path = Req :get ( path) ,
  17.     case Req :get ( method) of
  18.         Method when Method =:= ‘GET’ ; Method =:= ‘HEAD’ ->
  19.             case Path of
  20.                 "test/" ++ Id ->
  21.                     Response = Req :ok ( { "text/html; charset=utf-8" ,
  22.                                       [ { "Server" ,"Mochiweb-Test" } ] ,
  23.                                       chunked} ) ,
  24.                     Response :write_chunk ( "Mochiconntest welcomes you! Your Id: " ++ Id ++ "\n " ) ,
  25.                     %% router:login(list_to_atom(Id), self()),
  26.                     feed( Response , Id , 1 ) ;
  27.                 _ ->
  28.                     Req :not_found ( )
  29.             end ;
  30.         ‘POST’ ->
  31.             case Path of
  32.                 _ ->
  33.                     Req :not_found ( )
  34.             end ;
  35.         _ ->
  36.             Req :respond ( { 501 , [ ] , [ ] } )
  37.     end .
  38.  
  39. feed( Response , Path , N ) ->
  40.     receive
  41.         %{router_msg, Msg} ->
  42.         %    Html = io_lib:format("Recvd msg #~w: ‘~s’<br/>", [N, Msg]),
  43.         %    Response:write_chunk(Html);
  44.     after 10000 ->
  45.         Msg = io_lib:format ( "Chunk ~w for id ~s\n " , [ N , Path ] ) ,
  46.         Response :write_chunk ( Msg )
  47.     end ,
  48.     feed( Response , Path , N +1 ) .
  49.  
  50. %%内部API
  51. get_option( Option , Options ) ->
  52.     { proplists:get_value ( Option , Options ) , proplists:delete ( Option , Options ) } .

 

启动Mochiweb应用

make && ./start-dev.sh
缺省的Mochiweb在所有网卡接口的8000端口上进行监听,假如是在桌面系统上做这些事,你可以使用任何浏览器访问http://localhost:8000/test/foo 进行测试。
这里只是命令行测试:

$ lynx --source "http://localhost:8000/test/foo"
Mochiconntest welcomes you! Your Id: foo<br/>
Chunk 1 for id foo<br/>
Chunk 2 for id foo<br/>
Chunk 3 for id foo<br/>
^C

是的,它可以工作。 现在,让我们使劲整它,呵呵。

调整linux内核参数,使它能够处理大量的TCP连接

为节省时间我们需要在进行大量并发连接测试之前调整内核的tcp设置参数,否则你的测试将会失败,你将看到大量的Out of socket memory 信息(假如在伪造将得到, nf_conntrack: table full, dropping packet. )

下面的是我用到的sysctl设置 – 你的配置可能不一样,但是大致就是这些:

# General gigabit tuning:
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
net.ipv4.tcp_syncookies = 1
# this gives the kernel more memory for tcp
# which you need with many (100k+) open socket connections
net.ipv4.tcp_mem = 50576   64768   98152
net.core.netdev_max_backlog = 2500
# I was also masquerading the port comet was on, you might not need this
net.ipv4.netfilter.ip_conntrack_max = 1048576

把这些写到 /etc/sysctl.conf中然后运行 sysctl -p 使其生效。不需要重启,现在你的内核能够处理大量的连接了,yay。

建立大量连接

有很多方法可以用. Tsung 就十分好, 也有很多其他比较好的工具如ab, httperf, httpload等等可以生成大量的无用请求。 但是它们中任何一款都不适合测试comet应用, 正好我也想找个借口测试一下Erlang的http客户端, 因此我写了一个基本的测试程序用以发起大量的连接。
只是因为你可以但并不意味着你就这样做.. 一个连接就用一个进程确实有点浪费。我用一个进程从文件中调入一批url链接,另一个进程建立连接并接收数据 (当定时器的进程每10秒打印一份报告)。所有从服务器接收来的数据都被丢弃,但是它增加计数,这样我们能够跟踪到底有多少http数据块被传输了。

floodtest.erl

  1. module ( floodtest) .
  2. export ( [ start/2 , timer/2 , recv/1 ] ) .
  3.  
  4. start( Filename , Wait ) ->
  5.     inets :start ( ) ,
  6.     spawn( ?MODULE , timer, [ 10000 , self( ) ] ) ,
  7.     This = self( ) ,
  8.     spawn( fun( ) -> loadurls ( Filename , fun( U ) -> This ! { loadurl, U } end , Wait ) end ) ,
  9.     recv( { 0 ,0 ,0 } ) .
  10.  
  11. recv( Stats ) ->
  12.     { Active , Closed , Chunks } = Stats ,
  13.     receive
  14.         { stats} -> io :format ( "Stats: ~w\n " ,[ Stats ] )
  15.         after 0 -> noop
  16.     end ,
  17.     receive
  18.         { http,{ _Ref ,stream_start,_X } } ->  recv ( { Active +1 ,Closed ,Chunks } ) ;
  19.         { http,{ _Ref ,stream,_X } } ->          recv ( { Active , Closed , Chunks +1 } ) ;
  20.         { http,{ _Ref ,stream_end,_X } } ->  recv ( { Active -1 , Closed +1 , Chunks } ) ;
  21.         { http,{ _Ref ,{ error,Why } } } ->
  22.             io :format ( "Closed: ~w\n " ,[ Why ] ) ,
  23.             recv( { Active -1 , Closed +1 , Chunks } ) ;
  24.         { loadurl, Url } ->
  25.             http :request ( get, { Url , [ ] } , [ ] , [ { sync, false} , { stream, self} , { version, 1.1 } , { body_format, binary} ] ) ,
  26.                 recv( Stats )
  27.     end .
  28.  
  29. timer( T , Who ) ->
  30.     receive
  31.     after T ->
  32.         Who ! { stats}
  33.     end ,
  34.     timer( T , Who ) .
  35.  
  36. % Read lines from a file with a specified delay between lines:
  37. for_each_line_in_file( Name , Proc , Mode , Accum0 ) ->
  38.     { ok, Device } = file:open ( Name , Mode ) ,
  39.     for_each_line( Device , Proc , Accum0 ) .
  40.  
  41. for_each_line( Device , Proc , Accum ) ->
  42.     case io:get_line ( Device , "" ) of
  43.         eof  -> file :close ( Device ) , Accum ;
  44.         Line -> NewAccum = Proc ( Line , Accum ) ,
  45.                     for_each_line( Device , Proc , NewAccum )
  46.     end .
  47.  
  48. loadurls( Filename , Callback , Wait ) ->
  49.     for_each_line_in_file ( Filename ,
  50.         fun( Line , List ) ->
  51.             Callback ( string:strip ( Line , right, $\n) ) ,
  52.             receive
  53.             after Wait ->
  54.                 noop
  55.             end ,
  56.             List
  57.         end ,
  58.         [ read] , [ ] ) .

每个连接我们都要用一个临时的端口,每个端口也是一个文件描述符, 缺省情况下这被限制为1024。为了避免Too many open files问题出现,你需要为你当前shell更改这个限制 ,可以通过修改/etc/security/limits.conf ,但是这需要注销再登陆。目前你只需要用sudo修改当前shell就可以了(假如你不想运行在root状态下,调用ulimit后请su回非权限用户):

udo bash
# ulimit -n 999999
# erl

你也可以把临时端口的范围区间增到最大:
# echo "1024 65535" > /proc/sys/net/ipv4/ip_local_port_range

为压力测试程序生成一个url列表文件
( for i in `seq 1 10000`; do echo "http://localhost:8000/test/$i" ; done ) > /tmp/mochi-urls.txt

现在在erlang提示符下你可以编译调用floodtest.erl 了:
erl> c(floodtest).
erl> floodtest:start("/tmp/mochi-urls.txt", 100).

这将每秒钟建立十个连接 (也就是每个连接100毫秒).

它将以{Active, Closed, Chunks}的形式输出状态信息 ,Active表示已建立连接数, Closed表示因每种原因被终止的连接数,Chunks是mochiweb以块传输模式处理的数据块数。 Closed应该为0,Chunks应该大于Active,因为每个活跃连接接收多个数据块 (10秒一个)。
10,000个活跃连接的mochiweb进程的固定大小是450MB-也就是每个连接45KB。 CPU占用率就好像预想中的一样微乎其微.

总结

第一次尝试是可以理解的。每个连接45KB内存看起来有些高 – 用libevent再做些调整我可以把它做到将近4.5KB每个连接 (只是猜猜, 谁有这方面的经验请留个回复). 如果就代码量和时间效率上对erlang和c做下考量,我想多花点内存还是有情可原的。
后续中,我将建立一个消息路由器 (我们可以把mochiconntest_web.erl中的 25行和41-43行的注释取消 )也探讨一下减少内存用量的方法。我也会分享当100k和1M个连接时的测试结果。

[C#]小议优化ASP.NET应用性能之Cache篇

mikel阅读(794)

       虽然现在已经是宽带时代,小猫已经离我们渐渐远去,可作为WEB应用开发者来说,我们仍然有责任和义务不断地通过技术手段来优化WEB应用性能,让用户浏览时少一些等待,多一些爽快。
       所 幸的是,ASP.NET作为基于.Net Framework的WEB开发技术,它也享用着.Net Framework的优越性,.Net Framework为我们提供了良好的Cache技术,使我们能开发出速度更快、用户体验更好的WEB应用。命名空间 System.Web.Caching提供了Cache类,其Cache的有效性依赖分以下三种情况:
1. 时间点(指定时间点内有效);
2. KEY值(KEY值作Cache项标识);
3. 文件或目录(指定文件或目录变更,则原Cache项不可用);


       
下面我就结合实际开发的应用跟大家分享一下使用Cache来提高ASP.NET应用的性能。
       我们在开发中常常会遇到读取记录列表(例如最近更新的新闻列表Top N)、记录本身(例如一条新闻),用户访问的时候,这样的信息是否每次都要重复从数据库中读取呢?聪明的你可能知道,这完全是没必要的。

我们为了方便处理,不防设计一个SiteCache类(借鉴了CS中的CSCache.cs),并提供若干静态方法,来负责处理Cache项的添加和删除。下面给出类图:
SiteCache.PNG
代码:


  1using System;
  2using System.Collections;
  3using System.Text.RegularExpressions;
  4using System.Web;
  5using System.Web.Caching;
  6
  7namespace Ycweb.Components
  8{
  9    public class SiteCache
 10    {
 11        private static readonly Cache _cache;
 12        public static readonly int DayFactor;
 13        private static int Factor;
 14        public static readonly int HourFactor;
 15        public static readonly int MinuteFactor;
 16
 17        static SiteCache()
 18        {
 19            DayFactor = 17280;
 20            HourFactor = 720;
 21            MinuteFactor = 12;
 22            Factor = 5;
 23            _cache = HttpRuntime.Cache;
 24        }

 25
 26        private SiteCache()
 27        {
 28        }

 29
 30        public static void Clear()
 31        {
 32            IDictionaryEnumerator enumerator = _cache.GetEnumerator();
 33            while (enumerator.MoveNext())
 34            {
 35                _cache.Remove(enumerator.Key.ToString());
 36            }

 37        }

 38
 39        public static object Get(string key)
 40        {
 41            return _cache[key];
 42        }

 43
 44        public static void Insert(string key, object obj)
 45        {
 46            Insert(key, obj, null1);
 47        }

 48
 49        public static void Insert(string key, object obj, int seconds)
 50        {
 51            Insert(key, obj, null, seconds);
 52        }

 53
 54        public static void Insert(string key, object obj, CacheDependency dep)
 55        {
 56            Insert(key, obj, dep, HourFactor*12);
 57        }

 58
 59        public static void Insert(string key, object obj, int seconds, CacheItemPriority priority)
 60        {
 61            Insert(key, obj, null, seconds, priority);
 62        }

 63
 64        public static void Insert(string key, object obj, CacheDependency dep, int seconds)
 65        {
 66            Insert(key, obj, dep, seconds, CacheItemPriority.Normal);
 67        }

 68
 69        public static void Insert(string key, object obj, CacheDependency dep, int seconds, CacheItemPriority priority)
 70        {
 71            if (obj != null)
 72            {
 73                _cache.Insert(key, obj, dep, DateTime.Now.AddSeconds((double) (Factor*seconds)), TimeSpan.Zero, priority, null);
 74            }

 75        }

 76
 77        public static void Max(string key, object obj)
 78        {
 79            Max(key, obj, null);
 80        }

 81
 82        public static void Max(string key, object obj, CacheDependency dep)
 83        {
 84            if (obj != null)
 85            {
 86                _cache.Insert(key, obj, dep, DateTime.MaxValue, TimeSpan.Zero, CacheItemPriority.AboveNormal, null);
 87            }

 88        }

 89
 90        public static void MicroInsert(string key, object obj, int secondFactor)
 91        {
 92            if (obj != null)
 93            {
 94                _cache.Insert(key, obj, null, DateTime.Now.AddSeconds((double) (Factor*secondFactor)), TimeSpan.Zero);
 95            }

 96        }

 97
 98        public static void Remove(string key)
 99        {
100            _cache.Remove(key);
101        }

102
103        public static void RemoveByPattern(string pattern)
104        {
105            IDictionaryEnumerator enumerator = _cache.GetEnumerator();
106            Regex regex1 = new Regex(pattern, RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.IgnoreCase);
107            while (enumerator.MoveNext())
108            {
109                if (regex1.IsMatch(enumerator.Key.ToString()))
110                {
111                    _cache.Remove(enumerator.Key.ToString());
112                }

113            }

114        }

115
116        public static void ReSetFactor(int cacheFactor)
117        {
118            Factor = cacheFactor;
119        }

120
121
122
123    }

124}

       其实该类主要就是利用前文所提及的关于Cache依赖项的第一点与第二点的特性来维护我们自己的Cache项。
有了SiteCache类,接下来看看如何使用它。还是以读取新闻TonN列表为例:

 1public static RecordSet GetNewsSetTopN(string classCode,int topN,SortPostsBy orderBy, SortOrder sortOrder, string language)
 2{
 3 string cacheKey = string.Format("NewsSetTopN-LG:{0}:CC:{1}:TN:{2}:OB:{3}:SO:{4}", language,classCode,topN.ToString(), orderBy.ToString(),sortOrder.ToString());
 4  
 5 //从上下文中读缓存项
 6 RecordSet newsSet = HttpContext.Current.Items[cacheKey] as RecordSet;
 7 if (newsSet == null)
 8 {
 9  //从HttpRuntime.Cache读缓存项
10  newsSet = SiteCache.Get(cacheKey) as RecordSet;
11  if (newsSet == null)
12  {
13   //直接从数据库从读取
14   CommonDataProvider dp=CommonDataProvider.Instance();
15   newsSet =dp.GetNewsSetTopN(language,classCode,topN,orderBy,sortOrder);
16   //并将结果缓存到HttpRuntime.Cache中
17   SiteCache.Insert(cacheKey, newsSet, 60, CacheItemPriority.Normal);
18  }

19        
20 }

21return newsSet;
22}

 

       这 样在5分钟内就不用重复访问数据库了来读该列表了,当然,也有人会问,如果在这5分钟内某条新闻删除了或修改了怎么办,没关系,我们在删除或修改时可以根 据Cache KEY来强制删除该Cache项,当然,如果你觉得你对列表的时效性不是特别在意,你可以不强制删除该Cache项,让Cache项定义的时间点自动失 效。当然,最好还是提供一个方法按匹配模式项来强行删除Cache项就可以了,例如:

1/// <summary>
2/// 删除匹配的NewsSetTopN列表的Cache项
3/// </summary>

4public static void ClearNewsSetTopNCache(string language,string classCode,int topN)
5{
6 string cacheKey = string.Format("NewsSetTopN-LG:{0}:CC:{1}:TN:{2}",language,classCode,topN.ToString());
7 SiteCache.RemoveByPattern(cacheKey);
8}

9

       发布新闻后调用静态方法ClearNewsSetTopNCache()强行清除原来的TopN缓存项,例如:

 

 1/// <summary>
 2/// 发布(新建)新闻
 3/// </summary>
 4/// <param name="post">新闻实例</param>
 5/// <returns>返回状态</returns>

 6public static int Create(News post)
 7{
 8 int status;
 9 CommonDataProvider dp=CommonDataProvider.Instance();
10 dp.CreateUpdateDeleteNews(post, DataAction.Create, out status);
11 //强制清除匹配的缓存项
12 ClearNewsSetTopNCache (post.Language, post.ClassCode,Globals.GetSiteSetting.NewsListTopN);
13 return status;
14}


That's all.若有不妥之处还望各位同行指正。

[Flex]Flex与JSON及XML的互操作

mikel阅读(709)

Flex之于Java,就像美丽之于大脑,或者还有别的说法?谁能告诉我?我所知道的是,Flex和Java真的是能配合得很好,能创建出难以置信 的富 Internet应用(RIA)。你会问Flex是什么?Flex是一个开源框架,你可以通过基于标签的MXML语言(以及ActionScript 3)来构建Flash应用。

请观看:Jack有关Flex与JSON及XML互操作的演讲 (QuickTime格式,33MB)。

你可以从Adobe的站点下载(http://adobe.com/flex)Flex IDE即所谓Flex Builder,并由此开始你的开发之旅。Flex Builder是个商业产品,但它有很长的免费试用阶段,能让你有足够时间想清楚是不是值得掏这个钱。在这篇文章中,我会演示如何一起使用Flex和 Java。Java会运行在服务器端,而Flex运行在客户端。这两端间的通信协议可以是任何你想要的协议。但在这里,我会先使用XML,然后再使用 JSON,因为这两种技术是我们在Web 2.0的世界里最常见的。

创建服务器代码

XML示例由列表1中显示的简单JSP文件开始:

 列表1. xml.jsp
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="1.2">
<jsp:directive.page import="java.text.*"/>
<jsp:directive.page import="java.lang.*"/>
<jsp:directive.page contentType="text/xml"/>
<days><jsp:scriptlet>
<![CDATA[
double compa = 1000.0;
double compb = 900.0;
for (int i = 0; i<=30; i++) {
compa += ( Math.random() * 100 ) - 50;
compb += ( Math.random() * 100 ) - 50;
]]>
</jsp:scriptlet>
<day>
<num><jsp:expression>i</jsp:expression></num>
<compa><jsp:expression>compa</jsp:expression></compa>
<compb><jsp:expression>compb</jsp:expression></compb>
</day>
<jsp:scriptlet>
<![CDATA[ }
]]>
</jsp:scriptlet>
</days>
</jsp:root>

这个服务会每三十天为两家公司(compa和compb)导出一些随机的股票数据。第一家公司的数值从1000美元开始,第二家从900美元开始,而JSP代码会每天为这两个数值增加一个随机数。

当我从命令行使用curl客户端去访问这个服务时,我获得的是下面这样的结果:

 % curl "http://localhost:8080/jsp-examples/flexds/xml.jsp"
<days><day><num>0</num><compa>966.429108587301</compa>
<compb>920.7133933216961</compb>
</day>...</days>

根标签是<days>标签,它包含了一个<day>标签的集合。每个<day>标签都 有一个<num>标签来表示天数,一个<compa>值来表示公司A的股票价格,以及<compb>值来表示公司B 的股票价格。两只股票的数值随着每次请求而不同,因为它们是随机生成的。

构建界面

现在我们已经有了一个web服务来输出股票的价格,我们还需要一个客户端应用来展现它。我们要构建的第一个界面是表格风格的界面,用它来简单的显示 数字。为了创建Flex项目,我们在Flex Builder IDE的新建菜单中选择Flex Project。显示如图1:

图1. 新Flex项目对话框

在这我们要做的就是给项目起个名字。我把它叫做xmldg,意思是XML数据表格。这样就会创建出一个名叫xmldg.mxml的文件,其中只包含一个空白标签。下面我会使用列表2中的代码来代替这个空白标签。

列表2. xmldg.mxml
<?xml version="1.0" encoding="utf-8"?>

<mx:Application  xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical">
<mx:XML source="http://localhost:8080/jsp-examples/flexds/xml.jsp" id="stockData" />
<mx:Panel title="Stock Data" width="100%" height="100%">
<mx:DataGrid dataProvider="{stockData..day}" width="100%" height="100%">
<mx:columns>
<mx:DataGridColumn dataField="compa" />
<mx:DataGridColumn dataField="compb" />
</mx:columns>
</mx:DataGrid>
</mx:Panel>
</mx:Application>

xmldg应用程序代码有两个主要的组件。第一个是<mx:XML>标签,它告诉Flex这是个XML数据源,并提供了URL。这样就会创建一个叫做stockData(由id属性指定)的局部变量,而<mx:DataGrid>组件可以把它当作dataProvider来使用。

代码的剩余部分就是界面了。<mx:Panel>对象为表格提供了一个简洁的包装。而<mx:DataGrid>用来显示数据。在<mx:DataGrid>中,是一串<mx:DataGridColumn>对象,来告诉表格显示什么数据。

如果我们从Flex Builder运行这个界面,你就会看到像图2的这个样子:

图2. xmldg应用运行界面

我们可以拉动滚动条,改变窗口大小,并且看到数据表格也会改变大小。如果需要添加一点过滤的功能,我们就需要使用<mx:HSlider>控件来更新代码,为它添加一个水平的滑块,来指定表格从哪一天开始显示数据。

比如,如果我们设置滑块到6,它就会只显示从第六天开始的数据。代码如列表3所示:

列表3. xmldg2.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical">
<mx:XML source="http://localhost:8080/jsp-examples/flexds/xml.jsp" id="stockData" />
<mx:Panel title="Stock Data" width="100%" height="100%" layout="vertical"
paddingBottom="10" paddingLeft="10" paddingRight="10" paddingTop="10">
<mx:HBox>
<mx:Label text="Start Day" />
<mx:HSlider minimum="0" maximum="30" id="dayslider" snapInterval="1" />
</mx:HBox>
<mx:DataGrid dataProvider="{stockData..day.(num >= daySlider.value )}" width="100%" height="100%">
<mx:columns>
<mx:DataGridColumn dataField="num" headerText="day" />
<mx:DataGridColumn dataField=="compa" headerText="Company A" />
<mx:DataGridColumn dataField=="compb" headerText="Company B" />
</mx:columns>
</mx:DataGrid>
</mx:Panel>
</mx:Application>

还有其他的一些标签,但规则基本上还是一样的。<mx:Panel>标签可以包含所有内容。其中可以是<mx:HBox> (水平 格)标签,并且box还包含着<mx:Label>和<mx:HSlider>控件。slider用 于<mx:DataGrid>的dataProvider字段。

让我们来更进一步看看dataProvider属性:

{stockData..day.(num >= daySlider.value )}

这里使用的是ActionScript的E4X语法来减少<mx:DataGrid>控件的数据集合,使其只包含那些<num>值大于或等于滑块值的标签。Flex非常智能,它能观察到滑块的变化事件,并自动更新数据表格。

当我们从Flex Builder运行这个界面时,它看起来就像是图3这样:

图3. 可过滤性网格

我们可以调整滑块的位置,并查看到表格中的数据如何变化。图4显示的是我把滑块设到12时的样子:

图4. 滑块设为12时的显示界面

这只是个使用ActionScript中E4X的简单例子。E4X语法使得处理XML变得非常容易,以至于你不会再愿意使用任何其他办法来处理XML了。

画图表

数据表格有点让人厌倦了,至少对我来说是这样。我喜欢有图像的。那么让我们来干点什么——在界面上放置一张图表。我们创建了一个新的名叫xmlgph(意思是XML图表)的项目,并用列表4中的代码来代替自动生成的xmlgph.xml文件。

列表4. xmlgph.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical">
<mx:XML source="http://localhost:8080/jsp-examples/flexds/xml.jsp" id="stockData" />
<mx:Panel title="Stock Data" width="100%" height="100%" layout="vertical"
paddingBottom="10" paddingLeft="10" paddingRight="10" paddingTop="10">
<mx:HBox>
<mx:Label text="Start Day" />
<mx:HSlider? minimum="0" maximum="30" id="dayslider" snapInterval="1" />
</mx:HBox>
<mx:LineChart id="chart" dataProvider="{stockData..day.(num >= daySlider.value )}"
width="100%" height="100%">
<mx:series>
<mx:LineSeries xField="num" yField="compa" displayName="Company A" />
<mx:LineSeries xField="num" yField="compb" displayName="Company B" />
</mx:series>
</mx:LineChart>
<mx:Legend dataProvider="{chart}" />
 </mx:Panel>
</mx:Application>

代码就跟xmldb2一样,但<mx:LineChart>控件替代了<mx:DataGrid>控件,用来显示一张数值 图表, 而不是一个表格。另外还有个<mx:Legend>控件来显示不同颜色线条代表的公司名称。而两 个<mx:LineSeries>对象就类似于<mx:DataGridColumn>的功能。它们让线性图表知道在哪个轴上显 示什么数据。

当我们从Flex Builder运行这个界面是,看到的会是图5这个样子:

图5. 线形图例

还不错吧?因为<mx:HSlider>控件还在那里,所以我们可以移动滑块的位置来改变图表的起始日期。

事实上,只需要一点点小的改变,我们就可以为用户在滑块上提供两个滑动杆,这样它们就能独立移动来让这个图表只显示一段日期内的数据。代码显示如列表5所示:

列表5. xmlgph2.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical">
<mx:XML source="http://localhost:8080/jsp-examples/flexds/xml.jsp" id="stockData " />
<mx:Panel title="Stock Data " width="100% " height="100% " layout="vertical "
paddingBottom="10 " paddingLeft="10 " paddingRight="10 " paddingTop="10 ">
<mx:HBox>
<mx:Label text="Date Range " />
<mx:HSlider minimum="0 " maximum="30 " id="daySlider " snapInterval="1 "
thumbCount="2 " values="[0,30] " />
</mx:HBox>
<mx:LineChart id="chart"
dataProvider="{stockData..day.(num>=daySlider.values[0] &&
num<=daySlider.values[1])}"
width="100%" height="100%">
<mx:series>
<mx:LineSeries xField="num" yField="compa" displayName="Company A" />
<mx:LineSeries xField="num" yField="compb" displayName="Company B" />
</mx:series>
</mx:LineChart>
<mx:Legend dataProvider="{chart}" />
 </mx:Panel>
</mx:Application>

我们需要做的就是为<mx:HSlider>标签添加thumbCount和values属性,并更 新<mx:DataGrid>标签中的dataProvider。因为这是段XML,我必须对dataProvider中的部分实体进行编 码。如果从Flex Builder运行这段代码,我们会看到图6显示的那样:

图6.窗口型线形图

以上这些就是范例演示的XML部分。下面开始我会演示如何构建一个能调用JSON服务的Flex应用程序。

构建JSON服务器

我们由创建一个JSON数据源作为开端,来创建JSON阅读应用程序。同样,我们还是使用可靠的JSP来给构建JSON编码的数据流。这段服务器上的JSP代码显示如列表6:

 列表6. json.jsp
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="1.2">
<jsp:directive.page import="java.text.*"/>
<jsp:directive.page import="java.lang.*"/>
<jsp:directive.page contentType="text/json"/>
[<jsp:scriptlet>
<![CDATA[
double compa = 1000.0;
double compb = 900.0;
for (int i = 0; i<=30; i++) {
compa += ( Math.random() * 100 ) - 50;
compb += ( Math.random() * 100 ) - 50;
if ( i > 0 ) out.print( "," );
]]> </jsp:scriptlet>{"compa":<jsp:expression>compa</jsp:expression>,"compb":<jsp:expres
sion>compb</jsp:expression>}<jsp:scriptlet>
<![CDATA[ }
]]>
</jsp:scriptlet>]
</jsp:root>

这就跟XML服务一样,但我们创建的不是XML标签,而是JSON编码的数据。

当我从命令行运行curl时,得到的页面如下所示:

 % curl "http://localhost:8080/jsp-examples/flexds/json.jsp"
[{"compa":992.2139849199265,"compb":939.89135379532}, ...]

而这恰恰是JavaScript客户端能够理解的东西。

使用JSON服务

Flex是用Flash播放器的编程语言ActionScript 3编写的。它和JavaScript很类似,但它没有eval方法。那么我们如何将JSON文本转换成ActionScript数据呢?幸运的是,免费的ActionScript 3核心库(http://as3corelib.googlecode.com)包含了JSON解码器和JSON编码器。

列表7中的代码演示了JSONDecoder对象的用法:

列表7. jsondg.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical"
creationComplete="jsonservice.send()">
<mx:Script>
<![CDATA[
import mx.rpc.events.ResultEvent;
import com.adobe.serialization.json.JSONDecoder;
private function onJSONResult( event:ResultEvent ) : void {
var data:String = event.result.toString();
data = data.replace( /\s/g, '' );
var jd:JSONDecoder = new JSONDecoder( data );
dg.dataProvider = jd.getValue();
}
]]>
</mx:Script>
<mx:HTTPService id="jsonservice"
url="http://localhost:8080/jsp-examples/flexds/json.jsp"
resultFormat="text" result="onJSONResult(event)" />
<mx:Panel title="Stock Data " width="100% " height="100% ">
<mx:DataGrid id="dg" width="100%" height="100%">
<mx:columns>
<mx:DataGridColumn dataField="compa " />
<mx:DataGridColumn dataField="compb " />
 </mx:columns>
</mx:DataGrid>
</mx:Panel>
</mx:Application>

因为服务器返回的是JSON文本,我们无法使用<mx:XML>标签来取得数据。因此我们用的 是<mx:HTTPService>标签。它的工作原理跟<mx:XML>很像。你需要给它一个服务的URL,并且告诉它结果的 格式(比如文本)以及HTTP服务发回响应数据时需要调用的ActionScript方法。

在这个例子中,我为结果处理方法指定的是在<mx:Script>标签中定义的onJSONResult方法。这个方法会去掉所有空 格,并把 JSON文本传递给JSONDecoder对象。接着它将<mx:DataGrid>控件的dataProvider设置成 JSONDecoder返回的处理结果。

所有这些都是安全的,因为ActionScript不支持eval方法。JSONDecoder类是个简单状态机解析器,来实时地从文本构建出对象。最糟糕的情况可能是这样的过程会需要一段比较长的时间,如果JSON文本太大的话。

下面干什么

Flex是基于Flash的,而Flash可以跟任何技术进行交互。它可以直接与基于SOAP的web服务交互。它甚至能跟AMF(Adobe Message Format)这样的协议进行二进制数据的通信。

如果这是你第一次使用Flex,你可能会想着如何用Flex来构建一个Flash小部件,放到自己的网站上以更吸引人的方式来显示数据。为了确保 Flash应用的尺寸足够小方便下载,记得一定要使用新版本Flash播放器中的运行时共享库(Runtime Shared Library,RSL)。这可以让你在客户端缓存大尺寸的库(比如Flex库),并在不同的Flash应用中重用这些库。

Flex和Java是一个强大的组合。Java提供了优秀的的服务器后端支持。而Flex和ActionScript 3提供的是一个易于编写和采用的通用跨平台的GUI层。

查看英文原文:Flex for XML and JSON

[Tool]Code2Plan:免费的Visual Studio敏捷项目管理插件

mikel阅读(786)

code2plan是由Jesse Johnston和Denis Morozov创建的一个敏捷软件项目管理工具,作为一款Visual Studio插件,其beta版已经免费发布了。该工具还能以独立应用的方式运行,可用来跟踪项目、迭代、用户故事、测试、缺陷及构建。

敏捷的软件项目管理正变得越来越流行,因此你也不会对另一个敏捷工具的出现而大惊小怪。该工具吸引人的地方在于其与Visual Studio 2008的集成,但它还能以独立应用的方式运行。要想运行该工具则需要.NET Framework 3.5 SP1,如果没有的话,那么在安装该工具时会自动安装。

当前code2plan会跟踪如下内容:项目、迭代、用户故事、特性、测试、缺陷及构建。该工具还可以通过跟踪阶段(而不是迭代)及需求(而不是故事)管理传统项目。它能够为这两类项目创建燃尽图。

code2plan可以使用自己的数据库,也能连接到团队的数据库,它同时支持SQL 2005及2008。通过使用团队的数据库,团队成员能共享项目并对其进行编辑。团队中的每个成员都可以更新当前视图以及时查看其它成员的修改。我们可以 离线编辑项目,一旦连接到网络上,这些改变就会自动地与团队进行同步。它对于团队成员的数量没有限制。

code2plan是免费的,也不需要注册和license。这个二人组打算将来发布一个功能更多的应用——很可能不再免费了,但他们承诺其基础版会一直免费下去。该项目并不开源,也不打算开源。不久他们会发布一个SDK,其他开发者可以借助其API来增强该工具。

[C#]线程池 And 线程池的使用(基于.net平台)

mikel阅读(876)

多线程可以提高应用程序的效率,这是肯定的,但是,效率是不是最优的呢,是不是觉得多线程很复杂呢?
    前面学习线程的知道,用多线程需要CreateThread创建线程,还要关闭线程。另外,多线程有时候还要对资源进行同步,也就是说,需要用到事件,信标,互斥对象。
    当然,线程与进程比较,无论速度,对资源的访问,安全性上面线程都有非常大的优势。但是,创建与销毁线程并不是免费的。
    要创建一个线程,需要分配和初始化一个内核对象,也需要分配和初始化线程的堆栈空间,而且 Windows® 为进程中的每个 DLL 发送一个 DLL_THREAD_ATTACH 通知,使磁盘中的页分配到内存中,从而执行代码。当线程终止时,给每个 DLL 都发送一个 DLL_THREAD_DETACH 通知,线程的堆栈空间被释放,内核对象亦被释放(如果其使用数达到 0)。因此,与创建和销毁线程相关的许多开销都和创建线程原本要执行的工作无关。
    为了更优化效率,Windows提供了线程池的概念。
    线程池使创建,管理与撤销线程变得更容易。
    我觉得,线程池可以:
    *初始化线程,动态得创建线程。
    *预分配线程池的内存空间
    *优先级的排队
    *管理线程,撤销线程。
    Jeffer Richter在"CLR线程池"文章中讲到CLR中线程的特性:
    当 CLR 初始化时,其线程池中不含有线程。当应用程序要创建线程来执行任务时,该应用程序应请求线程池线程来执行任务。线程池知道后将创建一个初始线程。该新线程 经历的初始化和其他线程一样;但是任务完成后,该线程不会自行销毁。相反,它会以挂起状态返回线程池。如果应用程序再次向线程池发出请求,那么这个挂起的 线程将激活并执行任务,而不会创建新线程。这节约了很多开销。只要线程池中应用程序任务的排队速度低于一个线程处理每项任务的速度,那么就可以反复重用同一线程,从而在应用程序生存期内节约大量开销。

    那么,如果线程池中应用程序任务排队的速度超过一个线程处理任务的速度,则线程池将创建额外的线程。当然,创建新线程确实会产生额外开销,但应用程序在其 生存期中很可能只请求几个线程来处理交给它的所有任务。因此,总体来说,通过使用线程池可以提高应用程序的性能。

    现在您可能想知道,如果线程池包含许多线程而应用程序的工作负荷又在减少,将会发生什么事情。这种情况下,线程池包含几个长期挂起的线程,浪费着操作系统 的资源。Microsoft 也考虑到了这个问题。当线程池线程自身挂起时,它等待 40 秒钟。如果 40 秒过去后线程无事可做,则该线程将激活并自行销毁,释放出它使用的全部操作系统资源(堆栈、内核对象,等等)。同时,激活并自行销毁线程可能并不影响应用 程序的性能,因为应用程序做的事情毕竟不是太多,否则就会恢复执行该线程。顺便说一句,尽管我说线程池中的线程是在 40 秒内自行激活的,但实际上这个时间并没有验证并可以改变。

    线程池的一个绝妙特性是:它是启发式的。如果您的应用程序需要执行很多任务,那么线程池将创建更多的线程。如果您的应用程序的工作负载逐渐减少,那么线程池线程将自行终止。线程池的算法确保它仅包含置于其上的工作负荷所需要的线程数!


    线程池可以提供的功能:
    1.异步调用功能:一般我们调用函数都是同步的,即函数返回后,才执行下一句代码。但是,用线程池可以异步的调用我们的函数,调用后,马上执行下一句,至于线程的什么时候返回,是主线程所不知道的。
    另外,请注意,永远不要调用任何可以自己创建线程的方法; 如果需要,CLR 的线程池将自动创建线程,如果可能还将重用现有的线程。另外,线程处理回调方法后不会立即销毁该线程;它将返回到线程池并准备处理队列中的其他工作项。使 用System.Theading.TheadPool中的QueueUserWorkItem 会使您的应用程序更有效,因为您将不需要为每个客户端请求创建和销毁线程。
        例:下面写了一个线程池来实现异步调用的方法:
       

using System;
using System.Collections;
using System.Threading;

public class MyClass
{
    
public static void Main()
    
{
        Console.WriteLine(
"Main Thread:Queuing an aynchronous operation");
        ThreadPool.QueueUserWorkItem(
new System.Threading.WaitCallback(MyAsyncOperation));
        
        Console.WriteLine(
"Main Thread:Performing other operation");
        Console.WriteLine(
"Main thread: Pausing to simulate doing other operations.");
        Console.ReadLine();
    }

    
    
static void MyAsyncOperation(object state)
    
{
        Console.WriteLine(
"ThreadPool thread:Perform aynchronous operation");
        Thread.Sleep(
5000);
    }

}

    知道它的结果是什么吗?如果是传统的调用MyAsyncOperation函数,可以肯定得出结论,是输出:
    Main Thread:Queuing an aynchronous operation
    ThreadPool thread:Perform aynchronous operat
    Main Thread:Performing other operation
    Main thread: Pausing to simulate doing other operations
    但是,用了线程池,创建一个线程来执行这个函数,结果却大不一样。以下是执行结果的截图:
   

2.以一定的时间间隔调用方法:
    如果应用程序需要在某个时间执行某项任务,或者定时执行某个任务。那么,就使用线程池吧。
    System.Threading.Timer类可以为你构造这样的功能。函数原型如下:
   

public Timer(TimerCallback callback, Object state,   Int32 dueTime, Int32 period);
   public Timer(TimerCallback callback, Object state,   UInt32 dueTime, UInt32 period);
   public Timer(TimerCallback callback, Object state,   Int64 dueTime, Int64 period);
   public Timer(TimerCallback callback, Object state,   Timespan dueTime, TimeSpan period); 

    用户定义的线程函数可以这样定义:

public delegate void TimerCallback(Object state);

    我们写一下让线程池线程立即调用一个方法,并且每隔 2000 毫秒(或两秒)再次调用的应用程序。
    以下是程序:
   

using System;
using System.Collections;
using System.Threading;

public class MyClass
{
    
static int Times=0;
    
public static void Main()
    
{
        Console.WriteLine(
"Checking for status updates every 2 seconds.");
        Console.WriteLine(
"Hit Enter to terminate the sample");
        Timer timer    
=    new Timer(new TimerCallback(CheckStatus),"Timeing",0,2000);
        
        Console.ReadLine();
    }

    
    
static void CheckStatus(object state)
    
{
        Console.WriteLine(
"Checking Status:"+Convert.ToString(state)+" "+(Times++).ToString()+"'s");
    }

}

    以下是输出的截图:
   
3.当单个内核对象得到信号通知时调用方法
    Jeffer Richter文章中说到:

    Microsoft 研究人员在做性能研究时发现,许多应用程序生成线程,只是为了等待某单个内核对象得到信号通知。一旦该对象得到信号通知,这个线程就将某种通知发送给另一 个线程,然后环回,等待该对象再次发出信号。有些开发人员编写的代码中甚至有几个线程,而每个线程都在等待一个对象。这是系统资源的巨大浪费。因此,如果 当前您的应用程序中有多个线程在等待单个内核对象得到信号通知,那么线程池仍将是您提高应用程序性能的最佳资源。
    
    至于它应该怎么使用呢?
    要让线程池线程在内核对象得到信号通知时调用您的回调方法,您可以再次利用 System.Threading.ThreadPool 类中定义的一些静态方法。要让线程池线程在内核对象得到信号通知时调用方法,您的代码必须调用一个重载的 RegisterWaitHandle 方法
      它的原型如下:

public static RegisterWaitHandle RegisterWaitForSingleObject(
   WaitHandle h, WaitOrTimerCallback callback, Object state, 
   UInt32 milliseconds, Boolean executeOnlyOnce);

public static RegisterWaitHandle RegisterWaitForSingleObject(
   WaitHandle h, WaitOrTimerCallback callback, Object state, 
   Int32 milliseconds, Boolean executeOnlyOnce);

public static RegisterWaitHandle RegisterWaitForSingleObject(
   WaitHandle h, WaitOrTimerCallback callback, Object state, 
   TimeSpan milliseconds, Boolean executeOnlyOnce);

public static RegisterWaitHandle RegisterWaitForSingleObject(
   WaitHandle h, WaitOrTimerCallback callback, Object state,
   Int64 milliseconds, Boolean executeOnlyOnce); 

    第一个参数h表示你要等待的内核对象,第二个参数callback表示调用的用户线程函数,第三参数state是传递给用户线程函数的参数,第四个参数 milliseconds表示线程池内核对象得到信号通知前应该等待的时间,通常传递-1(就跟前面提到的函数WaitForSingleObject第 二个参数作用相同),表示无限超时。第五个参数executeOnlyOnce 为真,那么线程池线程将仅执行回调方法一次。但是,如果 executeOnlyOnce 为假,那么线程池线程将在内核对象每次得到信号通知时执行回调方法。
    客户端定义的函数原型:

public delegate void WaitOrTimerCallback(Object state,Boolean timedOut);

    当调用回调方法时,会传递给它状态数据和 Boolean 值 timedOut。如果 timedOut 为假,则该方法知道它被调用的原因是内核对象得到信号通知。如果 timedOut 为真,则该方法知道它被调用的原因是内核对象在指定时间内没有得到信号通知。回调方法应该执行所有必需的操作。
    看具体的代码:

using System;
using System.Collections;
using System.Threading;

public class MyClass
{
    
public static void Main()
    
{
        AutoResetEvent are 
= new AutoResetEvent(false); //自动事件对象
        RegisteredWaitHandle rwh = ThreadPool.RegisterWaitForSingleObject(
             are, 
new WaitOrTimerCallback(EventSignalled), null1false);
          
for (Int32 x = 0 ; x < 5; x++
        
{
         Thread.Sleep(
5000);
         are.Set();
          }


          rwh.Unregister(
null);
          Console.WriteLine(
"Hit Enter to terminate the sample");
          Console.ReadLine();

    }

    
    
static void EventSignalled(object state,Boolean timedOut)
    
{
        
if (timedOut) 
             Console.WriteLine(
"Timed-out while waiting for the AutoResetEvent.");
           
else 
             Console.WriteLine(
"The AutoResetEvent became signalled.");
    }

}

执行如图所示:
   
Jeff的原文:http://blog.chinaunix.net/article.php?articleId=43400&blogId=5958

[团队管理]敏捷团队当如群鸟飞

mikel阅读(863)

Jurgen Appelo上个月发表了一篇博客“敏捷团队当如群鸟飞”,他写到,“在敏捷软件开发中不应该限定规则,只应当做基本的限制”。
他开篇提出:

在组织中,人们总是试图通过引入某些规则来解决问题,例如,“在X情况下,你必须要做Y”。

我认为这绝不是最好的方式。规则应该是留给团队自己决定,你只需要设定一些限制就行了。

紧接着,他举了一个在计算机上为鸟群行为建模的例子,模拟鸟群的行为非常简单,只需要三条基本限制:

  1. 不许离群
  2. 不许相撞
  3. 往一个方向飞

Jurgen Appelo认为,鸟群的行为可以很容易映射到软件开发团队上来:

  1. 不要把自己孤立
  2. 不要跟其他人打架
  3. 与团队的方向保持一致

自然,这样几条简简单单的限制是无法保证能把事情做好的,但是给团队制定规则就能管用么?敏捷宣言中提到过

鼓舞起每个人的积极性,以个人为中心构建项目,提供所需的环境、支持与信任。

最好的架构、需求和设计出自于自组织的团队。

团队会定期就如何更有效的工作进行回顾,继而调整行为。

那么,你有没有因为担心组织或者团队出现混乱,而从外部强加过多约束呢?

Jurgen Appelo接着说道:

在管理软件项目的时候,敏捷软件开发是一种很自然而然的方式。它设置了一些限制,如“跟客户协作”、“允许频繁的变化”、“只交付可以工作的成品”,剩下的规则就由团队自己选择。

……

这也表明,敏捷软件开发并不是天生就代表了结对编程、TDD、迭代……(注意,敏捷宣言根本没提到这些!)当然,这些实践很不错,但你要是想把它们当做固定规则来实施,你就……

当然,也就失去了敏捷的能力。

各位读者朋友,你对团队建设、团队管理持有何种态度呢?你有没有想办法组建自组织的团队?当团队能力和工作态度没有满足你的预期时,你采用了什么方式来提高生产效率,转变大家的心态?欢迎留下评论,与大家分享经验。

如果你有切实行之有效的实践经验,也欢迎为InfoQ中文站投稿,请mail至lijian[at]cn.infoq.com。

[C#]web.config 中SessionState的配置

mikel阅读(1145)

web Form 网页是基于HTTP的,它们没有状态, 这意味着它们不知道所有的请求是否来自
同一台客户端计算机,网页是受到了破坏,以及是否得到了刷新,这样就可能造成信息的
丢失。 于是, 状态管理就成了开发网络应用程序的一个实实在在的问题。
    在ASP中能够通过Cookie 、查询字符串、 应用程序、会话(Session) 等轻易解决这些问题。
现在在ASP.NET环境中,我们依然可以使用这些功能,并且功能更加强大。
  状态管理分为服务端和客户端两种情况, 这里只是介绍 服务端状态管理:

   与Application对象不同的是, ASP.NET 的Session对象可以在IIS服务器或者工作进程重新启动时
恢复启动前的状态而不丢失其中的数据。这是因为存储在Session中的所以信息都缺省的存储在
一个作为Windows服务运行的状态服务器进程中。状态可以被序列化并以二进制形式保存在内存中。
程序员可以悬着使用Microsoft SQL server数据库来存储数据。

 状态服务器服务和状态信息可以和web应用程序一起存在于同一台服务器上,也可以保存到外部的
状态服务器上。 为了指定如何存储信息,程序员可以在web.config文件中编写适当的配置。

  ASP.NET会话状态模块在Web.config文件中<System.web>标记下的<Sessionstate>标记的mode属性来决定
该属性的四种可能的值: Off、 Inproc StateServer 和SQLServer
  
 1  Inproc是缺省的设置
 
它允许“无Cookie”的会话,以及在服务器之外存储
会话数据。ASP.NET会话状态模块在Web.config文件中像下面这样配置:

<sessionState mode="InProc" cookieless="false" timeout="20" />

在这个例子中,mode属性设为InProc(默认值),表明会话状态要由ASP.NET存储到内存中,而且
不用Cookie来传递会话ID。相反,会话ID要直接插入一个网页URL的查询字符串中。例如,采用
InProc模式并建立一个会话之后,调用一个假想的ASP.NET网页时,需要采用下面这样的URL:

http://my.website.com/(12mfju55vgblubjlwsi4dgjq)/education.aspx

圆括号中长长的字母、数字字符串就是会话ID。ASP.NET引擎从查询字符中提取会话ID,并将用户
请求与特定会话联系起来。采取这种方式,不管Cookie还是隐藏表单字段都用不着了。
所以,即使网页中没有使用表单,也能加入会话。

  但是这种方法,应用程序的状态将依赖于 ASP.NET进程, 当IIS进程崩溃或者正常重启时,保存在
进程中的状态将丢失。

 2 mode属性设为Off
 
   和从前的ASP一样,ASP.NET的会话状态管理是要产生开销的。所以,假如某个网页不需要访问
Session对象,开发者应将那个页的Page预编译指令的EnableSessionState属性设为False。
要为整个网站禁用会话状态,可在Web.config文件中将sessionState元素的mode属性设为Off。

   为了克服inproc 模式的缺点, ASP.NET 提供了两种进程外保存会话状态的方法。

 3 StateServer会话管理

  将mode属性设为StateServer,也就是将会话数据存储到单独的内存缓冲区中,再由单独一台机器上运行

的Windows服务来控制这个缓冲区。状态服务全称是“ASP.NET State Service ”(aspnet_state.exe),

它由Web.config文件中的stateConnectionString属性来配置。该属性指定了服务所在的服务器,以及要监

视的端口:
<sessionState mode="StateServer"
    stateConnectionString="tcpip=myserver:42424"
    cookieless="false" timeout="20" />
  
 在这个例子中,状态服务在一台名为myserver的机器的42424端口(默认端口)运行。要在服务器上改变

端口,可编辑HKLM\SYSTEM\CurrentControlSet\Services\aspnet_state注册表项中的Port值。
 
显然,使用状态服务的优点在于进程隔离,并可在Web farm中共享。 使用这种模式,会话状态的存储将不

依赖于iis进程的失败或者重启,然而,一旦状态服务中止,所有会话数据都会丢失。换言之,状态服务不

SQL Server那样能持久存储数据;它只是将数据存储在内存中。

 4  用SQL Server进行会话管理
 
   ASP.NET还允许将会话数据存储到一个数据库服务器中,方法是将mode属性变成SQLServer
在这种情况下,ASP.NET尝试将会话数据存储到由sqlConnectionString属性(其中包含数据源以及登录服

务器所需的安全凭证)指定的SQL Server中。
为了用恰当的数据库对象来配置SQL erver,管理员还需要创建ASPState数据库,
方法是运行WinDir\Microsoft.Net\Framework\Version文件夹中的InstallState.sql脚本(WinDir是服务

器的Windows文件夹,而Version是你使用的.NET框架版本的安装文件夹)。
要配置SQL服务器,可以在命令行中运行SQL Server 提供的命令行工具osql.exe

osql -S [ server name] -U [user] -P [password] <InstallSqlState.sql
例如
osql -S (local)\NetSDK -U sa -P "" -i InstallSqlState.sql

  在这里用户名必须是SQL服务器上的sa帐号,或者具有同等权限的其他帐号。有兴趣的读者可以打开
这个脚本文件来了解ASP.NET是如何和SQL Server配合实现状态管理的。
 
   卸载这些表和存储过程,可以使用UninstallSqlState.sql脚本,使用方法与上面类似。

做好必要的数据库准备工作后,将web.config 文件中的sessionstate 元素的mode改为"SQLServer"
,并且指定SQL连接字符串。具体如下:

 mode="sqlserver"
 sqlConnectionString="data source=127.0.0.1; userid=sa; password="

配置好SQL Server后,应用程序代码运行时就和InProc模式没有什么区别。但要注意的是,由于数据不存

储在本地内存,所以存储会话状态的对象需要进行序列化和反序列化,以便通过网络传给数据库服务器,

以及从数据库服务器传回。这当然会影响性能。通过在数据库中存储会话状态,可分别针对扩展性及可靠

性来有效地平衡性能。另外,可以利用SQL Server的集群,使状态存储不依赖于单个的SQL Server,
这样就可以为应用程序提供极大限度的可靠性。

[Tool]二次开发WinWebMail邮件系统接口 - 企业邮件服务器解决方案

mikel阅读(874)

WinWebMail,是一个比较轻量级的邮件服务器系统,适用于中小型企业的邮件系统,功能也比较齐全,关于它的详细介绍可参见官网地址:http://www.winwebmail.com/. 从网上去下载一个安装到机器上,我们公司服务器上安装的是WinWebMail 3.7.6.1 企业版,安装的时候它回自动生成网站文件目录,全部都是ASP的页面,在IIS里面创建一个虚拟目录指向该Web文件夹,按照他的一些说明配置好权限等, 这样我们就在网页使用它的邮件服务了.

它的Web客户端比较简洁,不过该有的功能还是都有.第一次用admin进去添加一个域,再 添加到一个用户进去,我们就拥有该域名的邮箱.注意,如果你这台机器并没有独立网络IP地址或者域名没有解析到你这里你也能添加域,比如我添加一个 microsoft.com的域,再到里面添加一个用户jonllen,那我登陆jonllen@microsoft.com用户,我也能以它的用户名发 送出邮件,呵呵,不过对方看到的应该是在垃圾邮件里面,因为现在一般邮件服务器都会判断邮件域名和发送的来源是不是同一个地方,不是同一个地方就认为是伪 造的垃圾邮件,而且对方回复你也不能收到,因为microsoft.com不是解析到你那里,先来看一张WinWebMail登陆后的截图.

WinWebMail

发送邮件就只能在Web页面里面操作是往往不够的.比如系统自动发送邮件、定时群发邮件等这 些操作是不可能再跑到它的页面手工去发送,而应该要能在程序里调用他的接口进行发送邮件,而且最近可能会要开发一套OA,里面就要企业邮件,OA里注册一 个帐户就需要开一个邮件帐户,并且建立企业员工通讯录,邮件信息为内部相互通讯,且也能直接发送到外部邮箱,不过WinWebMail好象就提供接口方式 好象就是Web网页访问的形式而已,不过打开它里面的asp页面,我们能看到里面一些这样的代码.

dim ei
set ei = server.createobject("easymail.InfoList")
username
= Session("wem")
ei.LoadMailBox username,
trim(request("mode"))
'—————————————–

懂asp的朋友都知道,它里面使用server.createobject是创建一个对象, 比如对数据的连接操作都是要用到server.createobject,而asp里面的变量是弱变量类型,变量无须声明就可以使用的,那么上面代码里面 server.createobject("easymail.InfoList")创建的是什么对象呢?突然想起来,WinWebMail它还有个 C/S的客户端界面,但是要到服务器上进去才能看到操作的,功能比较的简单,可以增加用户和配置域,还可以修改一些系统的设置,打开任务管理查看所有进 程,可以看到它其实是就是一个程序,你也可以关闭,但是还有一个名为emsvr的进程,它便就是WebWinWeb邮件系统的后台服务,最核心的地方还是在这里!那么它们之间是怎么实现相互调用的呢?细心的程序员很快就会想到是用COM组件,因为COM组件通过接口能方便的实现相互调用通讯甚至为不同的语言,在VS里面添加引用,选择COM组件,果然有WinWebMail的组件,如下图.

添加COM引用

那么,上面server.createobject的就可以解释为创建调用的COM组件对象 了.既然有COM组件接口可以调用,那么就好办了.因为COM组件接口能跨任何语言调用,如果为标准的DLL组件的话,我们还可以使用非托管动态链接库的 方法进行引用,不过那好象你必须都要知道它的一些方法名和参数等.难道我去它的那些asp页面里一个个找出来不成?而且你没有相关文档还先得要搞清它的整 个页面逻辑,就算你都找到了那也不一定就是你要的方法名,那改如何是好呢?上面不是用VS来引用COM吗?没错!我们在项目里面添加 WebEasyMail的COM组件,VS会帮我们自动生成一个Interop.EASYMAILLib.dll文件到Bin目录,我们使用对象浏览器打 开,它里面的接口属性类一览如余.

COM对象浏览器

我们要操作调用COM接口,那我们调用它生成的类即是,就是这么的简单,非常的方便.写代码 的话我们先实例化它里面的类,我们能看到类里面有很多的方法和属性.刚开始你可能会对它里面的一些属性和方法是干什么的不太明白.但是你可以打开它的 asp页面里的代码,先看下它调用的整个步骤,也许你就有一些思绪了.我为了做得通用,把一些常见的操作都用C#写成了WebService方法.我这里 贴出一个返回用户邮件夹信息(包括名称、总数量大小和新邮件数)的方法.

[WebMethod(Description = "返回邮件箱 包含名称、邮件数和大小等信息")]
[System.Xml.Serialization.XmlInclude(
typeof(MailBox))]
[SoapHeader(
"Usheader", Direction = SoapHeaderDirection.In)]
public MailBox[] GetMailBoxes(string username)
{
InfoListClass infos
= new InfoListClass();
if (!username.Contains("@"))
username
+= System.Configuration.ConfigurationManager.AppSettings["EmailPostfix"];
infos.LoadSizeInfo(username);
System.Collections.Generic.List
<MailBox> list = new System.Collections.Generic.List<MailBox>();
list.Add(
new MailBox("收件箱", "in", infos.inboxMailCount, infos.newInBoxMailCount, infos.inboxMailSize));
list.Add(
new MailBox("草稿箱", "out", infos.outboxMailCount, infos.newOutBoxMailCount, infos.outboxMailSize));
list.Add(
new MailBox("发件箱", "sed", infos.sendboxMailCount, infos.newSendBoxMailCount, infos.sendboxMailSize));
list.Add(
new MailBox("垃圾箱", "del", infos.delboxMailCount, infos.newDelBoxMailCount, infos.delboxMailSize));
for (int i = 0; i < infos.PerFolderCount; i++)
{
MailBox box
= new MailBox();
infos.GetPerFolderInfo(i,
ref box.name, ref box.mailcount, ref box.size, ref box.newmailcount);
box.code
= box.name.ToString();
list.Add(box);
}
list.Add(
new MailBox("合计", "all", infos.allMailCount, infos.allNewMailCount, infos.allMailSize));
return list.ToArray();
}

其他添加域、用户、收发邮件也能以次类推写出来,只不过你可能要参考它asp页面的一些方 法,调用正确才能返回结果.注意:它接口里面没有一个实体对象模型,里面所有的结果都是使用ref来赋值的,取多条结果则是通过for循环,它asp页面 里调用好象也都是这样做的.我的做的时候调试也是比较的郁闷.因为是本地在公司的一个局域网,我用上面写的那些方法竟然都调用不了,也没有报错,就是没有 结果返回,但是进它的asp网站里面操作又都行!害得我郁闷了好久.后来把代码传到服务器上,直接用WebService访问,竟然又行.那证明我写的那 些方法是没有错,那到底是那里有问题了呢?我现在还是不太明白.

WebService方法

不过既然能在服务器上运行就OK了,因为邮件服务器最终都将是要部署在服务器上的,这样我们 就也能把添加用户、企业通讯录、收发邮件的这样方法以WebService的方式提供出来,如果公司还有OA、ERP、人力资源管理系统等都可以方便的调 用,而且能统一域下用户管理,实现企业用户员工的邮件通讯.有需要使用邮件系统的朋友可以考虑使用WinWebMail.我这里提供一个 WinWebMailv3.7.6.1 企业版的破解版下载,仅供非商业用途学习测试使用,感兴趣的朋友可以来下载,正式使用请到官网购买付费版本.

WinWebMailv3.7.6.1 企业破解版下载

[IM]即时通讯调研报告

mikel阅读(1174)

1概述
1.1 IM技术概念
  IM技术全称Instant Messaging,中文翻译“即时通讯”,它是一种使人们能在网上识别在线用户并与他们实时交换消息的技术,是电子邮件发明以来迅速崛起的在线通讯方 式。IM的出现和互联网有着密不可分的关系,IM完全基于TCP/IP网络协议族实现,而TCP/IP协议族则是整个互联网得以实现的技术基础。 最早出现即时通讯协议是IRC(Internet Relay Chat),但是可惜的是它仅能单纯的使用文字、符号的方式通过互联网进行交谈和沟通。随着互连网变得高度发达,即时通讯也变得远不止聊天这么简单,自 1996年第一个IM产品ICQ发明后,IM的技术和功能也开始基本成型,语音、视频、文件共享、短信发送等高级信息交换功能都可以在IM工具上实现,于 是功能强大的IM软件便足以搭建一个完整的通信交流平台。目前最具代表性的几款的IM通讯软件有MSN、Google Talk、Yahoo Messenger、腾讯QQ等。

1.2 IM技术原理和工作方式
  典型的IM工作方式如下:登陆IM通讯中心(IM通讯服务 器),获取一个自建立的历史的交流对象列表(好友列表),然后自身标志为在线状态,当好友列表中的某人在任何时候登录上线并试图通过你的计算机联系你 时,IM系统会发一个消息提醒你,然后你能与他建立一个聊天会话通道进行各种消息如键入文字、通过语音等的交流。

  从技术上来说,IM的基本技术原理如下:

  1.IM服务器

  2.登陆或注销

  3.用户A通过列表找到B,用户B获得的消息并与之交谈

  4.通过IM服务器指引建立与B单独的通讯通道

   第一步,用户A输入自己的用户名和密码登录IM服务器,服务器通过读取用户数据库来验证用户身份,如果验证通过,登记用户A的IP地址、IM客户端软件 的版本号及使用的TCP/UDP端口号,然后返回用户A登录成功的标志,此时用户A在IM系统中的状态为在线(Online Presence)。

   第二步,根据用户A存储在IM服务器上的好友列表(Buddy List),服务器将用户A在线的相关信息发送给也同时在线的IM好友的PC机,这些信息包括在线状态、IP地址、IM客户端使用的TCP端口 (Port)号等,IM好友的客户端收到此信息后将在予以提示。

  第三步是IM服务器把用户A存储在服务器上的好友列表及相关信息回送到他的客户端机,这些信息包括也在线状态、IP地址、IM客户端使用的TCP端口(Port)号等信息,用户A的IM客户端收到后将显示这些好友列表及其在线状态。

1.3 IM通讯方式
1.3.1在线直接通讯
如 果用户A想与他的在线好友用户B聊天,他将直接通过服务器发送过来的用户B的IP地址、TCP端口号等信息,直接向用户B的PC机发出聊天信息,用户B的 IM客户端软件收到后显示在屏幕上,然后用户B再直接回复到用户A的PC机,这样双方的即时文字消息就不再IM服务器中转,而是直接通过网络进行点对点的 通讯,即对等通讯方式(Peer To Peer)。

1.3.2在线代理通讯
用户A与用户B的点对点通讯由于防火墙、网络速度等原因难以建立或者速度很慢,IM服务器将会主动提供消息中转服务,即用户A和用户B的即时消息全部先发送到IM服务器,再由服务器转发给对方。

1.3.3离线代理通讯
用户A与用户B由于各种原因不能同时在线的时候,如此时A向B发送消息,IM服务器可以主动寄存A用户的消息,到B用户下一次登陆的时候,自动将消息转发给B。

1.3.4扩展方式通讯
用户A可以通过IM服务器将信息以扩展的方式传递给B,如短信发送方式发送到B的手机,传真发送方式传递给B的电话机,以email的方式传递给B的电子邮箱等。

早 期的IM系统,在IM客户端和IM服务器之间通讯采用UDP协议,UDP协议是不可靠的传输协议,而在IM客户端之间的直接通讯中,采用具备可靠传输能力 的TCP协议。随着用户需求和技术环境的发展,目前主流的IM系统倾向于在IM客户端之间、IM客户端和IM服务器之间都采用TCP协议。

即时通讯相对于其他通讯方式如电话、传真、email等的最大优势就是消息传达的即时性和精确性,只要消息传递双方均在网络上可以互通,使用即时通讯软件传递消息,传递延时仅为1秒种

1.4兴起的嵌入式IM工具
传 统的IM在统治了互联网即时通讯领域长达十年之久,以其日趋稳定的性能,与较强的用户黏着度,至今仍统治着这个巨大的市场。然而,软件行业的技术精英们, 并不满足于此。他们厚积薄发,一直致力于开发出性能更为优越的即时通讯工具。当然,在功能上的不断完善,自然是一个必然的发展方向,在Web2.0时代, 如何大力增强用户对网站的黏着度,而不仅仅是对于IM的拥附,已经成为他们的主攻方向了。于是,嵌入式IM工具,应运而生了。

相对以往的传 统的即使沟通工具,它们需要用户下载软件包,需要用户进行安装。对于拥有IM产品的网站而言,用户在登陆网站后,不能直接使用其IM工具,对于流量与用户 的黏着度,都是有一定影响的。因此在IM与网站相互依存的今天,没有哪家网络公司,愿意将IM工具孤立开来。

于是,目前,一种新型的嵌入式IM工具就应运而生了。这种IM工具,不需要下载安装,当用户登陆网页后,该IM直接嵌套在网页中,可以直接使用。

而 在功能上,则一点也不输于传统的IM,无论是传统的文字沟通的速度与效率,还是近年来越来越成为IM工具必备的音频/视频功能,这种嵌入式IM都能提供非 常稳定的传输。更值得一提的是,因为嵌入式IM是嵌套在网页上的,软件供应商,可以根据网站需求,设计出适合网站风格的IM产品。而不是像传统的IM工 具,千篇一律,毫无个性可言。

目前,这类嵌入式IM在社区、交友、社团及协作等类型的网站上,应用已经较为广泛。在Web2.0时代,将发挥越来越重要的作用。

2即时通讯协议
IM(Instant Messaging)正在被广泛地采用,特别是在公司与它们的客户互动联接方案上。为了解决即时通讯的标准问题,IETF成立了专门的工作小组,研究和开发与IM相关的协议。
目前IM有四种协议:即时信息和空间协议(IMPP)、空间和即时信息协议(PRIM)、针对即时通讯和空间平衡扩充的进程开始协议SIP(SIMPLE)以及XMPP。PRIM与XMPP、 SIMPLE类似,但已经不再使用了。
2.1 IMPP
IMPP 主要定义必要的协议和数据格式,用来构建一个具有空间接收、发布能力的即时信息系统。到目前为止,这个组织已经出版了三个草案RFC,但主要的有两个:一 个是针对站点空间和即时通讯模型的(RFC 2778);另一个是针对即时通讯/空间协议需求条件的(RFC2779)。RFC2778是一个资料性质的草案,定义了所有presence和IM服务 的原理。RFC2779定义了IMPP的最小需求条件。另外,这个草案还就presence服务定义了一些条款,如运行的命令、信息的格式,以及 presence服务器如何把presence的状态变化通知给客户。

2.2 SIP/SIMPLE
SIMPLE是目前为止制定的 较为完善的一个。SIMPLE和XMPP两个协议,都符合RFC2778和RFC2779 。SIMPLE计划利用SIP来发送presence信息。SIP是IETF中为终端制定的协议。SIP一般考虑用在建立语音通话中,一旦连接以后,依靠 如实时协议(RTP)来进行实际上的语音发送。但SIP不仅仅能被用在语音中,也可以用于视频。SIMPLE被定义为建立一个IM进程的方法。 SIMPLE在2002年夏季得到额外的信任,目前,微软和IBM都致力于在它们的即时通讯系统中实现这个协议。
SIMPLE小组致力于进程模式 的操作,这将提升运行效率,使基于SIP的机制能够进行会议和三方电话交谈控制,也考虑到能和未来提供的许多新特性实现兼容并提升表现能力。有了进程模 式,SIMPLE使用SIP来建立一次进程,再利用SDP(进程描述协议)来实际传输IM数据。
SIMPLE是SIP for Instant Messaging and Presence Leveraging Extensions的缩写, 其目标是将SIP协议应用于IM 和出席检测业务。因为SIP和IMPP所提出的RFC2779中的要求有许多共同之处,加上SIP已受到较广泛的支持而且相对较成熟,故采用SIP实现 IMPP是很自然的选择。
SIP(Session Initiation Protocol)协议是由IETF提出的一种用于IP网络多媒体通信的应用层控制协议,其主要功能是创建、修改、终结和管理多媒体会话或呼叫,SIP协 议的语法和语义在很大程度上借鉴了SMTP和HTTP的机制,使用C/S通信模式以及文本形式的消息编码。本质上,SIP提供以下功能:①名字翻译和用户 定位:无论被呼叫方在哪里都确保呼叫达到被叫方;执行所有描述信息到定位信息的映射;确保呼叫(会话)的本质细节被支持。②特征协商:它允许与呼叫有关的 组在支持的特征上达成一致。③呼叫参与者管理:呼叫中参与者能够引入其他用户加入呼叫或取消到其他用户的连接,支持呼叫转移和呼叫保持。④呼叫特征改变: 用户能够改变呼叫过程中的呼叫特征。例如,某次呼叫开始时被置为"voice2only",但用户可以在呼叫过程中按需开启视频功能,新加入呼叫的第三方 也可以开启不同的特征。
多数即时消息和出席检测架构可以不加修改地利用SIP中已有的特性。例如,一个IM客户机向SIP注册服务器发送 REGISTER消息,通知它可以接收IM,注册服务器采用与SIP系统中其他规则相同的方法来处理登录请求。不过, SIP缺乏消息路由机制,不完全符合IMPP,还需对其进行一定的扩展,目前SIMPLE工作组已经提交了3项RFC和17项草案。SIMPLE在本质上 与SIP相同,没有采用GET和POST等数据存取方法,而采用INV ITE和BYE等信令方法来启动和结束一次呼叫或会话。SIMPLE增加了一种称为MESSAGE的新的请求方法来发送一次性的短消息,即寻呼机模式的 IM;用SUBSCR IBE发送对出席消息的询问,用NOTIFY传输出席消息。在持续较长的IM会话中,参与者在一段时间内交换多条消息,这时就要用到INV ITE信令和一种称为"消息会话中继协议"(MSRP)的传输协议;文本通过MSRP传输,话音和视频数据则和其他SIP应用一样用RTP来传输。
2.3 Jabber/XMPP
XMPP 是一种基于XML的协议,它继承了在XML环境中灵活的发展性。这表明XMPP是可扩展的。可以通过发送扩展的信息来处理用户的需求,以及在XMPP的顶 端建立如内容发布系统和基于地址的服务等应用程序。而且,XMPP包含了针对服务器端的软件协议,使之能与另一个进行通话,这使得开发者更容易建立客户应 用程序或给一个配好系统添加功能。
XMPP目前在免费源代码开放Jabber IM系统中被广泛采用。2002年,这个产品有超过5万的下载量。XMPP拥有成千的Jabber开发者,以及大约数万台配置的服务器和超过百万的终端用户。
在XMPP能够成为标准前,还需要努力克服它本身的缺点。对于网络协同工作者而言,需要加强安全性和互连性。
XMPP(Extensible Messaging and Presence Protocol,可扩展的消息和出席协议)是一种基于XML的传递出席信息(Presence)和消息路由的协议,它为不同的网络之间互联提供了一种安 全而简单的编程语言,是Jabber系统(一种开放源代码的IM系统)的基础,IETF成立了XMPP工作组并已发布了若干项草案。XMPP路由的核心是 一种类似于电子邮件的逻辑编址方案,在Jabber系统中,这一地址被称为Jabber ID,其形式为node@domain/resource。 XMPP的工作方式也与简单邮件传输协议(SMTP)相似,地址中的域(有相应的服务器)可以用普通的DNS系统来解析,由服务器来转发用户的消息。与 SMTP不同的是,节点(node)部分既可以表示用户,也可以表示应用或服务;resource是连接识别标记,允许同一个客户在同一个时刻多次登录服 务器。当节点连接到一台服务器时,它们利用来自本地目录系统的证书进行鉴权、指定资源并通知服务器向订阅者(比如说列在好友名单上的用户)发出出席公告。 服务器之间能相互发现、连接和认证,不管某节点的本地服务器(域)是什么,只要不违反保密或业务规则,它都可以通过服务器的转接而连接到XMPP社区中的 任意其他节点。
每个XMPP节(stanza)都是标有JID地址的XML结构化数据块,节的类型可以通过恰当的命名空间XML结构化数据加以扩 展。正如W3C是Web格式的标准化主体,Jabber软件基金(JSF)是XMPP节类型扩展的标准化主体,不过,任何其他组织都可以按需确定事实上的 标准格式。XMPP将出席(Presence)和相关的消息嵌入XML 结构化数据之中,使之能有效地路由至最合适的资源,起到了通用的XML结构化数据传输层的作用,这种层次结构最大程度地简化了客户端的实现。JSF提供了 到其他IM系统的网关规范,只要网关支持,客户端就可以与其他IM系统交流。

说明:
综合考虑功能、扩展能力和成熟度等各方面特征,两种协议各有特色,其对比如表1 SIMPLE与XMPP协议的比较所示。

表1 SIMPLE与XMPP协议的比较
  SIMPLE XMPP
基础 SIP协议 XML协议
成熟度 较为成熟 新兴技术
功能支持 各种即时消息通信 支持各种即时消息通信
扩展能力 一般 很强
主流厂商的支持 微软、IBM、SUN Oracle、Google
前景 将率先广泛应用 后来居上

SIP 已经在网络电话等领域得到了较广泛的应用,出现了大量的产品和方案,对现有SIP系统进行一定的修改后就能支持SIMPLE , 这是SIMPLE最大的优势。从产业上看,微软已经选择SIP作为其实时通信策略并在WindowsXP,Pocket PC和MSN Messenger中进行了部署, IBM也在大力地推进SIP的应用。微软公司宣布其Live Communications Server 2005将使用户能够与雅虎,AOL的即时通信服务的用户互联互通,进一步增强了SIMPLE作为标准化IM协议的强势地位。
XMPP的优势在于 灵活和开放。它具有XML带来的与生俱来的扩展性,任何人都可以用扩展的XML 信息来处理客户的特殊要求并在XMPP基础上构建新的应用。例如, CRM或别的企业应用系统可以作为一个XMPP的客户端注册到服务器上,在管理员许可的前提下,用户或别的应用能发现CRM的状态并向其发送数据、对其进 行操作。利用XMPP作为通用的传输机制,不同组织内的不同应用都可以进行有效的通信。由于XML很易穿过防火墙,所以用XMPP构建的应用不易受到防火 墙的阻碍。从产业上看, XMPP起源于开放源代码的Jabber系统,现在已有了大量使用该协议的IM系统,目前SUN和Oracle已经在它们的IM系统中使用XMPP,而 Google也可能在其IM系统中使用这一协议。
SIMPLE和XMPP各有其优势,前者有较成熟的应用基础和主流IM服务商的支持,在未来一段 时间内可能会成为主要的标准化IM协议;后者发挥了XML的优势,随着XML的发展,它的应用会越来越普及。在相当的一段时间内仍会出现两种标准系统共存 的状况,所以这两种系统之间的互联就成了一个新的问题。不过, XMPP支持网关扩展,用XMPP2SIP网关可以实现两类系统间的互联。

3 Web IM 实现技术
3.1基于插件的技术
如ActiveX,插件相对稳定,但插件需要用户自己允许并下载安装,而大多数用户担心安装了黑客软件或插件对计算机系统不好而不愿意安装,并且上网助手等软件也拦截插件,导致很多用户无法使用。另外,ActiveX受平台限制,只能在IE下使用。
3.2基于Flash的技术
典型的如Yahoo web messenger,结合Flash和Ajax, 
3.3纯粹的基于HTTP的技术
前端使用Ajax的Web IM:meebo, ebuddy, ILoveIm, MSN Web Messenger, KoolIM等。
后台使用comet的Web IM:meebo, gtalk等。
只有ebuddy支持wap
4服务器端实现方式
  优点 缺点
循 环的面向连接的方式 调试、编程、维护、修改容易 1.服务器一次只能处理一个客户机的请求,可能造成客户端请求的拒绝或响应变慢;2.客户机无法知道服 务器是否收到了其它客户机发给它的信息;3.服务器对客户机的处理是按照连接请求的先后顺序进行,与一个客户机的通讯故障可能对后续客户造成影响。
多进程并发方式 并行化、简单有序、互不干扰、事务化 1.创建进程需要一定的时间开销;2.考虑如何实现多个进程之间的互不干扰;3.占用较多的系统资源
多线程并发方式 除了具有多进程并发方式的优点外,与多进程相比:1.线程节约系统的资源;2.创建时的时间开销小;3.减小了对服务器资源的消耗 1.需要采用同步机制实现多个线程之间的同步与互斥;2.程序的编写、调试、维护难度大
5流行即时通讯系统分析
Web QQ
是国内最为流行的聊天工具QQ的网页式聊天工具,国外没有相关的服务是提供给QQ的,马化腾不失时机地做了基于Web浏览器的IM服务,所谓是web2.0的大势所趋。

功能特点:因为用户群过于庞大,非常有发展潜力。
目前Web QQ同样在测试阶段,只支持IE6.0的浏览器。其在线主界面保持了客户端QQ主界面的风格,在操作上可以使用快捷键;不提供聊天记录保存、语音视频聊天、文件传输等功能,纯属客户端QQ的精简版。
Web MSN
通 过试用发现,在线方式不支持视频聊天,也不能保存聊天记录;在线方式由于精简了功能,使得聊天窗口弹出及具体的文字聊天速率较理想;对浏览器的支持较好, 适用于IE5.0或更高版本、Netscape 7.1 或更高版本、Mozilla1.6或更高版本、Maxthon1.5或更高版本等。
总的说来功能较单一。
即 时消息是Windows Messenger中的一种通信和协作的模式。IM对话中使用的初始化和通信协议依所选择的服务器或服务而定。对于.NET Messenger或Exchange IM,IM文本是通过TCP连接进行传送的。当使用SIP Proxy服务器时,可以对服务器进行配置,以使用TCP UDP或"安全套接层"(Secure Sockets Layer,SSL)–贯穿TCP的安全协议–传输IM文本。
发起对话的客户向服务器发送一个请求,邀请与联系人开始对话。在服务器将邀请发送到联系人后,就可以进行IM通信了。
消 息文本通过服务器发送给对方。消息文本的定义依采用的服务器和协议而不同,通常为HTTP消息,由服务器将其封装在TCP连接中,内部包含"超文本传输协 议"(Hypertext Transfer Protocol,HTTP)或"可扩展标记语言"(Extensible Markup Language,XML)。当使用SIP IM时,可以设置在同级之间直接传送消息文本。但是采用SIP Proxy服务器的话,就需要对服务器进行配置。
Gtalk
在此在线IM方式中,同样不能实现网络通话功能(需通过下载Google Talk 客户端实现);电子邮件和即时聊天在同一页面完成,方便快捷;在浏览器支持方面做得不够,适用于IE 6.0 以上和 Firefox 1.0 以上版本。最有特色的是其聊天记录的保存和直接回复功能。

基于 Iframe 及 htmlfile 的流(streaming)方式
iframe 是很早就存在的一种 HTML 标记, 通过在 HTML 页面里嵌入一个隐蔵帧,然后将这个隐蔵帧的 SRC 属性设为对一个长连接的请求,服务器端就能源源不断地往客户端输入数据。

图 3. 基于流方式的服务器推模型
 
上 节提到的 AJAX 方案是在 JavaScript 里处理 XMLHttpRequest 从服务器取回的数据,然后 JavaScript 可以很方便的去控制 HTML 页面的显示。同样的思路用在 iframe 方案的客户端,iframe 服务器端并不返回直接显示在页面的数据,而是返回对客户端 Javascript 函数的调用,如"<script type="text/javascript">js_func("data from server ")</script>"。服务器端将返回的数据作为客户端 JavaScript 函数的参数传递;客户端浏览器的 Javascript 引擎在收到服务器返回的 JavaScript 调用时就会去执行代码。
从 图 3 可以看到,每次数据传送不会关闭连接,连接只会在通信出现错误时,或是连接重建时关闭(一些防火墙常被设置为丢弃过长的连接, 服务器端可以设置一个超时时间, 超时后通知客户端重新建立连接,并关闭原来的连接)。
使 用 iframe 请求一个长连接有一个很明显的不足之处:IE、Morzilla Firefox 下端的进度栏都会显示加载没有完成,而且 IE 上方的图标会不停的转动,表示加载正在进行。Google 的天才们使用一个称为"htmlfile"的 ActiveX 解决了在 IE 中的加载显示问题,并将这种方法用到了 gmail+gtalk 产品中。Alex Russell 在 "What else is burried down in the depth's of Google's amazing JavaScript?"文章中介绍了这种方法。Zeitoun 网站提供的 comet-iframe.tar.gz,封装了一个基于 iframe 和 htmlfile 的 JavaScript comet 对象,支持 IE、Mozilla Firefox 浏览器,可以作为参考。(请参见 参考资源)

sigslot是一个线程安全、类型安全,用C++实现的sig/slot机制(sig/slot机制就是对象之间发送和接收消息的机制)的开源代码库

网易POPO
从登录速度上,网页版网易POPO是最快的,基本上瞬间就可登录到使用界面。同样地,它也不能实现多媒体聊天功能;而且也不能保存聊天记录;未提供好友删除及好友分组的管理功能;适用于各版本浏览器。

  Web QQ Web MSN Gtalk Web POPO
登录速率 理想 一般 一般 理想
头像更改 否 否 否 否
呢称更改 否 否 否 是
好友添加 是 是 是 是
好友管理 否 否 是 否
聊天记录管理 否 否 是 否
界面美观度 理想 理想 一般 理想
总体印象 较好 一般 一般 较好

Sohu小纸条
vqq
vqq.com-vqq围围圈是一款国内在线聊天室,分免费和VIP付费两种方式。提供自定义图标的功能非常新颖。
功能特点:提供widget,把"VQQ围圈圈"聊天窗口嵌入在你自己的网页,自定义风格丰富,widget演示-点击rorol网页右下角的绿色图标展开聊天室。
radiusim
radiusim.com–基于ajax技术构建的在线web聊天服务,试用感觉不是很稳定,速度也比较慢.
支持的IM: Yahoo, AIM, MSN or GTalk
功能特点:会自动检测你的地址位置,并在radiusim中置入GOOGLE MAP标识这个位置.通过拖拉GOOGLE MAP,你能发现在地图中标注出来的各地会员.如果你想去某个城市旅游,通过radiusim的地图找位会员先了解情况倒是不错的方法.
Meebo
Meebo.com–称得上网页聊天的元老级别了,不论功能还是界面设计来说都很讨人喜欢,有几十种国家语言版本,能很好的支持中文.现在所看到的是新版的设计(原老版的报道).
支持的IM: AIM, Yahoo!, MSN, Google Talk, ICQ and Jabber
功能特点:meebome提供widget,用户可自定义widget的标题和联系人名称,大小尺寸,和聊天模板的颜色。
Skype
1. 网络结构:
1台服务器控制登陆,其余都是节点Node,拥有独立公网IP的节点为Super Node,其余为一般Node。普通Node必须通过Super Node才能加入Skype的世界。
2. 采用STUN(Simple Traversal of UDP through NAT)和TURN(Traversal Using Relay NAT) 协议来检测所处的NAT及防火墙环境
参见《私网穿越技术在软交换体系中的应用(详解)》
3. 编解码器
Global IP Sound在他的网站上专用明它为Skype提供点对点语音通讯软件:Global IP Sound provides voice processing software to Skype's peer-to-peer voice-communications。
最大特点是音频动态范围超过普通电话,能够处理50~8000hz的声音,出来的效果自然要比PSTN300~3400Hz好得多,声音更加圆润饱满
4.加密技术: AES
控制信号采用TCP传输,数据传输采用UDP传输;

6相关技术简介
6.1 Pushlets
6.2 Openfire
Openfire(原来的Wildfire)是一个跨平台,采用Java开发,开源的实时协作(RTC)服务器基于XMPP(Jabber)协议。Openfire安装和使用都非常简单,并利用Web进行管理。单台服务器可支持上万并发用户。
6.7 Json
JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScript Programming Language, Standard ECMA-262 3rd Edition – December 1999的一个子集。 JSON采用完全独立于语言的文本格式,但是也使用了类似于C语言家族的习惯(包括C, C++, C#, Java, JavaScript, Perl, Python等)。这些特性使JSON成为理想的数据交换语言。
JSON建构于两种结构:
· "名 称/值"对的集合(A collection of name/value pairs)。不同的语言中,它被理解为对象(object),纪录(record),结构(struct),字典(dictionary),哈希表 (hash table),有键列表(keyed list),或者关联数组 (associative array)。
· 值的有序列表(An ordered list of values)。在大部分语言中,它被理解为数组(array)。
这些都是常见的数据结构。事实上大部分现代计算机语言都以某种形式支持它们。这使得一种数据格式在同样基于这些结构的编程语言之间交换成为可能。
JSON具有以下这些形式:
对象是一个无序的"'名称/值'对"集合。一个对象以"{"(左括号)开始,"}"(右括号)结束。每个"名称"后跟一个":"(冒号);"'名称/值' 对"之间使用","(逗号)分隔。
 
数组是值(value)的有序集合。一个数组以"["(左中括号)开始,"]"(右中括号)结束。值之间使用","(逗号)分隔。
 
值(value)可以是双引号括起来的字符串(string)、数值(number)、true、false、 null、对象(object)或者数组(array)。这些结构可以嵌套。
 
字符串(string)是由双引号包围的任意数量Unicode字符的集合,使用反斜线转义。一个字符(character)即一个单独的字符串(character string)。
字符串(string)与C或者Java的字符串非常相似。
 
数值(number)也与C或者Java的数值非常相似。除去未曾使用的八进制与十六进制格式。除去一些编码细节。
 
空白可以加入到任何符号之间。 以下描述了完整的语言。
参考文献:http://www.json.org/json-zh.html
6.8 Server Push技术
推 送技术(Server Push)的基础思想是将浏览器主动查询信息改为服务器主动发送信息。服务器发送一批数据,浏览器显示这些数据,同时保证与服务器的连接。当服务器需要再 次发送一批数据时,浏览器显示数据并保持连接。以后,服务器仍然可以发送批量数据,浏览器继续显示数据,依次类推。
客户端拉曳(Client Pull)  :在客户端拖曳技术中,服务器发送一批数据,在HTTP响应或文档头标记中插入指令 ,让浏览器"在5秒内再次装入这些数据"或"10秒内前往某URL装入数据"。当指定的时间达到时,客户端就按照服务器的指示去做,或者刷新当前数据,或 者调入新的数据。
其实push 和 pull 这两种技术手段非常不同,但目的几乎一致,都是为了给最终用户方便的提供最新信息。
在服务器推送技术中,HTTP 连接一直保持着,直到服务器知道自己已结束发送数据并发送一个结束信号,或者客户端中断连接。而在客户端拖曳技术中,并不保持HTTP连接,相反,客户端被告知何时建立新连接,以及建立连接是获取什么数据。
在服务器推送中,奇妙之处在于"multipart/mixed"格式的MIME,它能够使一个报文(或HTTP响应)包含许多数据项、在客户端拖曳中,奇妙之处在于HTTP响应头标(或等效的HTML元素),它能告知客户端在指定的延时时间后执行何种动作。
服务器推送通常效率要比客户端拖曳效率高,因为它不必为后续数据建立新的连接。由于始终保持连接,即使没有数据传输时也是这样,因此服务器必须愿意分配这些TCP/IP端口,对于TCP/IP端口数有限的服务器这将是一个严重的问题。
客户端拖曳效率低,因为这必须每次为传送数据建立新的连接。但是它不必始终保持连接。
在实际情况中,建立HTTP连接通常需要花费相当多的时间,多达一秒甚至更多。因此从性能上考虑,服务器推送对于最终用户更有吸引力,特别是对于需要经常更新信息的情况下。
服 务器推送相对客户端拖曳的另一点优势是,服务器推送相对比较容易控制。例如,服务器每一次推送时都保持一个连接,但它又随时可以关闭其中的任何连接,而不 需要在服务器上设置特殊的算法。而客户端拖曳在同样的情况下要麻烦许多,它每次要与服务器建立连接,服务器为了处理将客户端拖曳请求与特定的最终用户匹配 等情况,需要使用相当麻烦的算法。
如果实现服务器推送的CGI程序是使用Shell脚本语言编写的,有时会存在一些问题。例如,客户端最终用 户中断连接,Shell程序通常不能注意到,这将使资源毫无用处的浪费掉,解决这一问题的办法是用Perl或者C来编写这类CGI程序,以使用户中断连接 时能够结束运行。
如上所述,在服务器推送中,多个响应中连接始终保持,使服务器可在任何时间发送更多的数据。一个明显的好处是服务器完全能够控制更新数据的时间和频率。另外,这种方法效率高,因为始终保持连接。缺点是保持连接状态会浪费服务器端的资源。服务器推送还比较容易中断。

接下来就大概说说服务器推送技术。
服 务器在响应请求时,HTTP使用MIME报文格式来封装数据。通常一个HTTP响应只能包含一个数据块。但MIME有一种机制可用一个报文(或HTTP响 应)表示将多个数据块,这种机制就是成为"multipart/mixed"的标准MIME类型。multipart/mixed报文大体格式如下:
Content-type:multipart/mixed;boundary=ThisRandomString
–ThisRandomString
Content-type:text/plain
第一个对象的数据。
–ThisRandomString
Content-type:text/plain
第二个对象的数据。
–ThisRandomString–

上述报文包括两上数据块,二者的类型都是"text/plain"。最后一个"ThisRandomString"后的两条短线(–)表示报文结束,后面没有数据。

对于服务器推送,使用一个"multipart/mixed"类型的变种
–multipart/x-mixed-replace。这里,"x-"表示属于实验类型。"replace"表示每一个新数据块都会代替前一个数据块。也就是说,新数据不是附加到旧数据之后,而是替代它。
 
下面是实际使用的"multipart/x-mixed-replace"类型:
Content-type:multipart/x-mixed-replace;boundary=ThisRandomString
–ThisRandomString
Content-type:text/plain
第一个对象的数据
–ThisRandomString
Content-type:text/plain
第二个(最后一个)对象的数据。
–ThisRandomString–
使 用这一技术的关键是,服务器并不是推送整个"multipart/x-mixed-replace" 报文,而是每次发送后数据块。HTTP连接始终保持,因而服务器可以按自己需要的速度和频率推送新数据,两个数据块之间浏览器仅需在当前窗口等候,用户甚 至可以到其他窗口做别的事情,当服务器需要发送新数据时,它只是源(ABC输入法没那个字*&^$#)传输管道发送数据块,客户端相应的窗口进行 自我更新。
在服务器推送技术中,"multipart/x-mixed-replace"类型的报文由唯一的边界线组成,这些边界线分割每个数据 块。每个数据块都有自己的头标,因而能够指定对象相关的内容类型和其他信息。由于"multipart/x-mixed-replace"的特性是每一新 数据块取代前一数据对象,因而浏览器中总是显示最新的数据对象。
"multipart/x-mixed-replace"报文没有结尾。也就是 说,服务器可以永远保持连接,并发送所需的数据。如果用户不再在浏览器窗口中显示数据流,或者浏览器到服务器间的连接中间(例如用户按"STOP"按 钮),服务器的推送才会中断。这是人们使用服务器推送的典型方式。
当浏览器发现"Content-type"头标或到达头标结束处时,浏览器窗口中的前一个文档被清除,并开始显示下一个文档。发现下一个报文边界时,就认为当前数据块(文档)已经结束。
总之,服务器推送的数据由一组头标(通常包括"Content-type")、数据本身和分割符(报文边界)三部分组成。浏览器看到分割符时,它保持状态不变,直到下一个数据块到达。
将以上概念进行用编程方法实现,就可以得到实际的服务器推送程序。例如,下面的Unix shell程序将使浏览器每5秒显示一次服务器上的进程列表:

#!/bin/sh
echo "HTTP/1.1 200"
echo "Content-type: multipart/x-mixed-replace; boundary=–ThisRandomString–"
echo ""
echo "–ThisRandomString–"
while true
do
echo "Content-type: text/html"
echo ""
echo "h2Processes on this machine updated every 5 seconds/h2"
echo "time:"
date
echo "p"
echo "plaintext"
ps -el
echo "–ThisRandomString–"
sleep 5
done

注意到,边界设置在sleep语句之前发送,这能够确保浏览器清除其缓冲区,并显示所接收到的最新数据。
NCSA HTTPD用户在内容类型中不能使用空格,包括边界参数。NCSA HTTPD只能将不带空格字符的字符串作为内容类型。如果在内容类型行中存在空格(冒号后面的空格除外),空格后的任何文本都会被删除。
 
下面的示例是正确的:
Content-type: multipart/x-mixed-replace;boundary=ThisRandomString
而下例则不能正常工作,因为它在中间有空格:
Content-type: multipart/x-mixed-replace; boundary=ThisRandomString
服务器推送的另一个优点是它可以针对单个内联图象进行。包括图象的文档可以由服务器定时或定周期进行更新。而实现这一点非常简单:只需使IMG元素的SRC属性指向推送一系列图象的URL即可。
如果服务器推送用于单个内联图象,文档中的图象就会一次次被新推送来的图象所代替,而文档本身不需变化(假设文档没有进行服务器推送)。这样,WEB页面中有限的动画就可以为静态画面所代替。

客户端拖曳
客户端拖曳的一个简单用法是使文档按固定周期自动重载。例如,考虑下面的HTML文档:
<META HTTP-EQUIV="Refresh" CONTENT=1>
<TITLE>Document ONE</TITLE>
<H1>This is Document ONE!</H1>
Here's some text.<P>
如果将它载入支持动态文档的浏览器(Netscape 1.1以上,Internet Explorer和Mosaic也支持客户端拖曳),它将每隔一秒将自己重载一次。
由于META元素实际是在HTML文档中模拟HTTP响应头标,所以它能够告知浏览器将自身信息当作HTTP响应使用。上例中的META标记相当于:
Refresh:1
这样,实际上就是HTTP头标告知浏览器每一秒更新一次文档。如果需要延时是12秒,那么就是这样的指令:
<META HTTP-RQUIV="Refresh" CONTENT=12>
那么它等效于:
Refresh:12

关于客户端的拖曳我也懒的继续写下去,关于怎么使客户端自动申请其他URL的数据话,请使用如下:
<META HTTP-EQUIV="Refresh" CONTENT="12;URL=">http://icools.yeah.net/">
注意的是,此处的URL不能使用相对路径,必须全部指定。

其中时间间隔可以设置为0,这样浏览器在当前文档显示完毕后,以最快的速度载入新的数据!

实 现Server   push技术非常简单。Server   push在服务器的CGI脚本声明HTML文档类型时,把传统的content-type:text/html改为content- type:multipart/x-mixed-replace;boundary=BOUNDARY这样的文档类型,就会反馈给用户一个 Server   push类型的连接。这是Server   push和Client   pull的根本区别。如果CGI脚本中提供了这样的HTML文档头,服务器在处理客户机请求调用CGI脚本程序时,就会把CGI脚本中指定的数据强行推给 客户机。  
        Server   push在生成页面时会采用很多的技巧来处理用户端浏览器页面的生成。主程序和传统方式没有本质的区别,但记得在脚本中加入print"Content- Type:multipart/x-mixed-replace;boundary=BOUNDARY
6.9 Comet
目前最强大的开源Comet解决方案是:
Dojo+Jetty Cometd+Jetty Continuation+Bayeux协议
一些相关的文档先放在这里,我就不多介绍了,大家都完全有能力读懂。
Jetty的作者,Servlet规范专家组成员Greg Wilkins写的两篇文章:
Ajax, Comet and Jetty:
http://www.webtide.com/downloads/whitePaperAjaxJetty.html
Cometd with Jetty:
http://blogs.webtide.com:8080/gregw/2006/08/03/1154583360000.html
Bayeux协议:
http://svn.xantus.org/shortbus/trunk/bayeux/protocol.txt
一种基于JSON的、平台中立的分路复用协议,可以由任何Comet客户端和服务器端实现。目前客户端的Dojo、服务器端的Jetty Cometd已经实现了对这个协议的支持。
一个使用这个解决方案的实例:
Active AJAX based live dashboards:
http://www.qenet.co.uk/warwick/whitepaper-pushTech.pdf
根据Greg Wilkins的测试,最后Jetty Cometd服务10000个用户875个线程,只用了57M内存。
http://groups.google.com/group/cometd-dev/browse_thread/thread/09d80fb4abdc4f5c
Pushlets作者Just van den Broecke也承认,Pushlets存在着可伸缩性的问题:
"Yes, I am aware of the scalability limitations of the Pushlets framework. A dedicated server-side technique based on NIO (such as Greg, hi there, is working on ?) could help."

并且申请加入Cometd的开发工作:
"With great interest I have been following recent COMET developments and would like to join cometd developments in whatever way."
6.9.1 Dojo
Dojo是一个非常强大面向对象,开源的JavaScript工具箱。它为开发Web胖客户端程序提供了一套完整的Widget和一些特效操作。
官方网站:http://www.dojochina.com

6.10 Ruby、Rails
ruby是一种面向对象的动态脚本语言。它的语法很灵活,而且提供了丰富的类库。因此,用ruby编写程序的效率是非常高的。
Rails 是使用纯ruby编写的框架(framework)。它对web开发提供了强有力的支持,如支持数据映射、MVC模式、Web Services、安全等。而且这些功能操作起来要比同类的产品容易的多,如MVC模式就比struts更容易使用。除了这些,rails还可以根据模板 自动生成web程序。这样可以省去我们很多的时间。
6.11 Erlang

附录1.常用开源Jabber服务器介绍
1. Openfire (Wildfire) 3.x
授权:GPL or 商用
操作系统平台:所有(使用Java开发)
XMPP Jabber 协议实现情况:98%
Tim 评价:
安 装和使用非常简单,安装后进入Web界面进行2~3分钟的配置所有的东西都设好了。使用Java语言开发,在目前Java开发人员到处普及的情况下进行维 护和扩展的成本非常低。在我的测试中加上 Connection Manager 的情况下单台服务器可支持 30 万并发用户。缺点是目前还不支持cluster。如果企业内部部署IM使用 Wildfire + Spark 是最佳的组合。
见:http://hi.baidu.com/jabber/blog/category/Wildfire
2. ejabberd
授权:GPL
操作系统平台:Linux,Windows,MacOS X等
XMPP Jabber 协议实现情况:91%
Tim 评价:
Ejabberd目前是可扩展性最好的一种Jabber/XMPP服务器,支持分布多个服务器,并且具有容错处理,单台服务器失效不影响整个cluster运作。
顾虑就是它采用一种大家都没听过的语言Erlang开发,所以很多人可能会象我一样因为这个原因放弃了它。
3. Jabberd 2.x
授权:GPL
操作系统平台:主要是 Linux,(Windows 也支持,但不知道性能怎样)
XMPP Jabber 协议实现情况:76%
Tim 评价:
自从jabber.org改用ejabberd之后,Jabberd一直都在走下坡路。扩展性比不上ejabberd, 易用性比不上 Wildfire,唯一的优势是使用C开发,如果你坚持要用C开发,那么还是选择jabberd吧。
4. Jabberd 1.x
授权:GPL
操作系统平台:主要是 Linux, (Windows 也支持,但不知道性能怎样)
XMPP Jabber 协议实现情况:45%
Tim 评价:
在几年前 jabberd 就是 Jabber 的代名词,至今很多 Jabber 文档仍然介绍的是 Jabber 1.4,在我以前写的《Jabber 服务器占有率比较》中仍然排名第一。但是它很多新的规范都不支持,相信大部分用户都将转向新的服务器。
见:http://hi.baidu.com/jabber/blog/item/7d25bb199f31a44542a9ad02.html
5. 后起之秀 DJabberd
授权:open source
操作系统平台:主要是 Linux,(Perl写的,其他平台应该也支持)
XMPP Jabber 协议实现情况:N/A
Tim 评价:
djabberd 使用 epoll 技术,理论上单台服务器可以支持更多用户。Djabberd目前主要应用在LiveJournal上,大部分XMPP协议都支持,稳定性也不用置疑。但是因为推出时间尚短,很多细节功能可能需要时间慢慢完善。
djabberd介绍见:http://hi.baidu.com/jabber/blog/category/Djabberd
附录2.使用comet技术的实例
GMail's GTalk integration
Jot Live
Renkoo
cgi:irc
Meebo

信息来源:http://alex.dojotoolkit.org/?p=545
附录3.基于SIP协议的即时通讯产品
这 类产品,目前也有一些。但大部分属于个人或小团体开发,因此功能上还很不完善。一般只支持文本信息的实时传递,也有一些产品可以利用SIP协议进行语音的 实时通讯,主要应用在SIP电话上,而且成熟的产品也并不多见。可以实现文本,语音,视频的真正意义上的利用SIP协议开发的多媒体即时通讯产品还并不多 见。所以,开发基于SIP协议的即时通讯系统,是具有一定价值和意义的。下面列举一些利用SIP协议的开发的即时通讯产品。
(1) X- Pro/X-Lite
Xten Network公司的这一产品,是一个比较成熟的商业化产品,属于基于SIP协议的软电话工具。支持最多六条线路并发的语音通讯,支持最多十方参加的语音电话会议,自动应答,自动选择编解码方式等。
(2 ) KPhone
KPhone是一个运行在Linux平台的SIP客户端程序。利用KPhone,可以和远端用户建立VOIP连接,实现双方的即时通讯。
(3 ) SIPPS
Ahead Software AG公司推出的SIPPS也是一个基于SIP协议的软电话工具。SIPPS具有很多特点,包括良好的语音效果,易用性,加解密机制,电话录音,以及兼容MSN Messenger等。
(4)SIP Multimedia PC Client
Nortel Networks公司推出的SIP Multimedia PC Client是一个功能全面的真正意义的多媒体即时通讯工具。利用SIP Multimedia PC Client,可以不仅仅实现传递文本信息,也可以建立语音和视频连接,从而真正实现多媒体信息的实时通讯。
附录4.为comet技术改进的Server
(1)Jetty http://www.mortbay.org/ 100% Java
(2)Tomcat6
(3)下面的Server都是专门为Comet设计的,也提供了客户端的JS库:
Lightstreamer http://www.lightstreamer.com Demo with Dojo
Orbited http://www.orbited.org/
Meteor http://meteorserver.org/
(4)iPush Server[台湾] www.icetechnology.com
附录4. Comet实现
一些其他Comet Ajax服务器推送模型的实现:
· Orbited :一种开源的分布式Comet服务器
· AjaxMessaging :Ruby on Rails的Comet插件
· Pushlets :一个开源框架,可以让服务器端java对象推送事件到浏览器端javascript,java applet,或者flash应用程序
· Lightstreamer :提供基于AJAX-COMET模式的HTTP流的商业实现
· Pjax :Ajax的推送技术
· Virgil's One?
· SmartClient
· Fjax
· Server-Sent Events
· COMETd
· Ajax for IBM WebSphere Platform
信息来源:http://wiki.matrix.org.cn/Wiki.jsp?page=Comet
附录5. 前台技术
AJAX框架
Ext
是一组扩展自Yahoo!UI,具有CS风格的Web用户界面组件。主要UI包括:dialog,grid,layout,tabs等。
http://www.ajaxjs.com
基于ruby的rails框架
Flash
附录6. 后台技术
1. 开发语言
(1) Erlang
(2) C
(3) Perl
(4) C#
(5) JAVA
2. 操作系统
UNIX/LINUX/Windows都有,一般都采用多台服务器集群实现负载均衡。
3. 服务器结构
(1) 循环服务器
(2) 并发服务器:多进程/多线程
4. 数据交换格式
(1) JSon
(2) XML